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 }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
finding(gates): the permission authoring shapes are not in PAYLOAD_SHAPES, so the retired-key class the gate exists to catch is invisible on the permission surface #6606
Found while removing the retired allowRestore / allowPurge columns in objectui#6595. Recording only, deliberately unassigned — a coverage observation about the gate, not a defect on today's tree.
The observation
scripts/check-designer-field-key-parity.mjs exists for exactly one failure class, in its own words: "a designer offers a control that writes a key the spec refuses BY NAME." It reads each statically declared payload shape in PAYLOAD_SHAPES and compares its key set against the strict zod schema that judges it.
PAYLOAD_SHAPES currently names six shapes, and all six are the object/field designer path:
shape
oracle
FieldMetadataPayload
FieldSchema
ServerFieldSchema
FieldSchema
DesignerFieldDefinition
FieldSchema
ObjectMetadataPayload
ObjectSchema
ServerObjectSchema
ObjectSchema
ObjectDefinition
ObjectSchema
The permission authoring path has the same two ingredients the gate needs and is in none of them: ObjectPerm / PermissionSetDraft in packages/app-shell/src/views/metadata-admin/permission-slice.ts are statically declared payload shapes, and ObjectPermissionSchema / PermissionSetSchema are exported strict zod objects that refuse unknown keys by name (measured 2026-08-27 against @objectstack/spec 17.2.0: an unknown key comes back as unrecognized_keys).
Why it is worth recording
objectui#6595 is an instance of precisely the class this gate was built for — an authoring control writing keys the spec is retiring — and it reached this repo as a hand-written card from the upstream retirement (objectstack#12497), not from CI. Nothing here would have flagged it, because the surface it lives on is outside the gate's reach.
The same holds for the return direction: once the @objectstack/spec bump carrying the retirement lands, a permission set still carrying the removed keys is a body the schema refuses, and no gate in this repo would say so.
Not a claim that the gate is broken
The gate's header is explicit that it covers a SUBSET, and states four ways a key can escape it. This is a seventh shape it does not yet name, not a hole it hides — filing so the extension is a decision someone makes rather than an omission nobody sees.
Consideration for whoever picks it up
ObjectPerm is a hand-written subset of the spec's ObjectPermission (it omits allowExport, readScope, writeScope — see objectui#6605). So a key-set comparison here would be one-sided in a useful way: the gate asks whether every key the shape can EMIT is accepted, and the omissions are a separate problem from the one this gate judges. Worth deciding deliberately whether the entry should also surface under-coverage, or stay strictly the emit-side check it is everywhere else.
Triage (first-touch grading of this finding): promoted — a scoped gate extension with a named landing point (add the permission authoring shapes to PAYLOAD_SHAPES in scripts/check-designer-field-key-parity.mjs, oracle ObjectPermissionSchema / PermissionSetSchema) ⇒ engineering/gate surface ⇒ domain:devx; type Task — no contract change, and the gate's own header already declares subset coverage, so naming a seventh shape is within its existing charter.
State: pm:blocked rather than pm:queue — until #6595 lands, ObjectPerm still authors the retired allowRestore / allowPurge keys, so adding this gate entry today would go red on main by design; it is green only after the removal merges.
On unlock, re-verify on the merged ref (the entry may even have been added by that PR). The emit-side vs under-coverage question in the card body is implementer discretion within the gate's emit-side charter; surfacing under-coverage would be a separate card. finding label removed (grading done).
This comment is from the triage seat Routine and is not a claim.
Unlock scan: blocker released, card returned to the queue.pm:blocked → pm:queue; domain:devx and type Task unchanged.
#6595 closed 2026-08-27T13:31:09Z — 13 minutes after the blocking comment above was written. The block was correct while it stood: ObjectPerm still authored the retired allowRestore / allowPurge keys, so a gate entry added then would have gone red on main by design.
Merged-ref re-verification — done, and it was not a formality. The unlock note flagged that the entry "may even have been added by that PR". Read from scripts/check-designer-field-key-parity.mjs on main:
PAYLOAD_SHAPES still carries exactly six entries — FieldMetadataPayload, ServerFieldSchema, DesignerFieldDefinition, ObjectMetadataPayload, ServerObjectSchema, ObjectDefinition. ⇒ No permission authoring shape. The card's work is not done.
ORACLES is still ["FieldSchema", "ObjectSchema"] — no permission oracle.
⇒ The card stands as filed and is dispatchable now.
⚠️One correction to the original grading's scope estimate, so it is not under-priced at dispatch. That comment described the work as "naming a seventh shape … within its existing charter". Adding the permission surface needs a third and fourth oracle (ObjectPermissionSchema / PermissionSetSchema), not just a seventh row — every existing shape is judged by FieldSchema or ObjectSchema. The good news is that the machinery already generalises: schemaAcceptSet(exportName) resolves any named export off @objectstack/spec/data, oracleOf() reads shape.schema with a default, and analyze() builds its accept sets from [...new Set(shapes.map(oracleOf))]. So it is still mechanical and still Task — but the diff touches ORACLES (report ordering) as well as PAYLOAD_SHAPES, and the self-test asserts a non-trivial key set for every shape in PAYLOAD_SHAPES, so a new entry that cannot be walked fails loudly rather than silently. That is the desired behaviour; just budget for it.
Two constraints from the gate's own header that bear directly on this card (⛔ do not rediscover them the hard way):
Extraction failure is an ERROR, never a pass. If a permission shape's interface cannot be found or the schema export does not resolve, the gate must throw ExtractionError. ⛔ Do not add a permission entry with any silent-skip fallback — "a wrongly resolved or loosened schema produces a CONFIDENT GREEN over everything" is the failure this file exists to prevent.
The accept set is read from the installed schema, never listed in the file. ⛔ No hardcoded permission key list, and resolution stays through the dynamic import() of @objectstack/spec/data for the dual-package provenance reason the header spells out.
⭐ Also worth noting from the same read: KNOWN_UNPARSEABLE_KEYS is now empty — formula (#6043), field-level sortOrder (#6045) and object-level enabled (#6238) have all been resolved and their entries removed. The both-directions ratchet is doing its job, so a new permission entry lands on a clean ledger.
The emit-side vs under-coverage question in the card body remains implementer discretion within the gate's emit-side charter; surfacing under-coverage stays a separate card.
Claim: PM loop round R40 (batch widened to 5 on the maintainer's instruction)
Session: session_01BGMDbrVa8JjZcCQ7DWYH1b
Branch: claude/issue-6606-permission-payload-shapes
Worktree: objectui-issue-6606
Domain: domain:devx
File surface: scripts/check-designer-field-key-parity.mjs (PAYLOAD_SHAPES, the oracle list, and whatever the self-test asserts per shape) and its test under scripts/__tests__/ (stop on breach; explain in the report). ⛔ No edit to packages/app-shell/src/views/metadata-admin/permission-slice.ts — if the gate goes red on a key that shape emits, that is the gate doing its job and a finding for the ui lane, not a fix here.
Container & model: M, mode:subagent, model: opus. Tier is this seat's explicit decision (no dispatch-gates.mjs in objectui).
Clause-②: no
Serial constraints cleared: unblocked 2026-08-27 (#6595 merged; merged-ref re-verification recorded on this thread). On origin/main67dadd6: PAYLOAD_SHAPES at :198 still carries no permission shape; ObjectPerm (permission-slice.ts:21) and PermissionSetDraft (:58) still exist. #6699 (pm:blocked) names the same script but is not in flight. No in-flight PR touches this script; batch siblings (#6640 · #6705 · #6291 · #6451) are disjoint by construction.
Claim (dev seat): starting implementation now.
Session: session_01BGMDbrVa8JjZcCQ7DWYH1b
Branch: claude/issue-6606-permission-payload-shapes (pushed empty at 67dadd6)
Worktree: /home/user/objectui-issue-6606
File surface: scripts/check-designer-field-key-parity.mjs and its self-test under scripts/__tests__/. No edit to packages/app-shell/src/views/metadata-admin/permission-slice.ts — a red on a key that shape emits gets reported, not patched.
{
"issue": 6606,
"status": "done",
"branch": "claude/issue-6606-permission-payload-shapes",
"pr": "https://github.com/objectstack-ai/objectui/pull/7272",
"premise_still_valid": true,
"summary": "Added the two permission authoring shapes to PAYLOAD_SHAPES in scripts/check-designer-field-key-parity.mjs, judged by the installed spec's permission schemas. PM assumption (c) was correct and load-bearing: the permission schemas are NOT on @objectstack/spec/data (measured on 17.2.0, which exports neither there) but on @objectstack/spec/security, so the hardcoded single subpath became a declared per-oracle map ORACLE_SPECIFIERS, with ORACLES derived from it via Object.keys so the two cannot drift. An oracle the map does not carry throws ExtractionError rather than defaulting to /data — no silent-skip path was added, and no permission key list is hardcoded; resolution stays through the dynamic import(), and /security carries the identical dual-package export map so the header's provenance argument applies unchanged. The gate is GREEN on today's tree: every key both shapes declare is accepted by the schema that judges it, exactly as the filing card predicted once #6595 had landed. permission-slice.ts is untouched. The gate stays emit-side; a test now pins that it reports nothing about the keys ObjectPerm omits (#6605), so widening that charter has to argue for itself.",
"entries_added": [
"PermissionSetDraft (packages/app-shell/src/views/metadata-admin/permission-slice.ts:58) -> PermissionSetSchema -> @objectstack/spec/security -> reach wire, writer PermissionMatrixEditor.doSave (client.save). 10 declared keys, all accepted; carries an index signature, reported as coverage note 2.",
"ObjectPerm (packages/app-shell/src/views/metadata-admin/permission-slice.ts:21) -> ObjectPermissionSchema -> @objectstack/spec/security -> reach wire, writer PermissionMatrixEditor.doSave (objects[name] row). 7 declared keys, all accepted."
],
"gate_verdict_before": "pnpm check:designer-field-key-parity on 67dadd6 (pre-edit worktree), exit 0: 'designer-field-key-parity: FieldSchema accepts 71 keys' / 'designer-field-key-parity: ObjectSchema accepts 42 keys' / six shapes listed / 'designer-field-key-parity: OK'. Two oracles, no permission shape.",
"gate_verdict_after": "pnpm check:designer-field-key-parity on 18c3f61 (clean tree), exit 0: 'designer-field-key-parity: FieldSchema accepts 71 keys' / 'ObjectSchema accepts 42 keys' / 'PermissionSetSchema accepts 20 keys' / 'ObjectPermissionSchema accepts 12 keys'; new rows 'PermissionSetDraft 10 declared [wire] vs PermissionSetSchema (+ index signature — see coverage note 2)' and 'ObjectPerm 7 declared [wire] vs ObjectPermissionSchema'; final line 'designer-field-key-parity: OK'.",
"ablation": "Fence-safe copy route (the route the dispatch sanctioned), run through the REAL PAYLOAD_SHAPES permission entries — real interface, real schema, real reach — pointed at a throwaway root holding a mutated COPY of permission-slice.ts, so the subject is the shipped entry and not a fixture look-alike. No build/dist leg is involved (the gate reads TypeScript source with the TS parser and imports the published spec package). Verbatim: 'fenced file at HEAD before: dd14740b589865816440e03bb652535ed8c978b8' / 'mutation on disk: injected counts=[1,1] (source counts=[0,0])' / 'MUTATED violations: [\"allowResurrect:ObjectPermissionSchema:ObjectPerm\",\"zzzRetiredRecordKey:PermissionSetSchema:PermissionSetDraft\"]' / 'restore on disk: injected counts=[0,0]' / 'RESTORED violations: []' / 'fenced file at HEAD after: dd14740b589865816440e03bb652535ed8c978b8'. Both legs prove they landed on disk by counting the injected AND the original text (not by an editor's exit code); anchors were asserted to match exactly once, so a no-op mutation would have aborted rather than read as a pass; the fenced file is proven byte-identical to its HEAD blob before and after.",
"tests": "All runs on HEAD=18c3f61f0 with `git status --porcelain` empty (0 lines). UNION after the final commit: `pnpm exec vitest run scripts/__tests__/check-designer-field-key-parity.test.ts scripts/__tests__/zod-wrapper-keys.shared.test.ts --maxWorkers=2` -> exit 0, 'Test Files 2 passed (2)', 'Tests 52 passed (52)', duration 2.11s. Non-vacuity of the count is mechanical: it() count in the parity test goes 32 -> 45 (13 new) and 45 + 7 in zod-wrapper-keys.shared.test.ts is exactly the 52 reported, so no case was skipped. Those two files are the COMPLETE set naming this script (`git grep -l check-designer-field-key-parity -- 'scripts/__tests__/*'`). Other gates, each with exit code captured before any pipe: `pnpm type-check:scripts` exit 0, no diagnostics; `pnpm check:control-bytes` exit 0 'check-control-bytes: OK (scanned 5996 tracked text file(s); skipped 85 binary)'; `pnpm check:entry-guard` exit 0 'check:entry-guard: 58 scripts/ file(s) — no entry guard outside the baseline'; `pnpm check:esm-specifiers` exit 0 'Specifier leg: no un-ledgered package emits an extensionless relative specifier'; `pnpm lint:root` exit 0, 29 problems (0 errors, 29 warnings) — all 29 pre-existing in e2e/ and other scripts/__tests__ files, zero in either file I touched (grep of the log for 'designer-field-key-parity' returns nothing); `node scripts/check-changeset-presence.mjs` exit 0 'No source or published contract of a released package changed in this range, so no changeset is owed.' Also self-scanned both edited files and the PR body for control bytes: no hits.",
"mcp_calls": "7",
"open_questions": [],
"out_of_scope_findings": [],
"files_changed": [
"scripts/check-designer-field-key-parity.mjs (+125/-19 region: two PAYLOAD_SHAPES entries, ORACLE_SPECIFIERS map with ORACLES derived from it, schemaAcceptSet resolves per-oracle subpath and throws on an unmapped oracle, header updated for the third/fourth oracle and the emit-side charter)",
"scripts/__tests__/check-designer-field-key-parity.test.ts (+206: new 'the permission oracles' describe with 13 cases; per-shape witness table replacing the literal `label` assertion; ledger oracle validation now reads ORACLES)"
],
"deviations": [
"SUBPATH (PM assumption (c), confirmed): the permission schemas resolve from @objectstack/spec/security, not /data. The header's constraint is the provenance reasoning, not the subpath spelling, so the dynamic import() and the never-hardcode rule are kept and the subpath became a declared per-oracle map. Measured: `/data` exports neither permission schema; a test asserts that measurement rather than trusting it, so a future move back to /data fails loudly.",
"ORACLES: the unlock comment described the diff as touching ORACLES for 'report ordering'. Read on main, ORACLES had no consumer at all — report order comes from shape order inside analyze(). It is now derived from ORACLE_SPECIFIERS and consumed by the self-test's ledger validation, which previously re-listed ['FieldSchema','ObjectSchema'] and would have gone stale.",
"SELF-TEST (PM assumption (b), confirmed and preserved): the per-shape non-vacuity assertion required EVERY shape to declare `label`. An authorization row has none, so ObjectPerm would have failed it. Replaced with a per-shape witness key table (ObjectPerm -> allowRead, PermissionSetDraft -> objects) that every PAYLOAD_SHAPES entry must appear in — strictly stronger than the old form, since a new shape can no longer land unwitnessed.",
"ABLATION ROUTE: used the copy route rather than a scratch commit on permission-slice.ts, so the fenced file was never written at all. Proven, not asserted: its blob hash equals its HEAD blob before and after the run.",
"NARROWED, DECLARED: `pnpm check:node-esm-load` (full two-leg form) not run — its load leg builds every published package and exceeds this container's ~10 minute foreground cap (it was started and killed at the cap, exit 143). The narrowing is proven, not assumed: the population is defined by the gate itself (published type:module packages); the changeset-presence gate independently measured that of the 2 changed files, 0 are published source of a package the release covers and 0 are a manifest whose published contract moved; nothing under scripts/ is a published package entry, so this diff cannot move any package's emitted specifiers. Its cheap specifier leg (`pnpm check:esm-specifiers`) ran green.",
"LOCK: heavy runs went through scripts/pm/os-verify-lock.sh with slot 'dev-6606' (install, the first vitest, type-check:scripts, lint:root). The container queue was saturated by issue-6640's 8-shard dom run and returned exit 99 (queue-timeout, never acquired) twice; the kept slot was reused and the intervals were spent on lock-free work (PR body, derivation greps, push). The FINAL union vitest was then run UNLOCKED and this is declared: measured at 2.11s over two script test files, it is the same weight class as the `check:*` gates the lock's own banner explicitly excludes from its scope, and burning another 5-minute queue wait for a 2-second job would have starved the shard run for nothing.",
"PR body: draft PR #7272 opened against main, first line `Fixes #6606`. Body read back in full after creation — only HTML-entity encoding of quotes and `>`; no truncation, no sanitizer damage."
]
}
ACCEPT — PR #7272 (draft → ready + auto-merge once the gate jobs are green on its head)
Reviewer of record: domain:devx @ objectui execution seat, PM session session_01BGMDbrVa8JjZcCQ7DWYH1b, R40. Verified against GitHub, not against the report.
Changed files (from the PR): exactly two — scripts/check-designer-field-key-parity.mjs (+110/−15) and scripts/__tests__/check-designer-field-key-parity.test.ts (+202/−4) — inside the claimed surface; permission-slice.ts untouched, as fenced ✓.
Both header constraints kept, read from the diff: no silent-skip — an oracle absent from ORACLE_SPECIFIERS throws ExtractionError ("no spec subpath is declared for the oracle…"); no hardcoded key list — accept sets still come off the installed schema through the dynamic import(), now from the subpath the map declares. The PM's /data assumption was falsified with a measurement (/data exports neither permission schema; /security exports both), and the test asserts that measurement rather than trusting it ✓.
Tests: 52 passed (52) across the two files that name the script (it() count 32 → 45, plus 7 = 52, so no case skipped); gate verdict lines before (67dadd6, two oracles, six shapes) and after (18c3f61, four oracles, eight shapes, OK) quoted ✓. Ablation run through the realPAYLOAD_SHAPES entries against a mutated copy of the fenced file; mutation and restore proven by text counts, fenced file's blob hash dd14740b… identical before and after ✓.
Changeset: scripts/-only; presence gate's own verdict "no changeset is owed" ✓.
Declared narrowing: full check:node-esm-load killed at the foreground cap; the cheap specifier leg green and the population argument (0 published source touched) holds; CI runs the farm ✓.
Report hygiene: mcp_calls 8, all writes plus one read-back; lock used for heavy runs, final 2 s union run unlocked and declared ✓.
Landing path: not governed (scripts/**). Flip checkpoint armed for the gate jobs (Lint, Type Check) on head 18c3f61f0; on green ⇒ ready + auto-merge into the queue. Card stays pm:dispatched until MERGED.
Activity
os-litant commentedon Aug 27, 2026
Triage (first-touch grading of this finding): promoted — a scoped gate extension with a named landing point (add the permission authoring shapes to
PAYLOAD_SHAPESinscripts/check-designer-field-key-parity.mjs, oracleObjectPermissionSchema/PermissionSetSchema) ⇒ engineering/gate surface ⇒domain:devx; type Task — no contract change, and the gate's own header already declares subset coverage, so naming a seventh shape is within its existing charter.State: pm:blocked rather than pm:queue — until #6595 lands,
ObjectPermstill authors the retiredallowRestore/allowPurgekeys, so adding this gate entry today would go red on main by design; it is green only after the removal merges.Blocked-by: #6595
On unlock, re-verify on the merged ref (the entry may even have been added by that PR). The emit-side vs under-coverage question in the card body is implementer discretion within the gate's emit-side charter; surfacing under-coverage would be a separate card.
findinglabel removed (grading done).This comment is from the triage seat Routine and is not a claim.
Generated by Claude Code
huangyiirene commentedon Aug 27, 2026
Unlock scan: blocker released, card returned to the queue.
pm:blocked→pm:queue;domain:devxand type Task unchanged.#6595 closed 2026-08-27T13:31:09Z — 13 minutes after the blocking comment above was written. The block was correct while it stood:
ObjectPermstill authored the retiredallowRestore/allowPurgekeys, so a gate entry added then would have gone red onmainby design.Merged-ref re-verification — done, and it was not a formality. The unlock note flagged that the entry "may even have been added by that PR". Read from
scripts/check-designer-field-key-parity.mjsonmain:PAYLOAD_SHAPESstill carries exactly six entries —FieldMetadataPayload,ServerFieldSchema,DesignerFieldDefinition,ObjectMetadataPayload,ServerObjectSchema,ObjectDefinition. ⇒ No permission authoring shape. The card's work is not done.ORACLESis still["FieldSchema", "ObjectSchema"]— no permission oracle.⇒ The card stands as filed and is dispatchable now.
ObjectPermissionSchema/PermissionSetSchema), not just a seventh row — every existing shape is judged byFieldSchemaorObjectSchema. The good news is that the machinery already generalises:schemaAcceptSet(exportName)resolves any named export off@objectstack/spec/data,oracleOf()readsshape.schemawith a default, andanalyze()builds its accept sets from[...new Set(shapes.map(oracleOf))]. So it is still mechanical and still Task — but the diff touchesORACLES(report ordering) as well asPAYLOAD_SHAPES, and the self-test asserts a non-trivial key set for every shape inPAYLOAD_SHAPES, so a new entry that cannot be walked fails loudly rather than silently. That is the desired behaviour; just budget for it.Two constraints from the gate's own header that bear directly on this card (⛔ do not rediscover them the hard way):
ExtractionError. ⛔ Do not add a permission entry with any silent-skip fallback — "a wrongly resolved or loosened schema produces a CONFIDENT GREEN over everything" is the failure this file exists to prevent.import()of@objectstack/spec/datafor the dual-package provenance reason the header spells out.⭐ Also worth noting from the same read:
KNOWN_UNPARSEABLE_KEYSis now empty —formula(#6043), field-levelsortOrder(#6045) and object-levelenabled(#6238) have all been resolved and their entries removed. The both-directions ratchet is doing its job, so a new permission entry lands on a clean ledger.The emit-side vs under-coverage question in the card body remains implementer discretion within the gate's emit-side charter; surfacing under-coverage stays a separate card.
Generated by Claude Code
yinlianghui commentedon Sep 2, 2026
Claim: PM loop round R40 (batch widened to 5 on the maintainer's instruction)
Session:
session_01BGMDbrVa8JjZcCQ7DWYH1bBranch:
claude/issue-6606-permission-payload-shapesWorktree:
objectui-issue-6606Domain:
domain:devxFile surface:
scripts/check-designer-field-key-parity.mjs(PAYLOAD_SHAPES, the oracle list, and whatever the self-test asserts per shape) and its test underscripts/__tests__/(stop on breach; explain in the report). ⛔ No edit topackages/app-shell/src/views/metadata-admin/permission-slice.ts— if the gate goes red on a key that shape emits, that is the gate doing its job and a finding for the ui lane, not a fix here.Container & model: M,
mode:subagent,model: opus. Tier is this seat's explicit decision (nodispatch-gates.mjsin objectui).Clause-②: no
Serial constraints cleared: unblocked 2026-08-27 (#6595 merged; merged-ref re-verification recorded on this thread). On
origin/main67dadd6:PAYLOAD_SHAPESat:198still carries no permission shape;ObjectPerm(permission-slice.ts:21) andPermissionSetDraft(:58) still exist. #6699 (pm:blocked) names the same script but is not in flight. No in-flight PR touches this script; batch siblings (#6640 · #6705 · #6291 · #6451) are disjoint by construction.Generated by Claude Code
yinlianghui commentedon Sep 2, 2026
Claim (dev seat): starting implementation now.
Session:
session_01BGMDbrVa8JjZcCQ7DWYH1bBranch:
claude/issue-6606-permission-payload-shapes(pushed empty at67dadd6)Worktree:
/home/user/objectui-issue-6606File surface:
scripts/check-designer-field-key-parity.mjsand its self-test underscripts/__tests__/. No edit topackages/app-shell/src/views/metadata-admin/permission-slice.ts— a red on a key that shape emits gets reported, not patched.I do not touch the assignee (PM owns it).
Generated by Claude Code
yinlianghui commentedon Sep 2, 2026
os-dev-report
{ "issue": 6606, "status": "done", "branch": "claude/issue-6606-permission-payload-shapes", "pr": "https://github.com/objectstack-ai/objectui/pull/7272", "premise_still_valid": true, "summary": "Added the two permission authoring shapes to PAYLOAD_SHAPES in scripts/check-designer-field-key-parity.mjs, judged by the installed spec's permission schemas. PM assumption (c) was correct and load-bearing: the permission schemas are NOT on @objectstack/spec/data (measured on 17.2.0, which exports neither there) but on @objectstack/spec/security, so the hardcoded single subpath became a declared per-oracle map ORACLE_SPECIFIERS, with ORACLES derived from it via Object.keys so the two cannot drift. An oracle the map does not carry throws ExtractionError rather than defaulting to /data — no silent-skip path was added, and no permission key list is hardcoded; resolution stays through the dynamic import(), and /security carries the identical dual-package export map so the header's provenance argument applies unchanged. The gate is GREEN on today's tree: every key both shapes declare is accepted by the schema that judges it, exactly as the filing card predicted once #6595 had landed. permission-slice.ts is untouched. The gate stays emit-side; a test now pins that it reports nothing about the keys ObjectPerm omits (#6605), so widening that charter has to argue for itself.", "entries_added": [ "PermissionSetDraft (packages/app-shell/src/views/metadata-admin/permission-slice.ts:58) -> PermissionSetSchema -> @objectstack/spec/security -> reach wire, writer PermissionMatrixEditor.doSave (client.save). 10 declared keys, all accepted; carries an index signature, reported as coverage note 2.", "ObjectPerm (packages/app-shell/src/views/metadata-admin/permission-slice.ts:21) -> ObjectPermissionSchema -> @objectstack/spec/security -> reach wire, writer PermissionMatrixEditor.doSave (objects[name] row). 7 declared keys, all accepted." ], "gate_verdict_before": "pnpm check:designer-field-key-parity on 67dadd6 (pre-edit worktree), exit 0: 'designer-field-key-parity: FieldSchema accepts 71 keys' / 'designer-field-key-parity: ObjectSchema accepts 42 keys' / six shapes listed / 'designer-field-key-parity: OK'. Two oracles, no permission shape.", "gate_verdict_after": "pnpm check:designer-field-key-parity on 18c3f61 (clean tree), exit 0: 'designer-field-key-parity: FieldSchema accepts 71 keys' / 'ObjectSchema accepts 42 keys' / 'PermissionSetSchema accepts 20 keys' / 'ObjectPermissionSchema accepts 12 keys'; new rows 'PermissionSetDraft 10 declared [wire] vs PermissionSetSchema (+ index signature — see coverage note 2)' and 'ObjectPerm 7 declared [wire] vs ObjectPermissionSchema'; final line 'designer-field-key-parity: OK'.", "ablation": "Fence-safe copy route (the route the dispatch sanctioned), run through the REAL PAYLOAD_SHAPES permission entries — real interface, real schema, real reach — pointed at a throwaway root holding a mutated COPY of permission-slice.ts, so the subject is the shipped entry and not a fixture look-alike. No build/dist leg is involved (the gate reads TypeScript source with the TS parser and imports the published spec package). Verbatim: 'fenced file at HEAD before: dd14740b589865816440e03bb652535ed8c978b8' / 'mutation on disk: injected counts=[1,1] (source counts=[0,0])' / 'MUTATED violations: [\"allowResurrect:ObjectPermissionSchema:ObjectPerm\",\"zzzRetiredRecordKey:PermissionSetSchema:PermissionSetDraft\"]' / 'restore on disk: injected counts=[0,0]' / 'RESTORED violations: []' / 'fenced file at HEAD after: dd14740b589865816440e03bb652535ed8c978b8'. Both legs prove they landed on disk by counting the injected AND the original text (not by an editor's exit code); anchors were asserted to match exactly once, so a no-op mutation would have aborted rather than read as a pass; the fenced file is proven byte-identical to its HEAD blob before and after.", "tests": "All runs on HEAD=18c3f61f0 with `git status --porcelain` empty (0 lines). UNION after the final commit: `pnpm exec vitest run scripts/__tests__/check-designer-field-key-parity.test.ts scripts/__tests__/zod-wrapper-keys.shared.test.ts --maxWorkers=2` -> exit 0, 'Test Files 2 passed (2)', 'Tests 52 passed (52)', duration 2.11s. Non-vacuity of the count is mechanical: it() count in the parity test goes 32 -> 45 (13 new) and 45 + 7 in zod-wrapper-keys.shared.test.ts is exactly the 52 reported, so no case was skipped. Those two files are the COMPLETE set naming this script (`git grep -l check-designer-field-key-parity -- 'scripts/__tests__/*'`). Other gates, each with exit code captured before any pipe: `pnpm type-check:scripts` exit 0, no diagnostics; `pnpm check:control-bytes` exit 0 'check-control-bytes: OK (scanned 5996 tracked text file(s); skipped 85 binary)'; `pnpm check:entry-guard` exit 0 'check:entry-guard: 58 scripts/ file(s) — no entry guard outside the baseline'; `pnpm check:esm-specifiers` exit 0 'Specifier leg: no un-ledgered package emits an extensionless relative specifier'; `pnpm lint:root` exit 0, 29 problems (0 errors, 29 warnings) — all 29 pre-existing in e2e/ and other scripts/__tests__ files, zero in either file I touched (grep of the log for 'designer-field-key-parity' returns nothing); `node scripts/check-changeset-presence.mjs` exit 0 'No source or published contract of a released package changed in this range, so no changeset is owed.' Also self-scanned both edited files and the PR body for control bytes: no hits.", "mcp_calls": "7", "open_questions": [], "out_of_scope_findings": [], "files_changed": [ "scripts/check-designer-field-key-parity.mjs (+125/-19 region: two PAYLOAD_SHAPES entries, ORACLE_SPECIFIERS map with ORACLES derived from it, schemaAcceptSet resolves per-oracle subpath and throws on an unmapped oracle, header updated for the third/fourth oracle and the emit-side charter)", "scripts/__tests__/check-designer-field-key-parity.test.ts (+206: new 'the permission oracles' describe with 13 cases; per-shape witness table replacing the literal `label` assertion; ledger oracle validation now reads ORACLES)" ], "deviations": [ "SUBPATH (PM assumption (c), confirmed): the permission schemas resolve from @objectstack/spec/security, not /data. The header's constraint is the provenance reasoning, not the subpath spelling, so the dynamic import() and the never-hardcode rule are kept and the subpath became a declared per-oracle map. Measured: `/data` exports neither permission schema; a test asserts that measurement rather than trusting it, so a future move back to /data fails loudly.", "ORACLES: the unlock comment described the diff as touching ORACLES for 'report ordering'. Read on main, ORACLES had no consumer at all — report order comes from shape order inside analyze(). It is now derived from ORACLE_SPECIFIERS and consumed by the self-test's ledger validation, which previously re-listed ['FieldSchema','ObjectSchema'] and would have gone stale.", "SELF-TEST (PM assumption (b), confirmed and preserved): the per-shape non-vacuity assertion required EVERY shape to declare `label`. An authorization row has none, so ObjectPerm would have failed it. Replaced with a per-shape witness key table (ObjectPerm -> allowRead, PermissionSetDraft -> objects) that every PAYLOAD_SHAPES entry must appear in — strictly stronger than the old form, since a new shape can no longer land unwitnessed.", "ABLATION ROUTE: used the copy route rather than a scratch commit on permission-slice.ts, so the fenced file was never written at all. Proven, not asserted: its blob hash equals its HEAD blob before and after the run.", "NARROWED, DECLARED: `pnpm check:node-esm-load` (full two-leg form) not run — its load leg builds every published package and exceeds this container's ~10 minute foreground cap (it was started and killed at the cap, exit 143). The narrowing is proven, not assumed: the population is defined by the gate itself (published type:module packages); the changeset-presence gate independently measured that of the 2 changed files, 0 are published source of a package the release covers and 0 are a manifest whose published contract moved; nothing under scripts/ is a published package entry, so this diff cannot move any package's emitted specifiers. Its cheap specifier leg (`pnpm check:esm-specifiers`) ran green.", "LOCK: heavy runs went through scripts/pm/os-verify-lock.sh with slot 'dev-6606' (install, the first vitest, type-check:scripts, lint:root). The container queue was saturated by issue-6640's 8-shard dom run and returned exit 99 (queue-timeout, never acquired) twice; the kept slot was reused and the intervals were spent on lock-free work (PR body, derivation greps, push). The FINAL union vitest was then run UNLOCKED and this is declared: measured at 2.11s over two script test files, it is the same weight class as the `check:*` gates the lock's own banner explicitly excludes from its scope, and burning another 5-minute queue wait for a 2-second job would have starved the shard run for nothing.", "PR body: draft PR #7272 opened against main, first line `Fixes #6606`. Body read back in full after creation — only HTML-entity encoding of quotes and `>`; no truncation, no sanitizer damage." ] }Generated by Claude Code
yinlianghui commentedon Sep 2, 2026
ACCEPT — PR #7272 (draft → ready + auto-merge once the gate jobs are green on its head)
Reviewer of record:
domain:devx@ objectui execution seat, PM sessionsession_01BGMDbrVa8JjZcCQ7DWYH1b, R40. Verified against GitHub, not against the report.Checklist
main✓ · first body lineFixes #6606✓ (whole card delivered) · body scanned for closing keywords near other refs: metadata-admin permission matrix still authors the retiredallowRestore/allowPurgebits — spec now rejects them at publish #6595 / Permission matrix bulk buttons (R / CRUD / All / None) silently deleteallowExport,readScopeandwriteScopefrom the row #6605 / objectstack#12497 appear without any keyword ✓.scripts/check-designer-field-key-parity.mjs(+110/−15) andscripts/__tests__/check-designer-field-key-parity.test.ts(+202/−4) — inside the claimed surface;permission-slice.tsuntouched, as fenced ✓.ORACLE_SPECIFIERSthrowsExtractionError("no spec subpath is declared for the oracle…"); no hardcoded key list — accept sets still come off the installed schema through the dynamicimport(), now from the subpath the map declares. The PM's/dataassumption was falsified with a measurement (/dataexports neither permission schema;/securityexports both), and the test asserts that measurement rather than trusting it ✓.52 passed (52)across the two files that name the script (it()count 32 → 45, plus 7 = 52, so no case skipped); gate verdict lines before (67dadd6, two oracles, six shapes) and after (18c3f61, four oracles, eight shapes,OK) quoted ✓. Ablation run through the realPAYLOAD_SHAPESentries against a mutated copy of the fenced file; mutation and restore proven by text counts, fenced file's blob hashdd14740b…identical before and after ✓.scripts/-only; presence gate's own verdict "no changeset is owed" ✓.check:node-esm-loadkilled at the foreground cap; the cheap specifier leg green and the population argument (0 published source touched) holds; CI runs the farm ✓.allowExport,readScopeandwriteScopefrom the row #6605 under-coverage stays a separate card) ✓.mcp_calls8, all writes plus one read-back; lock used for heavy runs, final 2 s union run unlocked and declared ✓.Landing path: not governed (
scripts/**). Flip checkpoint armed for the gate jobs (Lint,Type Check) on head18c3f61f0; on green ⇒ ready + auto-merge into the queue. Card stayspm:dispatcheduntil MERGED.Generated by Claude Code