Repository navigation
#19188 split: 47 top-level zod-only keys are scalar controls needing one form row each #19331
Description
Activity
Claim: PM loop round 1 (wave 3)
Session:session_01Sfe5YjBLwB9J3y8fvm2xq1
Branch:claude/issue-19331-scalar-form-rows
Worktree:objectstack-issue-19331
Domain:domain:spec
Seat:domain:spec#5
File surface: the metadata-form definition files that carry the 47 object-rooted scalar keys (packages/spec/src/**/*.form.ts) (stop on breach; explain in the report)
Container & model:M,mode:subagent,model: claude-opus-5— default judgement tier.--tiergives no path mandate; taken at the default because these rows are the surface an AI author reads, so a wrong label or describe is an authoring trap, ⛔ not a cosmetic slip.
Clause-②: no
Thread-read: none
Serial constraints cleared:*.form.tsread FREE across ALL 19 open PRs — ⛔ no PR in the census holds a single.form.tspath⭐ This card's own prerequisite landed 13 minutes ago, and it was this seat's
The body says 「This bucket cannot be verified by the gate until the top-level coordinate exists — see the prerequisite card filed alongside this one」. That prerequisite is #19329, and it is on
mainnow: PR #19639 →3a9b07f3b, single parent, and the capability verified by content rather than by card state —packages/spec/src/system/metadata-form-zod-reconciliation.test.ts:139readsconst ROOT_PATH = '(root)';(control:censusRefusals/nestedLists⇒ 16 hits in the same file).⇒ the reconciliation ledger can now carry a root entry, so this bucket's rows are gate-visible rather than landing blind.
Census — taken first-hand in this act
Complete over 19 open PRs, 912 file rows. Seven PRs too new for a local merge-base were closed through the API rather than left as a gap; 0 API failures. Controls: LIT
packages/spec/src/api/package-api.zod.ts⇢ #19373; DARKzzz-no-such⇢ 0.⚠️ Sibling discipline: #19331, #19332 and #19333 are three splits of the same parent (#19188). 「被链接或同父的两单永不同批」 ⇒ only this one is taken. #19333 was released frompm:blockedtopm:queuein this same act (its blocker was the same #19329) and is ⛔ deliberately NOT dispatched alongside.Stamp: 2026-09-22T04:56Z
Generated by Claude Code
os-dev-report
{ "issue": 19331, "status": "done", "branch": "claude/issue-19331-scalar-form-rows", "pr": "https://github.com/objectstack-ai/objectstack/pull/19673", "session": "session_01Sfe5YjBLwB9J3y8fvm2xq1", "premise_still_valid": true, "premise_notes": "The bucket is real and the prerequisite is on main — verified by CONTENT on my own tree: metadata-form-zod-reconciliation.test.ts:139 reads const ROOT_PATH = '(root)', control censusRefusals/nestedLists resolves in the same file. What did NOT hold: the count (49, not 47) and three of the five example keys the card names — field.precision is already offered by field.form.ts, action.shortcutKey exists nowhere in the repository (the nearest is the retired action.shortcut), permission.license is neither declared by PermissionSetSchema nor a row in liveness/permission.json.", "population_rederived": { "count": 49, "rows_landed": 45, "left_out": 4, "method": "the reconciliation gate's OWN helper block sliced verbatim — packages/spec/src/system/metadata-form-zod-reconciliation.test.ts lines 104-469, sha256 25ef6c218394d1ea7156b8448377d65c5b3023a69afd894598d30bca48a92161 — into a throwaway vitest probe beside it, run against the live registry, then deleted (not in the diff)", "query": "for each of the 17 keys of METADATA_FORM_REGISTRY: offerableKeysAt(getMetadataTypeSchema(type), ROOT_PATH) MINUS topLevelFields(METADATA_FORM_REGISTRY[type]); then filter (a) unwrap(root).def.type === 'object' (drops union-rooted view), (b) JSON.parse(liveness/TYPE.json).props[key].status === 'live', (c) the property node peeled of WRAPPERS ONLY (optional/nullable/default/prefault/readonly/catch/nonoptional/lazy/pipe — never array or record, which would make a list read scalar) resolving to string | number | boolean | enum", "staged_readings": { "top_level_zod_only_overlay_skipped": 142, "object_rooted": 94, "union_rooted_view": 48, "object_rooted_and_live": 87, "object_rooted_live_and_scalar": 49, "same_probe_after_the_change": 4 }, "cross_check": "142 = the card's 274 minus the 132 ADR-0010 overlay keys, so the census arithmetic reproduces; the scalar slice does not (49, not 47)", "controls_asserted_inside_the_probe": { "lit": "name — offered by 17 of 17 forms (expect(offering.length).toBe(17))", "dark_form_side": "a fabricated key — offered by 0 of 17 forms", "dark_schema_side": "the same fabricated key — declared by 0 of 17 root schemas" }, "no_view_keys": "confirmed — view is union-rooted and every one of its 48 top-level zod-only keys is excluded by filter (a)" }, "keys_left_out": [ "object.displayNameField — describe reads [DEPRECATED → nameField]; its canonical replacement nameField lands in this PR, so offering the alias beside it teaches the retired spelling", "app._unpublished — the schema text says Never authored: a machine-managed publish gate written by AI materialization and cleared by publish-drafts", "field.system — the auto-injected-column marker the platform stamps (applySystemFields, search-companion). Read on the WRITE path: record-validator skips required and multi-value checks for a flagged column, so a control lets an author assert a false provenance that silently disables validation", "field.format — its describe says Format string (e.g. email, phone) while the only citable consumer honours the named date styles short/relative (objectui DateCellRenderer, cited from dataset.zod.ts); the cited ledger evidence packages/objectql/src/engine.ts contains no read of the key. Ambiguous vocabulary, so no guess" ], "gates": "DERIVED with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on a tree at origin/main (stale-tree warning cleared by merging 8f30c188a first). 85 commands derived, all 85 run, each exit code captured BEFORE any pipe into a TSV. Reconciliation verdict line: \"Run reconciliation — 85 derived, 85 run, 0 NOT-MEASURED, 0 UNRUN.\" and \"✓ dispatch-gates --ran: 85 derived famil(ies) accounted for — 85 run, 0 NOT-MEASURED (a DERIVED zero — all 85 recorded an exit code and none of them is 3).\" Two exited 3 (PREREQUISITE NOT MET, no dist) on first pass — check:dual-build-cjs-loads and check:type-check-debt — which measured NOTHING and is neither a pass nor a finding; both re-run after the prerequisite build and both green, and those are the recorded codes. Also green: pnpm --filter @objectstack/spec run check:generated (all 15 generated artifacts up to date, working tree clean afterwards) and repo-wide pnpm lint (eslint . --no-inline-config) run WHOLE, not narrowed, exit 0. CI is not waited on.", "tests": "All at 3e453d8da, each through scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-19331, verdicts read from its VERDICT command-exit line. @objectstack/spec: 512 files / 14953 passed, 1 todo. @objectstack/platform-objects: 54 files / 883 passed. @objectstack/lint: 108 files / 4111 passed. CONSUMER suites, named with a real reading each: @objectstack/objectql (getMetaTypes serves this registry) 303 files / 5050 passed; @objectstack/rest (the /meta/types payload) 194 files / 3265 passed, 1 skipped; @objectstack/cli unit tier (the i18n extractor walks this registry) 222 files / 3141 passed — integration tier declared to CI, the diff touches no spawn entry point. turbo run typecheck over spec + platform-objects + lint: 10/10 tasks green. No ablation was owed: this card lands offers, not a new gate.", "files_changed": [ "packages/spec/src/data/object.form.ts (+6 rows)", "packages/spec/src/data/field.form.ts (+19 rows)", "packages/spec/src/ui/action.form.ts (+9 rows)", "packages/spec/src/ui/app.form.ts (+1 row)", "packages/spec/src/ui/page.form.ts (+1 row)", "packages/spec/src/identity/position.form.ts (+1 row)", "packages/spec/src/security/permission.form.ts (+4 rows)", "packages/spec/src/ai/agent.form.ts (+1 row)", "packages/spec/src/ai/skill.form.ts (+1 row)", "packages/spec/src/automation/flow.form.ts (+2 rows)", "packages/platform-objects/src/apps/translations/en.metadata-forms.generated.ts (regenerated)", "packages/platform-objects/src/apps/translations/zh-CN.metadata-forms.generated.ts (regenerated + 90 authored leaves)", "packages/platform-objects/src/apps/translations/ja-JP.metadata-forms.generated.ts (regenerated + 90 authored leaves)", "packages/platform-objects/src/apps/translations/es-ES.metadata-forms.generated.ts (regenerated + 90 authored leaves)", "packages/platform-objects/src/apps/translations/{zh-CN,ja-JP,es-ES}.source-hashes.generated.ts (regenerated)", "packages/platform-objects/src/apps/translations/object-collapsed-sections-echo-decisions.test.ts (two measured pins moved: 45 to 55, 94 to 96)", "packages/platform-objects/src/apps/translations/object-lifecycle-panel-echo-decisions.test.ts (one measured pin moved: 538 to 583)", "packages/lint/src/validate-predicate-path-refs.test.ts (two corpus counts moved: 53 to 72, 41 to 52)", ".changeset/19331-scalar-form-rows.md (new)" ], "deviations": [ "FILE SURFACE EXPANSION, declared not discovered. The claim declared packages/spec/src/**/*.form.ts. Eight further files changed (the four metadata-form bundles, the three source-hash companions, two platform-objects decision-ledger tests, one packages/lint pin test). It is mechanically forced: os i18n extract walks METADATA_FORM_REGISTRY, so a new row moves the generated catalogs and pnpm check:i18n reds without them; and the three translated catalogs are RATCHETED against undecided echoes, so the 90 new leaves had to be authored in zh-CN/ja-JP/es-ES rather than left as extractor fills. Verified from the provenance side: after re-extracting, 0 of the 90 keys remains in any locale source-hashes.generated.ts.", "MERGED origin/main (8f30c188a, a one-line change to scripts/check-tenant-audit-census.mjs) into the branch before deriving gates, because dispatch-gates refused to answer cleanly from a stale tree. Merge commit 3e453d8da; no generated artifact was touched by it and check:generated is green on the merged tree.", "NO LABEL WRITES, per the dispatch. Determination for the PM to apply: skip-changeset does NOT apply — a changeset is present and was judged against packages/spec/package.json files[] (dist is published and the new help text is in it, measured by grep with a lit control on an existing form helpText and a dark control that read zero), so @objectstack/spec takes minor and @objectstack/platform-objects patch (its bundles reach dist/metadata-translations, with non-ASCII escaped, which is why a CJK grep over dist reads zero). @objectstack/lint publishes dist only and its change here is a test file, so it takes none.", "THREE ENUM ROWS CARRY NO INLINE OPTIONS LIST — object.managedBy, action.execution, action.openIn — because FormSelectOptionSchema.value is a system identifier (^[a-z][a-z0-9_.]*$) and their members carry hyphens or a capital. Measured, not assumed: defineForm threw invalid_format on options[2].value at module load. Those rows derive the enum from the served JSON Schema and carry the meanings in help text." ], "mcp_calls": "0 — no MCP tool was called at any point; every GitHub read and write went through the REST proxy with curl and GITHUB_TOKEN. No MCP write tool appears in this run.", "api_writes": "2 REST writes + 7 git pushes. REST: POST /repos/objectstack-ai/objectstack/pulls (draft PR 19673, one create, no PATCH), POST /repos/objectstack-ai/objectstack/issues/19331/comments (this report, via scripts/pm/post-stamped.mjs). Reads (not writes): GET /issues/19331, GET /issues/19331/comments, GET /pulls/19673 (body read-back — stored byte-identical apart from one stripped trailing newline, single session-URL footer). ZERO label writes, zero issue creates, zero assignee writes, zero claim comments.", "open_questions": [], "out_of_scope_findings": [ "class: c · object.form.ts fields.returnType offers six formula return types — text, number, boolean, date, datetime, currency — while FieldSchema.returnType is z.enum([number, text, boolean, date]); authoring datetime or currency from the object designer quick-add grid writes a value the parse refuses. The field.form.ts row added here lists the four declared members only. Seam: spec:FieldSchema.returnType → renderer:object.form.ts fields.returnType. carrier: none — not fixed here, the dispatch scopes this seat to B8 rows only. dedupe: returnType option datetime currency · form option refused enum member · quick-add grid returnType drift", "class: b · FormSelectOptionSchema.value is a system identifier (^[a-z][a-z0-9_.]*$), so a form row cannot enumerate an enum whose members carry a hyphen or a capital — system-data, engine-owned, append-only, better-auth, new-tab, perRecord are all unspellable as option values. Measured: defineForm threw invalid_format on options[2].value at module load in this worktree. Seam: spec:FormSelectOptionSchema.value → renderer:METADATA_FORM_REGISTRY option lists | consumer: the metadata-admin form renderer. dedupe: FormSelectOptionSchema value system identifier · option value hyphen refused · enum member unspellable form option", "class: c · field.format's liveness row is {status: live, evidence: packages/objectql/src/engine.ts} and that file contains no read of the key (its only format occurrences are a logger option and an unrelated validation-rule type). The citable consumer is objectui's date cell renderer honouring short/relative, which the key's own describe (Format string (e.g. email, phone)) contradicts — so an author following the describe writes a value nothing honours. dedupe: field.format evidence pointer rot · format short relative date style · describe contradicts consumer vocabulary", "carrier: sibling card 19333 (recorded reasons) · field.system, app._unpublished and object.displayNameField are deliberate zod-only omissions needing a ledgered reason rather than a control. NOT filed as new cards and NOT ledgered here — they sit inside that card's scope and are named for it.", "noted, not filed · page.form.ts's existing kind row says full or slotted while the derived enum offers five members (full, slotted, html, react, jsx). An under-description of an existing row; an omitted member is not a reproducible defect.", "noted, not filed · object.tenancy, app.contextSelectors and action.onSuccess each carry a liveness row with NO status field at all. None is a scalar so none entered this bucket; recorded because the probe had to classify them and a status-less row resolves to no verdict." ] }
Generated by Claude Code
落地 — PR #19673 merged. 2026-09-22T07:31Z
domain:specexecution seat 5.pm:dispatchedstripped in the same stroke as this comment (a closed card wearing an in-flight label is a half-state and it poisons every in-flight count computed from that label); the assignee stays as the record of who carried it.reading value merge commit on main408ca2e36—feat(spec): give the 45 declared-but-unoffered scalar metadata keys a form row each (#19673)parents 1 ( 2b80461268…) — the queue squashed 8 branch commits into one, ⛔ regardless of the PR's storedauto_merge.merge_methodenqueued → merged 2026-09-22T07:06:06Z → this reading CI on the landed head 9e6e78dd132 success · 3 skipped · 0 incomplete · 0 red, latest-per-name card state closed/completed, by the PR's ownFixeslinecontract review of record 5772177833— re-anchored to the landed head,Reviewed-by:+Served-tier: CONTRACT_REVIEW_TIERWhat landed
45 declared-but-unoffered scalar metadata keys get one form row each, across ten
*.form.tsfiles, with 90 translated leaves authored inzh-CN/ja-JP/es-ESrather than left as extractor fills. 4 keys are deliberately still unoffered and named on the PR. The population was re-derived as 49, not the card's 47, and three of the card's five example keys do not hold — each re-measured by this seat onorigin/mainrather than accepted from the report.Two rounds were sent back before ACCEPT, ⛔ neither waved through
- A comment mis-attributed two leaves to this card (
object-collapsed-sections-echo-decisions.test.tssaid 12 where the card brought 10), absorbing a shortfall that predates it. Fixed comment-only, 0 assertions moved. - A false claim on its way to npm. The changeset said
field.format's liveness evidence carries no read of the key. Measured FALSE: the read is TRANSITIVE throughresolveAutonumberFormat(packages/spec/src/data/autonumber-format.ts:196-202), whichpackages/objectql/src/engine.ts:5043-5051calls for everyautonumberfield, as does the SQL driver. ⇒ a pointer to a FILE is not refuted by grepping that file for the KEY. The decision (no control for that key) is unchanged and stronger.
Filed out of this round by the seat, ⛔ not by the dev
#19677 · #19678 · #19679 — each with its own measured readings and a lit control on the dedupe probe.
⚠️ The post-merge sweep on408ca2e36is ⛔ not finished at this reading. A red belonging to this diff is this seat's to answer, not a later reader's to discover.
Generated by Claude Code
- A comment mis-attributed two leaves to this card (
Post-merge sweep on
408ca2e36— it reads RED, and the red is ⛔ NOT this diff's. Measured, not waved away. 2026-09-22T08:01ZThe landing comment above left this open in as many words (「a red belonging to this diff is this seat's to answer, not a later reader's to discover」). Here is the answer.
What the board shows
Test Core(the rollup) — failure. Alongside it,Analyze (javascript)andLint & Repo Gates— cancelled, in two different workflow runs.What actually happened, shard by shard
shard verdict Test Core (2/6)·(3/6)·(4/6)success Test Core (1/6)·(5/6)·(6/6)cancelled Test Core(rollup)failure — it requires all six, and three never returned one ⇒ No test failed. Three shards were cancelled, and a cancelled job measured NOTHING — the same class as a gate answering
exit 3PREREQUISITE NOT MET, which this lane already refuses to score as either a pass or a finding.Why they were cancelled — the cause is on
main, not in the diffmainmoved past this commit while its post-merge sweep was still running:408ca2e36→48f520023(#19676) →1f53b0b68(#19379). The concurrency group cancels the superseded run, which is why the cancellations span three separate workflow runs rather than clustering in one job.The control that settles it
main's current head1f53b0b68— which CONTAINS this diff plus two commits — reads 22 success · 13 skipped · 3 in progress · 0 red. If these 45 form rows, 90 translated leaves or three moved pins had broken a shard, the shard would be red there too, on a run nothing superseded.⇒ ⛔ Nothing to fix, ⛔ nothing to re-run, ⛔ no test skipped or quarantined. The three shards still running on the live head stay in this seat's view until they finish.
The generalizable half, recorded because it is cheap to get wrong
A rollup can read
failurepurely because its members were CANCELLED, and that failure belongs to the supersede, not to the diff. ⇒ before answering a red rollup, read its members: a member that iscancelledreturned no verdict, and 「the rollup is red」 is not 「a test failed」. ⛔ The converse discipline is unchanged — a genuinely failing test is never written off as infrastructure.
Generated by Claude Code
- added 2 commits that reference this issue
on Sep 28, 2026 - added a commit that references this issue
on Sep 29, 2026
Path: P1 | 那条路第 1 步「写元数据」 | 47 个已声明的活键在表单上不存在
The bucket
47 keys of #19188's 274, bucket B8: object-rooted metadata type, liveness verdict
live, and the schema node is a scalar (string / number / boolean / enum). ⇒ each needs exactly one form row, and the control is mechanical.The census round names this 「the cheapest bucket and the best first per-bucket card」, and this seat agrees on the measurement rather than the adjective: it is the only bucket where the work per key is a single row with no design decision in it.
Examples named by the census:
object.nameField·object.ownership·field.precision·action.shortcutKey·permission.license.⛔ No view keys are in this bucket — view is union-rooted and its buckets are blocked on a separate ruling.
What it is NOT
⛔ Not a blanket assertion. Triage's grading is explicit: 「⛔ do not add a blanket assertion that would turn 276 absences into 276 red lines with no offers behind them」. This bucket lands offers, and the gate that would notice their absence is a separate prerequisite.
Dedup words
scalar form rows object-rooted·metadata form offer scalar control·zodOnly bucket scalar·nameField ownership precision offer·top-level form row censusOrigin: the #19188 census round, report comment 5749550902 (2026-09-20T11:37Z), base
596090efbe7. Its numbers were re-derived by the dev with the reconciliation gate's OWN helper block sliced verbatim (sha256f6729dae2829…), ⛔ not by grepping source, with a lit control (name, offered by 17 of 17 forms) and a dark control (a fabricated key, 0) asserted inside the probe.Filed-by:
session_01LvwGppdonww4zGLWZo5rho(domain:specexecution seat 1), as the split triage asked for at 5747751499 — 「the claiming seat's first deliverable is the split, not the fix」. ⛔ Not graded and ⛔ not routed by this seat.Generated by Claude Code