Skip to content

fix(spec): reject unknown top-level keys on ObjectSchema.create() (#1535)#1540

Merged
os-zhuang merged 2 commits into
mainfrom
claude/magical-johnson-Zr1ah
Jun 2, 2026
Merged

fix(spec): reject unknown top-level keys on ObjectSchema.create() (#1535)#1540
os-zhuang merged 2 commits into
mainfrom
claude/magical-johnson-Zr1ah

Conversation

@os-zhuang
Copy link
Copy Markdown
Contributor

Fixes #1535.

Problem

ObjectSchemaBase is a plain z.object({...}) (Zod default .strip()), and ObjectSchema.create() parses through it. So any unknown top-level key — object-level workflows: [...], or a typo'd validation/indexs — was silently discarded: no error, no warning, and a green tsc. Declarative on-update automation authors believed they shipped vanished from every built artifact (15 object-level workflows across objectstack-ai/templates, dead from day one). This is the metadata-shape analogue of ADR-0032's "no silent failure".

Fix

1. Reject unknown keys, loudly and fixably (ask #1)
ObjectSchema.create() now detects unknown top-level keys before parsing and throws a located, fixable error that:

  • names the offending key(s),
  • suggests the intended key on a likely typo (validationvalidations, via edit distance),
  • for known-confusable keys (workflows, hooks, triggers) points at the supported mechanism.

The factory signature also constrains excess keys to never (NoExcessObjectKeys<T>), so the mistake surfaces at tsc time as well as at build. The non-strict ObjectSchema.parse() load path (registry/artifact validation) is unchanged.

ObjectSchema.create('demo'): unknown key(s) — workflows.
These keys would previously have been stripped silently at build, shipping
dead metadata with no diagnostic (ADR-0032 "no silent failure", issue #1535).

  • `workflows` is not an ObjectSchema field. Object-level, record-triggered
    automation is authored as a lifecycle hook (`src/objects/<name>.hook.ts`,
    registered via `defineHook()`) or as a top-level `record_change` flow —
    not as `workflows[]` on the object schema.

Remove the unknown key(s), fix the typo, or move the logic to a supported mechanism.

2. Object-workflow story (ask #2)
Decided (b): lifecycle hooks and top-level record_change flows are the supported paths; there is no object-level workflows[] field. The build now flags workflows explicitly and routes authors there.

3. Docs/skill (ask #3)
The objectstack-data CRM-blueprint row that taught the non-existent workflows[] shape is repaired to point at hooks / record_change.

Bonus: two latent #1535 instances in the framework's own objects

Enabling the strict check surfaced real dead config in sys_secret and sys_setting_audit — both carried silently-stripped views/scope/defaultViewName keys (views should be listViews, and type: 'list' isn't a valid ListViewSchema type — it's 'grid'). Migrated to the supported listViews field so the intended list views now actually render. A repo-wide scan confirmed no other ObjectSchema.create() call carries stray top-level keys.

Verification

  • packages/spec test suite: 6619 passed (incl. 4 new unknown-key tests).
  • packages/platform-objects: 52 passed (migrated objects load + validate).
  • Standalone tsc confirms the excess-key guard errors on workflows while valid usage compiles clean.
  • Migrated listViews shapes validated against the built ObjectSchema.

https://claude.ai/code/session_01KHJTd48x76KVcAK1e494wr


Generated by Claude Code

)

Object-level `workflows: [...]` (and any unknown ObjectSchema key) was
silently stripped at build by Zod's default `.strip()`, with no error, no
warning, and a green `tsc` — the metadata-shape analogue of ADR-0032's "no
silent failure". Declarative on-update automation authors believed they
shipped vanished from every built artifact.

`ObjectSchema.create()` now rejects unknown top-level keys with a precise,
fixable error: it names the offending key(s), suggests the intended key on a
likely typo (`validation` -> `validations`), and for known-confusable keys
like `workflows` points authors at the supported mechanism (a lifecycle hook
or a top-level `record_change` flow). The factory signature also constrains
excess keys to `never`, catching the mistake at `tsc` time too. The non-strict
`ObjectSchema.parse()` load path is unchanged.

Also fixes two platform objects (sys_secret, sys_setting_audit) that carried
silently-stripped `views`/`scope`/`defaultViewName` keys — their intended list
views are migrated to the supported `listViews` field (`type:'list'` ->
`'grid'`) so they now render. Repairs the objectstack-data skill's CRM
blueprint row that taught the non-existent `workflows[]` shape.

https://claude.ai/code/session_01KHJTd48x76KVcAK1e494wr
@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 2, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jun 2, 2026 10:39am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:data tests tooling size/m labels Jun 2, 2026
@os-zhuang os-zhuang marked this pull request as ready for review June 2, 2026 10:31
…1535)

The new strict ObjectSchema.create() correctly surfaced a latent #1535
instance: todo_task carried an object-level `workflows: [...]` block that Zod
stripped at build, so it never ran. Its intent is already realized through the
supported mechanisms — `task.hook.ts` (lifecycle hook), `task.handlers.ts`
(completion stamping), and `flows/task.flow.ts` (record_change + schedule
flows). Removed the dead block (and the now-unused `cel` import) and documented
where the automation actually lives.

https://claude.ai/code/session_01KHJTd48x76KVcAK1e494wr
@os-zhuang os-zhuang merged commit 023bf93 into main Jun 2, 2026
10 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:data size/m tests tooling

Projects

None yet

2 participants