Validate configs
Use when you touch Tale file-based org config or its guards — builtin-configs/, e2e fixture config trees, CONFIG_DOMAINS, Zod schemas under lib/shared/schemas/, app bundles under builtin-configs/apps/, or a red builtin-configs, builtin-apps, email-parity, fixture-drift, or migrations:check gate. Owns which gate proves what, validator registration, enforcement postures, and the snapshot-vs-migration ritual. Run the matching gate before shipping; never duplicate a shape a shared schema already owns. For tests use test-code; for failing behaviour use fix-bug.From its SKILL.md
npx -y skills add tale-project/tale --skill validate-configsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 20 stars20 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
8.6 KB, ~1.8k tokens by cl100k_base, as published. Nobody here has run it
validate-configs
Per-org configuration is JSON files under $TALE_CONFIG_DIR/<org>/<domain>/, seeded from the
builtin-configs/ catalog and validated by one shared Zod schema per
domain in lib/shared/schemas/. This skill owns
running and extending those guards. Editing a skill bundle's content is
author-skill; writing tests generically is
test-code.
When this applies
- Editing or adding any file under
builtin-configs/or the fixture org treesservices/platform/tests/e2e/fixtures/config/{default,qa-guides-org}/. - Adding a domain to
CONFIG_DOMAINS(lib/shared/config/registry.ts) or editing any schema underlib/shared/schemas/. - Authoring or refreshing an app bundle under
builtin-configs/apps/<slug>/. - A red gate — the universal suite,
builtin_apps,email_bundle_parity,fixture_bundle_drift, ormigrations:check.
The gates — run what you changed
All vitest gates run from services/platform/; migrations:check from the repo root.
| You changed… | Run |
|---|---|
catalog / fixture JSON in any non-apps domain | bunx vitest --run --project server lib/shared/config/builtin_configs_validation.test.ts |
| an app bundle (manifest, views, messages, wf, agents) | bunx vitest --run --project server convex/workflow_engine/helpers/validation/builtin_apps.test.ts convex/apps/email_bundle_parity.test.ts convex/apps/fixture_bundle_drift.test.ts |
any schema under lib/shared/schemas/ | bun run --filter @tale/platform migrations:check |
The universal suite is registry-driven — it walks every CONFIG_DOMAINS domain over
builtin-configs/ plus both fixture org trees with the same schemas the platform load path
uses. Its header doc carries the walker details; read it before touching it.
The same walk also runs as bun run --filter @tale/platform configs:validate (plain Bun, no
vitest) — wired into the platform build script and a CI step in build.yml so a broken builtin
catalog fails the image build, not just the test job — and, non-fatally, post-deploy via the
validateBuiltinCatalog internal action (convex/lib/config_store/validate_builtin_catalog.ts),
which walks the deployed $TALE_CONFIG_BUILTIN_DIR and logs any issue loudly without blocking boot.
The rules
- One schema, every consumer. The loader (
convex/<domain>/file_utils.ts), the write/publish path, and the CI gate all import the domain's schema fromlib/shared/schemas/— a hand-rolled twin drifts silently and validates nothing (reviewer-caught; inside the gates it's structural — they import the shared schemas). - Every domain declares a validator. A
CONFIG_DOMAINSentry with noDOMAIN_VALIDATORSentry fails the suite with "add a validator" — new domains cannot ship an unvalidated catalog. The walkers +DOMAIN_VALIDATORSregistry live incatalog_validator.ts(also driving the build-timeconfigs:validategate and the post-deploy runtime check); the vitest suite (builtin_configs_validation.test.ts) is a thin wrapper over it. - Enforcement posture follows the path. Publish/upload throws
ConvexErrorcodes —INVALID_VIEW,VIEW_BINDING_NOT_ALLOWED,VIEW_ROLE_UNKNOWN,INVALID_MANIFESTinconvex/apps/bundle_parse.ts— so a broken file never reaches an org's disk. Discovery degrades visibly — warn+skip a bad manifest, surface a bad view as an{ id, error }repair stub, never a silent drop and never a failed list (convex/apps/file_actions.ts). Match the posture of the path you're on. - Schema change ⇒ snapshot ritual.
migrations:checkfingerprints every schema againstconvex/migrations/config.snapshot.json. Data-safe growth (new optional field, widened enum, removed field) → refresh withbun run --filter @tale/platform migrations:snapshot. Data-incompatible (new required field, retype, narrowed enum/literal, optional→required, tightened constraint) → ship the versioned migration FIRST — scaffold withbun run gen:migrationand followconvex-migrations(enforced bymigrations:check). - Fixtures track builtins byte-for-byte. A builtin app-bundle refresh re-syncs its pinned
copies in both fixture trees (enforced by
convex/apps/fixture_bundle_drift.test.ts— its header documents the one allowedissue-desk-qarename transform). The three email inbox bundles (outlook/sync-emails,gmail/sync-emails,imap-smtp/sync-emails) are one product per provider — an edit to one must hit all three (enforced byconvex/apps/email_bundle_parity.test.ts). - Unknown-file posture matches the loaders. Flat/tree domains are STRICT — only
*.json(+*.secrets.jsonsidecars); any other file would be invisible at runtime, so the gate rejects it. Bundle items (skills/,integrations/<slug>/,apps/<slug>/) carry arbitrary assets; bundle roots hold only item directories. - Landmine — shipped workflows validate schema-only on the file path. About a dozen builtin
workflows deliberately fail
validateWorkflowDefinition's stricter reference/port lints while parsing fine withworkflowJsonSchema; the lint runs only on the agent-tool create/save (publish) path. Don't "fix" the builtins to pass it and don't add the lint to the file gate. App-bundled workflows DO pass full definition validation in the builtin-apps gate.
Adding a config domain (the drift guard forces all of it)
- Schema in
lib/shared/schemas/<domain>.ts— the loader and the gate must share it. - Registry entry in
CONFIG_DOMAINS— layout, readContext, dataModel, scaffoldKind; the list order is the org-scaffold seed order, keep it stable. The registry is pure (V8-importable); filesystem resolvers live inconvex/lib/config_store/resolvers.ts— the registry header explains the layering. - Validator entry in
DOMAIN_VALIDATORS— reuse the walker matching the layout (walkFlat/walkJsonTree/walkBundles), orexternal-gatewithcoveredBypointing at a dedicated suite (theappspattern; the suite then asserts your gate files keep existing). - Catalog-scaffolded (
scaffoldKindset)? Shipbuiltin-configs/<domain>/with at least one valid file — the catalog root asserts nonzero validated files. - Run
migrations:check— a new schema module changes the fingerprint (see the snapshot ritual).
Before you call it done
Tick every box, or N/A with a reason; an unticked box means not done.
- The gate matching each change ran green (per the table above) — output observed, not assumed.
- No consumer re-declares a shape — every new parse imports from
lib/shared/schemas/. - App-bundle edits — both fixture-tree copies re-synced and all three email siblings updated (drift + parity gates green), or N/A.
- Schema edits — snapshot refreshed (data-safe) or a versioned migration shipped (data-incompatible), or N/A.
- New domain — schema + registry entry + validator + builtin catalog dir all present, or N/A.