You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
metadata forms: one row each for object.access, object.highlightFields, object.requiredPermissions, object.searchableFields and permission.adminScope (#19332 flight G1a) #20349
Part of #19332 — flight G1a of ruling 5861442317 (batch #229 item 3, letter A, maintainer 「同意」).
Filed by the domain:spec seat 4 (session_01CiCTczDo7tGhafXjf61dUJ, seat post #18917). It is an in-scope sub-issue of an in-flight card, so it inherits the parent's domain:spec and priority:p2. The parent carries the ledger flight (claim 5861529325). This card exists so that flight G1a can run beside the ledger flight under its own claim.
What
An author who opens the Studio form for an object or a permission set cannot see or set five live settings that the platform honours once they are written in code. Each gets one form row, mirroring a row a registered form already has, with the four-locale catalogue rows in the same PR.
key
form
object.access
packages/spec/src/data/object.form.ts
object.highlightFields
object.form.ts
object.requiredPermissions
object.form.ts
object.searchableFields
object.form.ts
permission.adminScope
packages/spec/src/security/permission.form.ts
The ruling, quoted (G1)
G1 (21 keys …) — one form row each, mirroring a row a registered form already has (code/expression, json, string-tags, composite, textarea, plain), with the four-locale catalogue rows in the same PR. Union-typed values take json, never string-tags (which reads a stored map as an empty array and overwrites it); field-name lists are free text and a misspelling is refused loudly at parse.
Execution parameters: G1 and G2 land as form-row flights (G1: object + permission, then field + action; …) … Every flight is Clause-②: no (form face and ledger text). Confidence gaps carried: action.description's authors NOT MEASURED; no browser run of the objectui repeater face …
The objectui widget bounds (string-tags reads a non-array as []; field-multi binds nothing on an object draft) are the dev's reading at the .objectui-sha pin and are carried as the premise the first flight re-checks.
This is the first form flight, so it re-checks that premise.
The platform-objects echo-decision tests only if the new rows move them, and the reconciliation test's expectations only if they pin the residue count.
.changeset/.
⛔ Not field.* / action.* (flight G1b). ⛔ Not fieldGroups / indexes or the other G2 keys. ⛔ Not the ledger rows (#19188 split: 39 top-level zod-only keys are structured controls needing a designed widget, not a row #19332's ledger flight).
(stop on breach; explain in the report)
Container & model: M, mode:subagent, model: default judgment tier (dispatch-gates --tier: no path-derived mandate). The *.form.ts files are non-test packages/spec/src/**, so the at-tier review is owed before enqueue.
Clause-②: no
Thread-read: none
Serial constraints cleared: read at 2026-09-28T01:07Z.
The parent's ledger flight (5861529325) writes only the reconciliation test file, so the two are disjoint unless this flight's residue pin moves; the later lander merges main.
No open PR touches object.form.ts or permission.form.ts.
Ruling carried: 5861442317 · batch #229 item 3 · letter A · maintainer 「同意」 (G1, quoted in this card's body). This is the first form flight, so it re-checks the ruling's carried premise on objectui's widget bounds at the .objectui-sha pin.
domain:spec seat 4 (session_01CiCTczDo7tGhafXjf61dUJ), 2026-09-28T03:31Z. Clock-out on the maintainer's word in the seat session, verbatim: 「当前任务处理完,合并后就下班」, then 「快没token了」. The dev run was stopped mid-round. Only the pushed branch head below counts; nothing local survives. pm:dispatched → pm:queue in this act, with os-sales unassigned. The next claimant re-claims under the full protocol and re-measures on the day's main.
State. The branch is claude/issue-20349-g1a-object-permission-rows, remote head bc1d395d3. There is no PR yet. When stopped, the dev was updating the platform-objects echo-decision pins, after the five rows and the catalogues. Its completeness and its re-check of the ruling's objectui widget-bound premise are NOT MEASURED; read the branch against claim 5861551344.
Next. Finish the flight:
run spec test + test:repo and the platform-objects suite;
Claim: PM loop round 1
Session: session_01ARcDurZ5j34RdqsGgc4jgH
Account: os-warren (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-20349-g1a-object-permission-rows
Worktree: objectstack-issue-20349
Domain: domain:spec
Seat: domain:spec#4 (seat post #18917)
Ruling-ref: 5861442317
File surface: the surface of the released claim 5861551344, continued from the branch's pushed head bc1d395d3 (release 5862816545). The branch is re-used; its commits are UNVERIFIED hand-over, re-read hunk by hunk.
packages/spec/src/data/object.form.ts: one row each for object.access, highlightFields, requiredPermissions, searchableFields. packages/spec/src/security/permission.form.ts: one row for permission.adminScope.
The four-locale *.metadata-forms.generated.ts catalogue rows and the platform-objects re-extract and echo-decision pins that follow (regenerated, never hand-edited), plus .changeset/.
⛔ Not G1b (field.*, action.*), not G2, not the reconciliation ledger, not the zodOnly wiring (#19188 split: 145 top-level zod-only keys need a RECORDED REASON, never a form row — and none can be recorded until the ledger learns a root path #19333).
(stop on breach; explain in the report)
Container & model: S/M, mode:subagent, model: default judgment tier (no path-derived mandate). The diff's forms and catalogues are not among the review surfaces in contract-review.md unless it reaches packages/spec/src/** schemas; the seat re-checks the final file list before landing.
Clause-②: no
Thread-read: 5862816545
Serial constraints cleared: read at 2026-09-28T06:15Z on origin/main15bf186f5.
Ruling carried: 5861442317 · batch #229 item 3 · letter A · maintainer 「同意」: G1 rows, the objectui widget-bound premise re-checked in this first form flight, Clause-②: no.
{
"issue": 20349,
"status": "done",
"branch": "claude/issue-20349-g1a-object-permission-rows",
"pr": "https://github.com/objectstack-ai/objectstack/pull/20405",
"session": "session_01ARcDurZ5j34RdqsGgc4jgH (subagent run; the parent PM session's id, as CLAUDE_CODE_REMOTE_SESSION_ID resolves it)",
"premise_still_valid": true,
"summary": "Resumed branch head bc1d395d3 into worktree objectstack-issue-20349, reread every hunk and reverified it all. object.form.ts now has one row each for highlightFields and searchableFields (string-tags, in Basics), access (a composite over a declared default select) and requiredPermissions (json, the union), both in Advanced. permission.form.ts has one json row for adminScope. The four-locale catalogue rows are in, and the platform-objects echo pins are updated. Hand-over, per acceptance item. The five rows (7ce01726): complete, kept; every help-text claim was checked against its runtime reader. Catalogue rows and re-extract (3820f4a7, 3ae44a78): complete, kept; after the merge, check:i18n reports all 9 packages in sync, so the catalogues regenerate byte-for-byte. Echo pins (abbf9c7f): already complete; the platform-objects suite is green with no further pin edit. Changeset: half-done; it lacked the Clause-② line, and this run added it (353e708a). Merge: origin/main 15bf186f5 merged with os-regen-merge.sh as e505f724; no conflict, and check:generated is green with no regeneration. Ruling premise, re-checked by reading objectui f8a9d0fb0 source (no browser): both halves HOLD. widgets.tsx:1290 reads a non-array as an empty array, and add/remove write it back. ResourceEditPage.tsx:889-893 takes the field catalogue from interfaceConfig.source, data.object, object or objectName, and the object type declares none of the four. Residue, measured with the reconciliation test's own helper block: 28 at 15bf186f5, 23 on the head src; exactly the five keys removed and none added. Assumption 3 confirmed. Draft PR #20405 is open. CI was in progress when this report was written.",
"tests": "All through scripts/pm/os-verify-lock.sh (slot issue-20349), each VERDICT command-exit 0. For the two wrapper scripts, each inner exit was recorded separately, and all were 0. Commands and their real lines: pnpm --filter @objectstack/spec test = 'Test Files 557 passed (557) / Tests 16497 passed | 1 todo (16498)'. pnpm --filter @objectstack/spec test:repo = 'Test Files 35 passed (35) / Tests 634 passed (634)'. pnpm --filter @objectstack/platform-objects test = 'Test Files 55 passed (55) / Tests 911 passed (911)'. pnpm --filter @objectstack/spec typecheck: exit 0 (check:test-typecheck OK: 53 files, 255 errors, 142 signatures held by the ledger). pnpm --filter @objectstack/platform-objects typecheck: exit 0 (1 file, 3 errors, 2 signatures held). Downstream readers of the form registry: lint src/validate-predicate-path-refs.test.ts = 1 file, 54 tests passed; cli unit test/i18n-coverage.test.ts = 1 file, 20 tests passed. Builds: spec build exit 0; the i18n closure build (turbo, cli + 9 packages) = 'Tasks: 59 successful, 59 total'. pnpm check:i18n: 'check-i18n-bundles: OK (9 package(s) — all bundles in sync, no undeclared authoring keys)'. spec check:generated: 'All 15 generated artifacts are up to date'. Residue probe: the reconciliation test file copied whole, plus one appended probe block; scratch only, never committed. Before tree = 15bf186f5; after tree = the same tree with object.form.ts and permission.form.ts restored from 353e708a (git diff vs 353e708a over packages/spec/src = 0 lines). Each probe run: 1 file, 58 tests passed. Lit control field.accept was present in both; dark control object.zzFabricated20349 was absent in both. No ablation: no guard or gate was added.",
"mcp_calls": "0 — no MCP GitHub tool was called",
"api_writes": "3 — all through the fleet-write relay (repository_dispatch, executed as objectstack-fleet[bot]): (1) pr_create = POST /repos/objectstack-ai/objectstack/pulls (draft; relay run 36393424556, success); (2) label-write assign = POST /repos/objectstack-ai/objectstack/issues/20405/assignees [os-warren] (relay run 36393508455, success; read-back matches); (3) this os-dev-report comment = POST /repos/objectstack-ai/objectstack/issues/20349/comments. Plus 2 git pushes (e505f724, 353e708a), which are not REST. Reads were direct REST GETs only.",
"gates": {
"derived_by": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 353e708a (identical to the list derived at e505f724)",
"derived": 85,
"ran": 85,
"exit_nonzero_final": 0,
"not_measured": 0,
"unrun": 0,
"ran_verdict": "✓ 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).",
"first_run_nonzero": [
"pnpm check:dual-build-cjs-loads :: exit 3 (PREREQUISITE NOT MET: nine packages had no dist/). Rerun on the same tree once the dist/ directories existed :: exit 0, '104 published require entry point(s) across 66 package(s) load'. The ran.list records the rerun's code."
],
"partial_notes": [
"pnpm --filter @objectstack/spec run check:entry-nameability exit 0, printing its standing 'NOT MEASURED: no callable export on @objectstack/spec/api-assembled, @objectstack/spec/qa'. That line is independent of this diff."
],
"ci_at_report": "head 353e708a: 32 check runs — 12 success, 3 skipped, 17 in_progress, 0 failure"
},
"line_budget": {
"changed_lines": 188,
"additions": 182,
"deletions": 6,
"files": 9,
"human_merge_threshold": 5000,
"verdict": "under",
"skills_budget": "n/a — no skills/** path"
},
"files_changed": [
".changeset/20349-object-permission-form-rows.md",
"packages/platform-objects/src/apps/translations/en.metadata-forms.generated.ts",
"packages/platform-objects/src/apps/translations/es-ES.metadata-forms.generated.ts",
"packages/platform-objects/src/apps/translations/ja-JP.metadata-forms.generated.ts",
"packages/platform-objects/src/apps/translations/zh-CN.metadata-forms.generated.ts",
"packages/platform-objects/src/apps/translations/object-collapsed-sections-echo-decisions.test.ts",
"packages/platform-objects/src/apps/translations/object-lifecycle-panel-echo-decisions.test.ts",
"packages/spec/src/data/object.form.ts",
"packages/spec/src/security/permission.form.ts"
],
"deviations": [
"Worktree created from the remote branch, with no empty-branch push, as the dispatch ordered (rule-1 deviation by order).",
"The residue probe ran in a second scratch worktree at 15bf186f5, not in the issue worktree. The after-tree was that worktree with the two form files restored from 353e708a. This kept the gate run's change set free of untracked files. The worktree was removed cleanly afterwards.",
"check:dual-build-cjs-loads is recorded in ran.list with its rerun exit 0; its first run exited 3 (prerequisite)."
],
"open_questions": [],
"out_of_scope_findings": [
"carrier: #19333 item 2 (the zodOnly wiring; edits metadata-form-zod-reconciliation.test.ts) · noted, not filed — a dark-control comment there reads 'object.access is authorable and no form offers it', and that is stale after this PR. Its assertions (ruled-class admission) still hold. The file is the ledger, which is outside this flight's surface.",
"carrier: #19333 item 2 · noted, not filed — nothing pins the ruling's 'union values never take string-tags' across the registry. A future edit could flip requiredPermissions back to string-tags with no test turning red. Not a defect today.",
"carrier: 承接者:无 · noted, not filed — objectui's nested-form face cannot unset an object key. Once adminScope is touched, clearing every sub-field leaves an empty object, and the parse refuses it loudly (businessUnit is required). Loud, generic to every nested-form row.",
"carrier: PM (ruling text) · noted, not filed — the ruling says a misspelt field-name entry 'is refused loudly at parse'. It is refused at the object publish door and by os validate: validateObjectFieldRefs and validateSearchableFields at error, runtimeTypes including object. The Zod parse does not judge field names. The changeset and the PR body say so."
]
}
Review: ACCEPT · PR #20405 at head 353e708aa7808c39a649d8254cf2895a59f9f648 · 2026-09-28T08:21Z
domain:spec seat 4 (session_01ARcDurZ5j34RdqsGgc4jgH), reviewer of record under claim 5864511529. The readings below were taken on GitHub or on the tree by this seat.
check
reading
Shape
Draft, base main. Body line 1 Fixes #20349; line 2 names the parent without a closing verb (Flight G1a of #19332); Clause-②: no stands alone.
Scope
9 files, +182/−6: two form files, four generated catalogues, two echo-decision pins, the changeset. It matches the claim.
Changeset
@objectstack/specminor and @objectstack/platform-objectspatch, Clause-②: no, on the #19673 (408ca2e36) precedent. Its prose correctly places the misspelt-field-name refusal at the publish door and os validate, not at the Zod parse.
Contract review
At-tier record PASS5866196177, same head. It checks every row's widget against its schema shape and the ruling (requiredPermissions, the one union, takes json), and every help-text claim against its runtime reader. It re-read the objectui widget premise at the pin (both halves hold), and derives the reconciliation residue 28 → 23 exactly.
CI at this head
32 success, 3 skipped, all on the roster (check-expected-skips.mjs --pr 20405, exit 0). mergeable_state: clean. origin/main is 6 commits past the merge base and touches none of this PR's files, so the generated catalogues need no re-sync.
The objectui nested form cannot unset an object key. A cleared adminScope is refused loudly by the schema. This is a generic renderer bound → Acceptance notes, no carrier.
The ruling's 「refused loudly at parse」 is in fact the publish door and os validate. The changeset says so accurately, and this is noted here for the ruling's readers.
Landing next: ready + auto-merge through the relay. At MERGED, this card closes by Fixes, and #19332's G1b becomes the next flight.
domain:spec seat 4 (session_01ARcDurZ5j34RdqsGgc4jgH), landing record for claim 5864511529. ACCEPT 5866208907 on the at-tier PASS 5866196177. Queue path: ready at 08:21Z, added_to_merge_queue at 08:23Z, merged at 08:46Z.
Activity
objectstack-fleet commentedon Sep 28, 2026
Claim: PM loop round 1
Session:
session_01CiCTczDo7tGhafXjf61dUJAccount:
os-sales(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-20349-g1a-object-permission-rowsWorktree:
objectstack-issue-20349Domain:
domain:specSeat:
domain:spec#4(seat post #18917)File surface:
packages/spec/src/data/object.form.ts: rows foraccess,highlightFields,requiredPermissionsandsearchableFieldsonly.packages/spec/src/security/permission.form.ts: a row foradminScopeonly.packages/platform-objects/src/apps/translations/*.metadata-forms.generated.tscatalogues, produced by the repo's i18n pipeline as feat(spec): give the 45 declared-but-unoffered scalar metadata keys a form row each #19673 (408ca2e36) produced them..changeset/.field.*/action.*(flight G1b). ⛔ NotfieldGroups/indexesor the other G2 keys. ⛔ Not the ledger rows (#19188 split: 39 top-level zod-only keys are structured controls needing a designed widget, not a row #19332's ledger flight).(stop on breach; explain in the report)
Container & model:
M,mode:subagent,model: default judgment tier(dispatch-gates --tier: no path-derived mandate). The*.form.tsfiles are non-testpackages/spec/src/**, so the at-tier review is owed before enqueue.Clause-②: no
Thread-read: none
Serial constraints cleared: read at 2026-09-28T01:07Z.
5861529325) writes only the reconciliation test file, so the two are disjoint unless this flight's residue pin moves; the later lander mergesmain.object.form.tsorpermission.form.ts.scripts/pm/os-regen-merge.sh.Ruling carried:
5861442317· batch #229 item 3 · letter A · maintainer 「同意」 (G1, quoted in this card's body). This is the first form flight, so it re-checks the ruling's carried premise on objectui's widget bounds at the.objectui-shapin.objectstack-fleet commentedon Sep 28, 2026
Release: clock-out, round stopped before its PR
domain:specseat 4 (session_01CiCTczDo7tGhafXjf61dUJ), 2026-09-28T03:31Z. Clock-out on the maintainer's word in the seat session, verbatim: 「当前任务处理完,合并后就下班」, then 「快没token了」. The dev run was stopped mid-round. Only the pushed branch head below counts; nothing local survives.pm:dispatched→pm:queuein this act, withos-salesunassigned. The next claimant re-claims under the full protocol and re-measures on the day'smain.State. The branch is
claude/issue-20349-g1a-object-permission-rows, remote headbc1d395d3. There is no PR yet. When stopped, the dev was updating the platform-objects echo-decision pins, after the five rows and the catalogues. Its completeness and its re-check of the ruling's objectui widget-bound premise are NOT MEASURED; read the branch against claim5861551344.Next. Finish the flight:
test+test:repoand the platform-objects suite;mainwithos-regen-merge.sh(seat 1's PR feat(spec)!: retire currencyConfig.precision — a currency's decimal places are its currency's (ADR-0049) #20251 regenerates the same catalogues);Fixes #20349andClause-②: no;The later flights stay as the parent's ruling
5861442317orders them: G1b, then G2a and G2b.domain:specseat 4 · #18917 ·session_01CiCTczDo7tGhafXjf61dUJGenerated by Claude Code
objectstack-fleet commentedon Sep 28, 2026
Claim: PM loop round 1
Session:
session_01ARcDurZ5j34RdqsGgc4jgHAccount:
os-warren(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-20349-g1a-object-permission-rowsWorktree:
objectstack-issue-20349Domain:
domain:specSeat:
domain:spec#4(seat post #18917)Ruling-ref: 5861442317
File surface: the surface of the released claim
5861551344, continued from the branch's pushed headbc1d395d3(release5862816545). The branch is re-used; its commits are UNVERIFIED hand-over, re-read hunk by hunk.packages/spec/src/data/object.form.ts: one row each forobject.access,highlightFields,requiredPermissions,searchableFields.packages/spec/src/security/permission.form.ts: one row forpermission.adminScope.*.metadata-forms.generated.tscatalogue rows and the platform-objects re-extract and echo-decision pins that follow (regenerated, never hand-edited), plus.changeset/.field.*,action.*), not G2, not the reconciliation ledger, not thezodOnlywiring (#19188 split: 145 top-level zod-only keys need a RECORDED REASON, never a form row — and none can be recorded until the ledger learns a root path #19333).(stop on breach; explain in the report)
Container & model:
S/M,mode:subagent,model: default judgment tier(no path-derived mandate). The diff's forms and catalogues are not among the review surfaces incontract-review.mdunless it reachespackages/spec/src/**schemas; the seat re-checks the final file list before landing.Clause-②: no
Thread-read: 5862816545
Serial constraints cleared: read at 2026-09-28T06:15Z on
origin/main15bf186f5.origin/mainis 16 commits past the branch's merge base and moved all four catalogues (seat 1's PR feat(spec)!: retire currencyConfig.precision — a currency's decimal places are its currency's (ADR-0049) #20251 is no longer open). Open PR feat(spec)!: retire the list view's owntabskey; named presets arelistViewsentries #20357 (spec(ui): retirelist.tabsand the view container's bodyname(2 keys);listViews+ ViewTabBar and the row name already deliver both #20301,domain:specseat 2) also regenerates the catalogues andobject-lifecycle-panel-echo-decisions.test.ts. That is ordinary concurrency, merged withscripts/pm/os-regen-merge.sh.object.form.tsorpermission.form.ts, and no live claim names them.b1cbd9277, so the reconciliation residue base is 28.Ruling carried:
5861442317· batch #229 item 3 · letter A · maintainer 「同意」: G1 rows, the objectui widget-bound premise re-checked in this first form flight,Clause-②: no.domain:specseat 4 · #18917 ·session_01ARcDurZ5j34RdqsGgc4jgHGenerated by Claude Code
objectstack-fleet commentedon Sep 28, 2026
os-dev-report
{ "issue": 20349, "status": "done", "branch": "claude/issue-20349-g1a-object-permission-rows", "pr": "https://github.com/objectstack-ai/objectstack/pull/20405", "session": "session_01ARcDurZ5j34RdqsGgc4jgH (subagent run; the parent PM session's id, as CLAUDE_CODE_REMOTE_SESSION_ID resolves it)", "premise_still_valid": true, "summary": "Resumed branch head bc1d395d3 into worktree objectstack-issue-20349, reread every hunk and reverified it all. object.form.ts now has one row each for highlightFields and searchableFields (string-tags, in Basics), access (a composite over a declared default select) and requiredPermissions (json, the union), both in Advanced. permission.form.ts has one json row for adminScope. The four-locale catalogue rows are in, and the platform-objects echo pins are updated. Hand-over, per acceptance item. The five rows (7ce01726): complete, kept; every help-text claim was checked against its runtime reader. Catalogue rows and re-extract (3820f4a7, 3ae44a78): complete, kept; after the merge, check:i18n reports all 9 packages in sync, so the catalogues regenerate byte-for-byte. Echo pins (abbf9c7f): already complete; the platform-objects suite is green with no further pin edit. Changeset: half-done; it lacked the Clause-② line, and this run added it (353e708a). Merge: origin/main 15bf186f5 merged with os-regen-merge.sh as e505f724; no conflict, and check:generated is green with no regeneration. Ruling premise, re-checked by reading objectui f8a9d0fb0 source (no browser): both halves HOLD. widgets.tsx:1290 reads a non-array as an empty array, and add/remove write it back. ResourceEditPage.tsx:889-893 takes the field catalogue from interfaceConfig.source, data.object, object or objectName, and the object type declares none of the four. Residue, measured with the reconciliation test's own helper block: 28 at 15bf186f5, 23 on the head src; exactly the five keys removed and none added. Assumption 3 confirmed. Draft PR #20405 is open. CI was in progress when this report was written.", "tests": "All through scripts/pm/os-verify-lock.sh (slot issue-20349), each VERDICT command-exit 0. For the two wrapper scripts, each inner exit was recorded separately, and all were 0. Commands and their real lines: pnpm --filter @objectstack/spec test = 'Test Files 557 passed (557) / Tests 16497 passed | 1 todo (16498)'. pnpm --filter @objectstack/spec test:repo = 'Test Files 35 passed (35) / Tests 634 passed (634)'. pnpm --filter @objectstack/platform-objects test = 'Test Files 55 passed (55) / Tests 911 passed (911)'. pnpm --filter @objectstack/spec typecheck: exit 0 (check:test-typecheck OK: 53 files, 255 errors, 142 signatures held by the ledger). pnpm --filter @objectstack/platform-objects typecheck: exit 0 (1 file, 3 errors, 2 signatures held). Downstream readers of the form registry: lint src/validate-predicate-path-refs.test.ts = 1 file, 54 tests passed; cli unit test/i18n-coverage.test.ts = 1 file, 20 tests passed. Builds: spec build exit 0; the i18n closure build (turbo, cli + 9 packages) = 'Tasks: 59 successful, 59 total'. pnpm check:i18n: 'check-i18n-bundles: OK (9 package(s) — all bundles in sync, no undeclared authoring keys)'. spec check:generated: 'All 15 generated artifacts are up to date'. Residue probe: the reconciliation test file copied whole, plus one appended probe block; scratch only, never committed. Before tree = 15bf186f5; after tree = the same tree with object.form.ts and permission.form.ts restored from 353e708a (git diff vs 353e708a over packages/spec/src = 0 lines). Each probe run: 1 file, 58 tests passed. Lit control field.accept was present in both; dark control object.zzFabricated20349 was absent in both. No ablation: no guard or gate was added.", "mcp_calls": "0 — no MCP GitHub tool was called", "api_writes": "3 — all through the fleet-write relay (repository_dispatch, executed as objectstack-fleet[bot]): (1) pr_create = POST /repos/objectstack-ai/objectstack/pulls (draft; relay run 36393424556, success); (2) label-write assign = POST /repos/objectstack-ai/objectstack/issues/20405/assignees [os-warren] (relay run 36393508455, success; read-back matches); (3) this os-dev-report comment = POST /repos/objectstack-ai/objectstack/issues/20349/comments. Plus 2 git pushes (e505f724, 353e708a), which are not REST. Reads were direct REST GETs only.", "gates": { "derived_by": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 353e708a (identical to the list derived at e505f724)", "derived": 85, "ran": 85, "exit_nonzero_final": 0, "not_measured": 0, "unrun": 0, "ran_verdict": "✓ 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).", "first_run_nonzero": [ "pnpm check:dual-build-cjs-loads :: exit 3 (PREREQUISITE NOT MET: nine packages had no dist/). Rerun on the same tree once the dist/ directories existed :: exit 0, '104 published require entry point(s) across 66 package(s) load'. The ran.list records the rerun's code." ], "partial_notes": [ "pnpm --filter @objectstack/spec run check:entry-nameability exit 0, printing its standing 'NOT MEASURED: no callable export on @objectstack/spec/api-assembled, @objectstack/spec/qa'. That line is independent of this diff." ], "ci_at_report": "head 353e708a: 32 check runs — 12 success, 3 skipped, 17 in_progress, 0 failure" }, "line_budget": { "changed_lines": 188, "additions": 182, "deletions": 6, "files": 9, "human_merge_threshold": 5000, "verdict": "under", "skills_budget": "n/a — no skills/** path" }, "files_changed": [ ".changeset/20349-object-permission-form-rows.md", "packages/platform-objects/src/apps/translations/en.metadata-forms.generated.ts", "packages/platform-objects/src/apps/translations/es-ES.metadata-forms.generated.ts", "packages/platform-objects/src/apps/translations/ja-JP.metadata-forms.generated.ts", "packages/platform-objects/src/apps/translations/zh-CN.metadata-forms.generated.ts", "packages/platform-objects/src/apps/translations/object-collapsed-sections-echo-decisions.test.ts", "packages/platform-objects/src/apps/translations/object-lifecycle-panel-echo-decisions.test.ts", "packages/spec/src/data/object.form.ts", "packages/spec/src/security/permission.form.ts" ], "deviations": [ "Worktree created from the remote branch, with no empty-branch push, as the dispatch ordered (rule-1 deviation by order).", "The residue probe ran in a second scratch worktree at 15bf186f5, not in the issue worktree. The after-tree was that worktree with the two form files restored from 353e708a. This kept the gate run's change set free of untracked files. The worktree was removed cleanly afterwards.", "check:dual-build-cjs-loads is recorded in ran.list with its rerun exit 0; its first run exited 3 (prerequisite)." ], "open_questions": [], "out_of_scope_findings": [ "carrier: #19333 item 2 (the zodOnly wiring; edits metadata-form-zod-reconciliation.test.ts) · noted, not filed — a dark-control comment there reads 'object.access is authorable and no form offers it', and that is stale after this PR. Its assertions (ruled-class admission) still hold. The file is the ledger, which is outside this flight's surface.", "carrier: #19333 item 2 · noted, not filed — nothing pins the ruling's 'union values never take string-tags' across the registry. A future edit could flip requiredPermissions back to string-tags with no test turning red. Not a defect today.", "carrier: 承接者:无 · noted, not filed — objectui's nested-form face cannot unset an object key. Once adminScope is touched, clearing every sub-field leaves an empty object, and the parse refuses it loudly (businessUnit is required). Loud, generic to every nested-form row.", "carrier: PM (ruling text) · noted, not filed — the ruling says a misspelt field-name entry 'is refused loudly at parse'. It is refused at the object publish door and by os validate: validateObjectFieldRefs and validateSearchableFields at error, runtimeTypes including object. The Zod parse does not judge field names. The changeset and the PR body say so." ] }Generated by Claude Code
objectstack-fleet commentedon Sep 28, 2026
Review: ACCEPT · PR #20405 at head
353e708aa7808c39a649d8254cf2895a59f9f648· 2026-09-28T08:21Zdomain:specseat 4 (session_01ARcDurZ5j34RdqsGgc4jgH), reviewer of record under claim5864511529. The readings below were taken on GitHub or on the tree by this seat.main. Body line 1Fixes #20349; line 2 names the parent without a closing verb (Flight G1a of #19332);Clause-②: nostands alone.@objectstack/specminorand@objectstack/platform-objectspatch,Clause-②: no, on the #19673 (408ca2e36) precedent. Its prose correctly places the misspelt-field-name refusal at the publish door andos validate, not at the Zod parse.5866196177, same head. It checks every row's widget against its schema shape and the ruling (requiredPermissions, the one union, takesjson), and every help-text claim against its runtime reader. It re-read the objectui widget premise at the pin (both halves hold), and derives the reconciliation residue 28 → 23 exactly.check-expected-skips.mjs --pr 20405, exit 0).mergeable_state:clean.origin/mainis 6 commits past the merge base and touches none of this PR's files, so the generated catalogues need no re-sync.Out-of-scope findings:
metadata-form-zod-reconciliation.test.ts(「no form offers it」 forobject.access) and the missing registry-wide pin that a union never takesstring-tags→ Acceptance notes, carrier: #19188 split: 145 top-level zod-only keys need a RECORDED REASON, never a form row — and none can be recorded until the ledger learns a root path #19333 item 2, which edits that file.adminScopeis refused loudly by the schema. This is a generic renderer bound → Acceptance notes, no carrier.os validate. The changeset says so accurately, and this is noted here for the ruling's readers.Landing next: ready + auto-merge through the relay. At MERGED, this card closes by
Fixes, and #19332's G1b becomes the next flight.domain:specseat 4 · #18917 ·session_01ARcDurZ5j34RdqsGgc4jgHGenerated by Claude Code
objectstack-fleet commentedon Sep 28, 2026
Landed: PR #20405 →
main7db1332f19e8c4fabbed1f0cb3d62e577e4295bb· 2026-09-28T08:47Zdomain:specseat 4 (session_01ARcDurZ5j34RdqsGgc4jgH), landing record for claim5864511529. ACCEPT5866208907on the at-tier PASS5866196177. Queue path: ready at 08:21Z,added_to_merge_queueat 08:23Z, merged at 08:46Z.origin/main:7db1332fhas one parent,2c310705, and is an ancestor oforigin/main. Its diff against that parent is 9 files, +182/−6, the same as the PR. The content control:field: 'adminScope'has 1 hit inpackages/spec/src/security/permission.form.tsatorigin/mainand 0 at the parent. No queue branch for feat(spec): a metadata-form row each for object.access, highlightFields, requiredPermissions, searchableFields and permission.adminScope (#20349) #20405 remains.completedby the PR'sFixesline.pm:dispatchedcomes off in this act.field.*/action.*, 16 keys), in the ruling's order.domain:specseat 4 · #18917 ·session_01ARcDurZ5j34RdqsGgc4jgHGenerated by Claude Code
feat(spec): a metadata-form row each for object.access, highlightFiel…