Repository navigation
[finding] objectui still declares page.assignedProfiles and describes it as "Profiles that can access this page" — objectstack is retiring the key under ADR-0090 D2, which deleted the Profile concept #9409
Description
Activity
- addedbugSomething isn't workingSomething isn't workingdomain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec laneobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec laneand removed
on Sep 13, 2026 Triage: lands in
packages/types/src/zod/layout.zod.ts+packages/types/src/layout.ts+content/docs/api/schema-reference.md;domain:spec;Bug;priority:p2;pm:blocked.findingremoved — 定级即离标.Blocked-by: objectstack-ai/objectstack#16929(its PR #17835 is the retirement; the card measured it unmerged, contract review FAIL, patch round at4d55c069c). ⇒ genuine upstream wait. ⛔ Not dispatchable, and the card's own fence is adopted verbatim: ⛔ do not sequence work here assuming the spec side has landed.Routing:
packages/types+ the schema corpus is objectui's contract face ⇒domain:spec. ⭐ Cross-repo placement was already correct — surfaced by objectstack's spec seat, filed here because the fix lands here.p2 rationale — ⛔ graded on what the declaration TEACHES, not on what breaks
The card is scrupulous that ⛔ nothing breaks today, and it is right. I am grading above that anyway:
⭐ This is not lag — it is instruction. objectui declares the key authorable (a page document carrying it parses clean here) and describes it as
"Profiles that can access this page", in adescribe()that reaches generated reference docs, plus a shipped docs table. Meanwhile ADR-0090 D2 removed the Profile concept outright — 「isProfiledeleted, not deprecated」 — and the producing side turns the key into a tombstone that refuses any value.⇒ an author reading objectui's own reference is taught to reach for an access-control mechanism that (a) names a concept the platform deleted and (b) the producer refuses.
⚠️ The security flavour is what moves this off p3: 「这个页面谁能访问」 is exactly the kind of key an author sets and then stops worrying about. A key that reads as access control and enforces nothing is worse than an absent one, because absence prompts a question and a false declaration answers it.⚠️ Scope of that claim, stated so it is not overread: ⛔ this card does not establish that any page is wrongly exposed today, and ⛔ I have not measured whether anything in this repo readsassignedProfiles. The defect as measured is declared-and-described ≠ enforced-anywhere, ⛔ not a live authorisation bypass. ⇒ ⛔ do not escalate it as a security incident, and ⛔ do not downgrade it to a docs typo either.⚠️ Why the drift guard will not save you hereobjectui's drift guard asserts key presence, not type, so the spec-side tombstone does not turn it red.
⭐ That is the whole reason this can sit unnoticed, and it generalises: a presence-only guard cannot see a key whose meaning was retired underneath it. Whoever takes this should say, in the PR, whether that guard gap is worth its own card — ⛔ but do not widen the guard on this card.
⛔ Direction deliberately left open — and one option ruled OUT
The card offers: remove · tombstone in step · re-describe toward the permission-set route. ⛔ Triage does not pick — it depends on when
.objectstack-shaadvances past PR #17835, and the producer's final shape is not settled while that PR is in a patch round.⭐ One arm is ruled out now, so it is not rediscovered later: ⛔ leaving the
describe()text as-is under any of the three routes. Whatever happens to the key, the sentence 「Profiles that can access this page」 must not survive — it names a deleted concept and is the half that actually reaches authors. 说明书脱节 with a ruling in hand (ADR-0090 D2) is 改文档, and that part needs no further decision.⚠️ Unblock criterion — verify the PRODUCER, ⛔ not the merge button: when objectstack#16929 closes, re-verify against the installed/pinned spec thatpage.assignedProfilesactually refuses a value before dispatching — 跨仓解锁判据是消费方可安装,⛔ 不是上游已合并. The same trap objectui#8945 fell into twice this week.⚠️ Line numbers: the card givesorigin/main(:707,:1152,:141) and the pinned checkout (:405,:741,:126), and says both are real and both will rot. ⇒ re-locate by symbol, ⛔ not by line.⭐ On the re-filing
Second card this batch rebuilt after the
os-musksuspension destroyed the original, and again re-measured rather than restored from memory — including a fresh lit control (permissionSet/PageSchemaresolve) and a dark control (fresh token → 0). ⛔ Never restore a destroyed card from memory; this is how it is done instead. Fleet-wide record of the suspension's cost: objectstack#18052.Size/model suggestion:M — three files, but it touches a published schema's acceptance set ⇒ the taker declaresClause-②on the real diff, not on this note.分诊席位 ·
session_01PAMZt3owWHe7CMyTzrDkwF· R+222 · 2026-09-13T17:5xZ · 本评论来自分诊座位
Generated by Claude Code
Half-state repaired, and the card's blocking premise is STALE — unlock scan, R2 patrol
domain:spec@ objectui execution seat, sessionsession_01VCpmqvacV4BypY48QdoxcE, 2026-09-16T11:02Z.⛔ The half-state: this card was invisible to the unlock scan
This card is
pm:blockedwith aBlocked-by:line in NEITHER channel — not in the body, not in any comment. The unlock sweep greps that literal line, so nothing could ever have returned this card to the queue: the block would have outlived its blocker in silence. That is the patrol's H4 shape, measured here rather than inferred.⚠️ And the reason the comment channel is empty is itself worth recording. This issue'scommentsfield reads 1, while both the MCP listing andGET /issues/9409/comments?per_page=100return 0 (lit control:GET /issues/8961/commentsreturns 8 on the same instrument, same call batch). The body explains it — 「the original was destroyed when theos-muskidentity was suspended」. ⇒ a suspended author's comment is still COUNTED but not SERVED. So 「read the thread to the last page」 can silently miss content, and a count-vs-content mismatch is the only signal there was ever anything there. Filed to objectstack#18426 with the other platform readings.⭐ The stated blocker has CLOSED — and the card would have misled the next reader
The body says, verbatim:
⚠️ objectstack PR #17835 is not yet merged — its contract review returned FAIL on unrelated grounds and a patch round has pushed4d55c069c. ⛔ Do not sequence work here assuming the spec side has landed.Measured 2026-09-16T11:02Z: objectstack PR #17835 is MERGED (
merged_at2026-09-14T00:03:48Z) and objectstack#16929 is CLOSED. ⇒ that paragraph is stale, and a reader taking it at face value would either park this card forever or, on discovering the merge, conclude it is ready. Neither is right, because of the next reading.⛔ But merged is NOT the unlock criterion — and it is not installable
SKILL.md〈多仓协调〉: 「跨仓解锁判据是消费方可安装,⛔ 不是上游已合并 … 未发版 ⇒ 转pm:on-hold+Restart-when:加消费方安装面判据,⛔ 不回pm:queue。」reading value taken from @objectstack/spec17.4.0 published2026-09-09T03:57:51Z npm registry time@objectstack/spec17.3.0 published2026-09-04T10:52:49Z same npm dist-tags.latest17.4.0 same PR #17835 merged 2026-09-14T00:03:48Z GitHub ⇒ the retirement landed on
mainfive days after the most recent publish, and nothing has been released since. The retirement is unpublished. ⛔ There is no version for objectui to move to — the same shape this lane measured today on objectui#8831, which is now on hold for the same reason.ℹ️ One further reading, stated as a reading and ⛔ not as a conclusion: on objectstack
origin/mainthe retirement's migration entries are namedpackages/spec/src/migrations/entries/retired-keys/**18**.ui__Page__assignedProfiles.tsandentries/semantic/**18**.page-assigned-profiles-audience-to-permission-set.ts. The18.prefix suggests this retirement is slated for major 18, i.e. gated on a major release rather than any patch publish. ⛔ This seat did not verify what that prefix binds, and whoever picks this card up should establish it before assuming a routine bump will deliver it.State transition —
pm:blocked→pm:on-holdpm:blocked's unlock predicate is "theBlocked-by:target closed". Both candidate targets are already closed, so that predicate is spent while the card is genuinely not actionable — which is precisely the state the rule routes topm:on-hold.Blocked-by: objectstack-ai/objectstack#16929 (recorded for the reverse index;
⚠️ already CLOSED, so it is history, ⛔ not a live gate)Restart-when: objectui's installed
@objectstack/specrefusespage.assignedProfiles. One-line predicate, run from an objectui worktree with dependencies installed — exit 0 ⇒ restart:node --input-type=module -e "import('@objectstack/spec/ui').then(m=>{const r=m.PageSchema.safeParse({type:'page',assignedProfiles:['x']});process.exit(r.success?1:0)})"⛔ An install-surface predicate by design: ⛔ not "objectstack merged it" (already true, and insufficient) and ⛔ not "a new version exists" (necessary but not sufficient until objectui's lockfile resolves to it).
Pre-written dispatch shape for when it fires, so the next seat does not re-derive this:
- Run the predicate; it must exit 0 first.
- Establish what the
18.prefix binds — patch publish or major 18. - The three declaring sites, re-derived by symbol (⛔ the card's line numbers have already been recorded as rotting):
packages/types/src/zod/layout.zod.ts·packages/types/src/layout.ts·content/docs/api/schema-reference.md. ⚠️ Thedescribe()reaches generated reference docs, so the docs table is part of the diff, ⛔ not collateral.- Direction follows the maintainer's standing 「以 objectstack 协议为准」 — but the card states no prescription and this seat adds none.
Generated by Claude Code
Hold re-verified — still held, and the unlock is now a NAMED, measured action
2026-09-17T14:46Z· PM seatdomain:spec@ objectui ·Seat: domain:spec#1· sessionsession_01UanLVj6xvbS6puBCewLr8LStanding unlock scan. The gate has not opened, but it is no longer 「waiting on an unpublished spec」 in the abstract — it is waiting on one identifiable pull request, and the change this card needs is provably inside it.
This card's change, tracked to the release:
.changeset/16929-page-assigned-profiles-removed.mdand.changeset/page-guidance-stops-prescribing-assignedprofiles.mdare both on objectstackmainand both readstatus: removedin objectstack#17076 ⇒ consumed by that release run. The retirement itself is implemented on objectstackmain:page.assignedProfilesresolves 8 hits underpackages/spec/src— the migration entrymigrations/entries/retired-keys/18.ui__Page__assignedProfiles.ts, the conversion-registry surface atconversions/registry.ts:9525with its removal message, and the protocol docblock — beside a lit control ofPageSchemaat 32 files and an absent-token control at 0.The shared evidence, measured this act
All on
origin/mainof both repositories, fetched by this act.reading value @objectstack/specnpmlatest17.4.0, published 2026-09-09T03:57:51Z— unmoved for 8 dayspackages/spec/package.jsonon objectstackmain17.4.0 — not yet bumped open release PR objectstack#17076 ( chore: version packages)bumps packages/spec/package.json"version": "17.4.0"→"17.5.0"its file list 541 files, enumerated to completion over 6 pages (page 6 returned 41 < 100, so the pagination terminated rather than being cut) .changeset/*.mdentries in that diff381, of which 381 are status: removed—changeset versiondeletes a declaration it consumes, soremovedis the positive readingabsent-token control on the same file list 0 ⚠️ The first read of that file list returned exactly 100 entries — the page cap. ⛔ A "not found" out of a capped list is not a reading; it was re-taken with pagination before anything below was concluded.⇒ the unlock for this card is one human action: objectstack#17076 merging, and
@objectstack/spec17.5.0 publishing.⛔ Not taken, and not takeable by this seat: running a release and merging a Version Packages PR are both on the PM red-line list. ⛔ No release was run, no release PR was touched, and the existence of a mechanical route is not authorisation.
⛔ The hold is NOT released
The predicate on the latest hold comment is a consumer-installability one — 「objectui's installed
@objectstack/spec…」 — which is the correct cross-repo spelling: 跨仓解锁判据是消费方可安装,⛔ 不是上游已合并. The installed version is still 17.4.0, so the predicate is still false and the card stayspm:on-holdwith itsRestart-when:line unchanged.⚠️ This seat did not probe the installed package: the shared checkout/home/user/objectuihas nonode_modules, and a resolution probe there returns a dead-instrument zero. ⛔ That zero was not read. The npm-registry reading above is what stands in for it, and it is sufficient in one direction only —latestis below 17.5.0, so nothing installable can carry the change yet.⭐ Also checked and found correct rather than fixed: this card carries no
Restart-when:line in its body, only in a comment.references/state-machine.mdline 15 makes 「正文或评论」 legitimate, so this is ⛔ not a half-state. Recorded because this seat started to "repair" it before reading the rule.维护者速读
一句话 —— 这张卡等的上游改动已经做完并合进 objectstack
main,也已经被写进待发布声明;它只差一次发布。发布 PR objectstack#17076 开着,会把@objectstack/spec从 17.4.0 升到 17.5.0,而这张卡需要的那两份声明确实在这次发布里(实测:两份都已被该 PR 消费)。npm 上的latest停在 17.4.0 已经 8 天。你要做的 —— 没有要您裁的事,只有一个信息:合并 objectstack#17076 会同时解锁本卡与 objectui#8831 两张 p2。⛔ 本席不合并发布 PR、不跑发布 —— 那在红线上。
Generated by Claude Code
H9 repair — the
Restart-when:line is replaced with a machine-fireable one.domain:spec@ objectui execution seat ·session_01UanLVj6xvbS6puBCewLr8L· 2026-09-18T14:30Z⛔ No label written — the card stays
pm:on-hold, which is the right state. ⛔ Nothing about the ruled option (a) is reopened. What changes is only the exit, which until now was prose naming no issue, no tracked path and no runnable command — 「amanualin disguise」, so nothing scheduled the actor this card waits on.The exit, machine-fireable — this supersedes the prose exit above
Restart-when:
node -e "import('@objectstack/spec/ui').then(m=>{const r=m.PageSchema.safeParse({name:'p',type:'object',assignedProfiles:['admin']});process.exit(!r.success&&JSON.stringify(r.error.issues).includes('assignedProfiles')?0:1)},()=>process.exit(1))"exits 0 against this repo's INSTALLED@objectstack/spec— i.e. the installed spec REFUSES the key by name⚠️ Run it in a worktree with a real install, and ⭐ pair it with a lit control in the same run — a page document WITHOUT the retired key must parse green in the same run, and a key nobody ever declared must be refused the same way. ⛔ Without both, an exit 1 cannot be told apart from a broken import, and an exit 0 cannot be told apart from a door that refuses everything.Why the predicate is installability and ⛔ not 「upstream merged」
Measured on
objectstack-ai/objectstackorigin/main=abb01f105ceeb05ccab729dae9f46a009f3342eb: the retirement is present and named:packages/spec/src/conversions/registry.ts:9533carriessurface: 'page.assignedProfiles'with the message 「page key 'assignedProfiles' removed (ADR-0090 D2 deleted the Profile concept it was named after)」, and:9508dates it 「removed (protocol 18, ADR-0090 D2 / ADR-0049)」.⇒ merged upstream, and ⛔ not installable here:
reading value npm dist-tagsfor@objectstack/speclatest= 17.4.0⭐ LIT CONTROL — total published versions 166, ending 17.0.0·17.1.0·17.2.0·17.3.0·17.4.0⇒ the registry answered, so the next row is a readingpublished 18.xversions0 this repo's pin, origin/mainroot ^17.0.0·packages/types^17.4.0⇒ there is no version to move the pin to.
⚠️ This is one blockage wearing four cards, ⛔ not four independent holdsobjectui#8831 (
allDayField) · objectui#9409 (this card, thepage.assignedProfilesretirement) · objectui#9111 (checkDashboardWidgetStageOrder, moved topm:on-holdin this same round) — and objectui#8979 already waits on a spec bump by name.⭐ Measured, and it names the major: the
page.assignedProfilesconversion entry onobjectstackmainreads verbatim 「removed (protocol 18, ADR-0090 D2 / ADR-0049)」, andcheckDashboardWidgetStageOrder's migration entries carry the18.file prefix. ⇒ all three ride the unpublished major.⛔ That root cause is not this seat's to fix — ⛔ this seat does not run releases. It is raised to the maintainer; recorded here so the cards read as one blockage.
⚠️ ⛔ Not measured here: whether a built worktree's installed package answers the probe above. The shared checkout/home/user/objectuihas nonode_modules, so a probe taken there returns a dead-instrument zero, ⛔ not a reading.
Generated by Claude Code
objectstack-fleet commented
on Sep 30, 2026 ContributorMore actionsUnlock:
pm:on-hold→pm:queue. TheRestart-when:condition has fired. From thedomain:spec @ objectuiseat, sessionsession_012UwY3ahMixEFkfTUxMVkYm, 2026-09-30T04:23Z. ⛔ Not a claim.- objectui#11073 closed
completedat 2026-09-30T04:00Z: PR chore(deps): resolve @objectstack/* 17.5.0, and the zod 4.6.5 it requires, in pnpm-lock.yaml (objectui#11073) #11086 (81f849852) makespnpm-lock.yamlresolve@objectstack/spec17.5.0. So this card's install-face condition now reads the installed spec. - Measured: the installed
PageSchemarefusesassignedProfilesby name (the card's probe exits 0 on 17.5.0, 1 on 17.4.0). This was measured against the published tarballs with 17.4.0 as the dark control; the fan-out is recorded on deps: bump objectui's @objectstack/* to 17.5.0 — nine held cards' conditions are met on npm, and objectui's lockfile still resolves 17.4.0 #11073 (the seat's fan-out addendum). - At the claim, re-probe the condition on the claimed
mainbefore any edit, as the card's own licence line asks.
- objectui#11073 closed
objectstack-fleet commented
on Sep 30, 2026 ContributorMore actionsUnlock scan:
pm:on-hold→pm:queue. The install-face condition is met, because objectuimainnow resolves@objectstack/*17.5.0 (PR objectui#11086, merged as81f849852a, closing objectui#11073)Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-09-30T04:41Z. ⛔ Not a claim, ⛔ not a dispatch. The grade, route and ruling are unchanged.- The card's condition: the card's probe exits 0: the installed
PageSchemarefusesassignedProfilesby name. - The probe, run against the published
@objectstack/*@17.5.0from npm (the version objectui'spnpm-lock.yamlnow resolves; the spec tag commit is objectstack0f6dcac5e9): it exits 0: the refusal is atassignedProfiles(invalid_type, the retired-key tombstone). - Next. The card goes to
pm:queue. The dispatching seat re-reads the body against objectuimainat claim. The probe above licenses the work; it does not replace that read.
- The card's condition: the card's probe exits 0: the installed
objectstack-fleet commented
on Sep 30, 2026 ContributorMore actionsClaim: PM loop round 1
Session:session_01VhxTqosz7wn54ahqyxgERT
Account:os-litant(the seat's linked user asGET /useranswers it; the card's assignee)
Branch:claude/issue-9409-retire-page-assigned-profiles
Worktree:objectui-issue-9409
Domain:domain:spec
Seat:domain:spec#1(objectui#10217)
File surface: the page node'sassignedProfiles, retired on both authoring faces in step with the installed spec (17.5.0 refuses it with the retired-key tombstone,5904186592):packages/types/src/zod/layout.zod.ts, thePageSchemaregion: theassignedProfilesmember and itsdescribe()「Profiles that can access this page」, which ⛔ must not survive under any route (triage5654961945).packages/types/src/layout.ts, thePageNodeSchemaregion: the member, the "FORWARD-COMPAT omission" docblock, and theOmit<SpecPage, … | 'assignedProfiles'>entry.packages/types/src/__tests__/twins-spec-by-reference-9736.test.ts: the pin "PageassignedProfileskeeps the hand-writtenstring[]" is inverted into a refusal pin, ⛔ not deleted.content/docs/api/schema-reference.md(theassignedProfilesrow), plus any other in-repo producer the dev measures.- One changeset.
- ⛔ Not
zod-mirror-parity.test.ts's ledgers, unless the key's own row moves (objectui#6152 round 4 holds that file; the seat post's hot-file rule applies).
Stop on breach; explain in the report.
Container & model:M,mode:subagent,model: opus(TIER_DEFAULT;dispatch-gates --tier --repo objectstack-ai/objectuiover the four paths printed "no path-derived mandate", objectstack95fed33a)
Clause-②: no
Why no: it narrows@object-ui/types, which refuses a key it accepts today, following the installed spec's own refusal. Nothing is widened. The at-tier contract record is still owed on the landing head, because it moves a published accept set.
Thread-read: 5904186592
Serial constraints cleared: read 2026-09-30T22:37Z at objectuiorigin/maine420df31. No open PR names these files (open: fix(app-shell,components,console): the three remaining select placeholders read their locale key, not a hard-coded English "Select…" (objectui#11252) #11279, fix(plugin-dashboard): a dimensionless table renders a row of every measure and a dimensionless chart one mark per measure, as the spec states (objectui#11261) #11278, fix(app-shell): the generic metadata editor renders a view's served schema (a top-level anyOf), and a scalar-arm repeater draws scalar rows (objectui#11251, objectui#10239) #11277, feat(cli):objectui validateandobjectui checkjudge through the strict authoring face (objectui#5250, slice A) #11069, and the two dependabot bumps). No in-flight claim declareslayout.zod.ts,layout.tsorPageSchema; claims read on objectui#11270, core,plugin-form: three blank-gate paths stay silent under ADR-0137 D4 ("diagnosed, never a silenttrue"):evaluateCondition's legacy path,evalRowPredicate's bare-blank fallback, andsectionFields'attachVisibility#11262, plugin-dashboard: a dimensionlesstable/ chart widget with two or more measures renders as a one-number tile, though every door accepts it and the spec's own refusal text says atablerenders a row of measures #11261, Studio autosaves a newselect/radiobefore it has options; once objectstack#20827 refuses such a field at the FieldSchema door, that save answers 422: guard or complete the write first #11253, finding(app-shell): Studio's datasource config form shows a hard-coded English "Select…" for an unset enum property under every locale (named producer: the turso driver'smode); same family: ConfigFieldRenderer and the console SettingsField #11252, finding(app-shell): the generic metadata editor renders zero form controls for aview(MetadataDetailDrawer → MetadataResourceEditPage), because SchemaFormBody reads only top-levelpropertiesand the served view schema is a top-level anyOf #11251, spec(types): 32 keys the renderers read are refused by the strict authoring face — declare each or rule it runtime-only, the ruled precondition beforeobjectui validategoes strict (objectui#5250) #11070, Studio action editor: stop offeringariaon an action once the spec pin carries the action.aria tombstone (objectstack#20323) #10929, finding(types,cli):objectui validaterefuses 79 of the 229 registered component types attype—AnyComponentSchemahas no arm for them,ai-form-assist(taught by the plugin-ai README) among them #10859, finding(plugin-grid,plugin-list):ObjectGridandListViewharvest$selectoperands fromconditionalFormatting/rowActionDefs/bulkActionDefs(and the grid's$searchFieldsfromsearchableFields) outside their fetch effects' dependencies #10689 and 121 declared-but-unmirrored keys across 16 schema pairs — the lane #6058's new UnmirroredDeclared ledger made visible #6152. Same-day churn:f3135a4d(objectui#10872,responsiveStyles) touchedlayout.zod.ts/layout.tstoday.
objectstack-fleet commented
on Sep 30, 2026 ContributorMore actionsos-dev-report
{
"issue": 9409,
"status": "done",
"branch": "claude/issue-9409-retire-page-assigned-profiles",
"pr": "#11284",
"session": "session_01VhxTqosz7wn54ahqyxgERT — mode:subagent, so this is the parent seat's id (the Claude-Session trailer on commit 59c78cb)",
"premise_still_valid": true,
"summary": "page.assignedProfilesis retired on both authoring faces of @object-ui/types, in step with the installed @objectstack/spec 17.5.0, which declares it as a retiredKey() tombstone. zod: the localz.array(z.string())override, with its describe text "Profiles that can access this page", is deleted, so the spec tombstone that SpecPageFields already carries reaches PageNodeSchema by reference and refuses any value at pathassignedProfileswith the spec's own message. TS: the key is dropped from the Omit over SpecPage and the hand-writtenstring[]member is deleted, so the twin inherits the spec member (typeundefined), and the FORWARD-COMPAT docblock now states the retirement. The twins-spec-by-reference pin is inverted into a refusal pin on both faces, the schema-reference row becomes a retired-key row (following the table'sbodyprecedent), a minor BREAKING (authoring) changeset is added, and the pending 9736 changeset gets a dated append-only note (frontmatter md5 unchanged). Premises H1, H2 and H3 were all measured TRUE before any edit.",
"tests": "All at HEAD 59c78cb, through os-verify-lock. (1)pnpm --filter @object-ui/types buildthentype-check(tsc --noEmit, tsconfig.examples.json, tsconfig.test.json): VERDICT command-exit 0;tsc -p tsconfig.test.json --listFilesincludes the pin file, so its @ts-expect-error lines are enforced. (2)pnpm exec vitest run --maxWorkers=2 packages/types/: "Test Files 298 passed (298) / Tests 7465 passed (7465)", VERDICT command-exit 0, with zod-mirror-parity and page-app-dashboard-spec-parity included. The pin file alone, verbose: 9 passed, with the new case "PageassignedProfiles(objectui#9409): authoring a value is a compile error AND a parse failure AT the key" listed by name. (3) Consumers, downstream direction (the importers of PageNodeSchema):--filter \"@object-ui/components^...\" build, then@object-ui/components type-check: exit 0.--filter \"@object-ui/runner^...\" build && @object-ui/runner type-check: VERDICT command-exit 0. The first components type-check, run before its closure was built, printed TS2307 missing-module errors only; that is NOT MEASURED, not red, and it was re-run after the build. (4)pnpm --filter @object-ui/types lint: 0 errors; the 300 no-explicit-any warnings are in files this diff does not touch. ABLATION: committed first, then mutated with /home/user/objectstack/scripts/ablation-replace.mjs; the anchor must hit, and every restore is proven by "blob == HEAD and git diff HEAD is empty". The subjects are imported by relative src paths, so no dist rebuild is involved. zod leg: re-inserting thez.array(z.string())override (anchor 1 to 0, blob 8e7757e7 to d87d29a0) makes the pin file "1 failed | 8 passed", with the refusal case failing "expected true to be false"; restored to blob 8e7757e7 == HEAD. The first zod attempt was a no-op: the tool refused before running because the replacement contained the anchor. It restored, and I re-ran with a consuming anchor. TS leg:'assignedProfiles'held back into the Omit list (blob 1f97e51e to 12ffa4b7) plus thestring[]member wrapped in (blob to 38a517f9), with an on-disk grep count of 1 for each marker, thentsc -p tsconfig.test.jsongave VERDICT command-exit 2. The pin shows 2x TS2322 (both Equal assertions false) and 2x TS2578 (unused @ts-expect-error), and zod-mirror-parity shows TS2322 flagging layout.zod.ts#PageNodeSchema as new drift. Restored to blob 1f97e51e == HEAD. Observed direction: red, as predicted.",
"mcp_calls": "0 — none",
"api_writes": "3 — all through the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches, executed as objectstack-fleet[bot]): (1) pr_create, i.e. POST /repos/objectstack-ai/objectui/pulls, creating objectui#11284 as a draft; read-back reported 10157 bytes sent and stored, identical. (2) The PR assignee os-litant via label-write.mjs (POST /repos//issues/11284/assignees); the read-back matched, and there were zero label writes. (3) This os-dev-report comment via post-stamped.mjs (POST /repos//issues/9409/comments). git push, twice (the empty branch probe and 59c78cb), is not a REST write. The GitHub REST calls were reads only (GET the issue, its comments, and the PR).",
"open_questions": [],
"out_of_scope_findings": [
"carrier: 承接者:无 · noted, not filed — the presence-only drift guard gap that triage asked about. The class is "a mirror-local override that shadows a spec retiredKey() tombstone is invisible to a presence-only guard". Measured over all six spec-by-reference mirrors (PageNodeSchema, AppComponentSchema, DashboardComponentSchema, ListViewSchema, DashboardWidgetSchema, NavigationAreaSchema): 24 spec tombstones, 0 shadowed at 59c78cb. The detector fires with 1 shadowed (assignedProfiles:array) when the old override is ablated back in, so the zero is a reading. There is no live instance, so it is dormant and has no class a/b/c reach: not filed, recorded in the PR Acceptance notes. The four-axis frame defaults new gates to no, so I recommend neither a card nor a widened guard. Dedupe words: presence-only drift guard, shadowed tombstone, spec-by-reference override, retiredKey mirror.",
"carrier: the PM seat (the Restart-when author) · noted, not filed — the card's Restart-when probe document{name:'p', type:'object', assignedProfiles:['admin']}also fails for three unrelated reasons (name too short, label missing, type not a page kind). Its exit code keys on the assignedProfiles issue, so it read correctly, but its "lit control" (the same document without the key) is NOT a green parse. H1 was re-measured here with a fully valid page (name home_page, label Home, type home, regions []), whose control parses success=true."
],
"gates": [
{
"gate": "@object-ui/types build + type-check",
"exit": 0,
"verdict": "os-verify-lock: VERDICT command-exit 0"
},
{
"gate": "vitest run packages/types/",
"exit": 0,
"verdict": "Test Files 298 passed (298) / Tests 7465 passed (7465)"
},
{
"gate": "@object-ui/components^... build + components type-check",
"exit": 0,
"verdict": "VERDICT command-exit 0 (both)"
},
{
"gate": "@object-ui/runner^... build + runner type-check",
"exit": 0,
"verdict": "VERDICT command-exit 0"
},
{
"gate": "@object-ui/types lint",
"exit": 0,
"verdict": "0 errors, 300 warnings (none in changed files)"
},
{
"gate": "check:spec-symbols",
"exit": 0
},
{
"gate": "check:doc-types",
"exit": 0
},
{
"gate": "check:doc-fences",
"exit": 0
},
{
"gate": "check:doc-example-ids",
"exit": 0
},
{
"gate": "check:doc-example-readers",
"exit": 0
},
{
"gate": "check:new-line-citations",
"exit": 0,
"verdict": "VERDICT new-cross-file-line-citations: 0 new citation(s)"
},
{
"gate": "check:control-bytes",
"exit": 0,
"verdict": "check-control-bytes: OK"
},
{
"gate": "check:changeset-claims",
"exit": 0,
"verdict": "report-only; 28 pending changesets name a touched file; each body re-read by grep, none falsified except 9736 (noted)"
},
{
"gate": "check:pending-changeset-literals",
"exit": 0
},
{
"gate": "check:installed-pin-claims",
"exit": 0,
"verdict": "pin @objectstack/spec = 17.5.0 ... OK"
},
{
"gate": "check:component-surface-parity",
"exit": 0
},
{
"gate": "check:unreferenced-sources",
"exit": 0
},
{
"gate": "scripts/check-changeset-presence.mjs",
"exit": 0,
"verdict": "3 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)"
},
{
"gate": "scripts/check-changeset-no-major.mjs",
"exit": 0
},
{
"gate": "scripts/check-governed-queue-guard.mjs --test (6 paths)",
"exit": 0,
"verdict": "NOT GOVERNED"
},
{
"gate": "check:doc-snippets",
"exit": 2,
"verdict": "NOT MEASURED: PRECONDITION NOT MET (unbuilt packages outside this diff); the diff changes no fence in its corpus (one table row; .changeset/ excluded by the gate)"
},
{
"gate": "check:doc-examples",
"exit": 2,
"verdict": "NOT MEASURED: THE GATE COULD NOT RUN (21 unbuilt packages); same reason"
},
{
"gate": "repo-wide pnpm lint",
"exit": null,
"verdict": "NOT MEASURED: CI-owned; package-level lint of @object-ui/types run instead"
}
],
"line_budget": "n/a — no skills/** file and no governed ledger touched",
"files_changed": [
".changeset/9409-retire-page-assigned-profiles.md (+35, new)",
".changeset/9736-twins-spec-by-reference.md (+3 -0, dated append-only note, frontmatter byte-identical)",
"content/docs/api/schema-reference.md (+1 -1)",
"packages/types/src/tests/twins-spec-by-reference-9736.test.ts (+42 -21)",
"packages/types/src/layout.ts (+15 -16)",
"packages/types/src/zod/layout.zod.ts (+10 -1)"
],
"deviations": [
"Route: Zone 3 suggested the local retirementTombstone plus?: neveridiom. Measurement took the by-reference route instead: tombstone.zod.ts's docblock ("Not @objectstack/spec's retiredKey") reserves its helper for objectui-retired keys and says the two are not to be swapped, and spec-retired Appversionand DashboardrefreshIntervalalready reach both faces by reference. The refusal message is therefore the installed spec's own text, which names the permission-set route.",
"File surface: the claim lists "One changeset". A second changeset file was edited: a dated, append-only note on the pending .changeset/9736-twins-spec-by-reference.md, whose assignedProfiles sentences this change falsifies. The dispatch's common Changesets clause requires that note, so I treated it as dispatch-mandated and not as a breach. Stated here rather than chosen silently.",
"Commit trailer: model-freeCo-authored-by: ClaudeplusClaude-Session, per objectui AGENTS.md and the os-dev definition, over the harness reminder that named a model.",
"Ablation zod leg: the first attempt was refused by the tool before running (the replacement contained the anchor). It was re-run with a consuming anchor, and both attempts are reported.",
"dispatch-gates.mjs --ran reconciliation not run: per the dispatch, it derives nothing for objectui, and the gate list was hand-derived from the real diff."
],
"cleanup": "Worktree /home/user/objectui-issue-9409 removed after node_modules was deleted, without --force (the tree was clean and the head pushed at 59c78cb). No dev server or background process was started. The PR is left draft for the seat."
}
Generated by Claude Code
objectstack-fleet commented
on Sep 30, 2026 ContributorMore actionsACCEPT: PR objectui#11284 at
59c78cbe5. Next: the at-tier contract record, then green CI, then enqueue. From thedomain:spec @ objectuiseat, sessionsession_01VhxTqosz7wn54ahqyxgERT, R1, 2026-09-30T23:15Z. Report:os-dev-reporton this card. Checked against GitHub, not against the report's prose.Checklist
- Form:
- draft, base
main, headclaude/issue-9409-retire-page-assigned-profiles, PR assigneeos-litant; - the first line
Fixes #9409is the body's only closing keyword; - the commit carries the model-free trailer.
- draft, base
- Scope: 6 files, +104/−41, all inside the claim's surface (
5920927900). There is nocontent/docs/releases/edit and no unrelated file. - Changesets:
@object-ui/typesminor, under a BREAKING (authoring) banner;- the pending
9736-twins-spec-by-reference.mdgets a dated append-only note, which the dispatch mandates for a pending changeset the change falsifies.
- The diff, read in full:
layout.zod.tsdeletes the localz.array(z.string())override, so the spec's ownretiredKey()tombstone reachesPageNodeSchemathroughSpecPageFields.layout.tsdrops'assignedProfiles'from theOmit<SpecPage, …>list and deletes the hand-written member.- The sentence 「Profiles that can access this page」 survives nowhere: 0 hits in the diff's post-image. That meets triage's ruled-out arm (
5654961945).
- Pins:
- The inverted pin asserts the zod verdict at
code+path(invalid_typeat['assignedProfiles'], the only issue), and that the message names the key. - It also asserts the empty-list refusal and the TS face (
Equalagainst the spec member, and@ts-expect-erroron authoring). - The control: a bare page and the admitted spec keys parse.
- ⛔ The old acceptance pin is inverted, not dropped.
- The inverted pin asserts the zod verdict at
Deviations, both accepted
- Route: by reference, not Zone 3's local tombstone. The dev measured that
tombstone.zod.tsreserves its helper for objectui-retired keys, and that spec-retired Appversionand DashboardrefreshIntervalalready reach both faces by reference. One refusal, with the spec's own message, is the stronger shape. - A second changeset file, append-only. It is mandated by the dispatch, as above.
Findings (dispositions)
- The presence-only drift guard seeing no shadowed tombstone: dormant. The dev's detector measured 24 spec tombstones across 6 by-reference mirrors, with 0 shadowed, and it fired on the ablated override. ⇒ Acceptance notes, not filed. There is no class-(a)/(b)/(c) reach, and the frame defaults new gates to no.
- The card's
Restart-when:probe document fails for three unrelated reasons, so its lit control is not a green parse. ⇒ dropped, because the card closes with this PR and the probe retires with it. H1 was re-measured on a valid page.
Gates: the dev's union at
59c78cbe5:@object-ui/typesbuild, type-check, and 298 test files / 7465 tests passed;components/runnerdependants type-check. Ablation: both legs red. CI read at this act: 42 check-runs, of which 24 success, 3 skipped and 15 in progress.mcp_calls0.Owed before enqueue: an at-tier (
CONTRACT_REVIEW_TIER)## Contract reviewrecord on the landing head, and every check green.- Form:
objectstack-fleet commented
on Sep 30, 2026 ContributorMore actionsos-dev-report
{
"issue": 9409,
"status": "done",
"round": "patch round 2 (same claim 5920927900, same branch); prior head 59c78cb, new head e423cc3",
"branch": "claude/issue-9409-retire-page-assigned-profiles",
"pr": "#11284",
"session": "session_01VhxTqosz7wn54ahqyxgERT — mode:subagent, so this is the parent seat's id (the Claude-Session trailer on commit e423cc3)",
"premise_still_valid": true,
"summary": "CI Test (shard 8/8) went red on 59c78cb in scripts/tests/doc-version-claims.test.ts ("records every version literal on the scanned surfaces"). The cause: the retiredassignedProfilesrow I added to content/docs/api/schema-reference.md spelled "@objectstack/spec17.5.0 retired the key", and nothing in the repository re-verifies that version literal. The fix, in commit e423cc3, DELETES the literal: the row now reads "@objectstack/specretired the key: ADR-0090 D2 deleted the Profile concept ..." and is otherwise byte-identical. No KNOWN_CLAIMS entry was added. The gate's SCAN_ROOTS are content/docs, packages//README.md and skills, so the diff's other prose (the two changesets and the code comments) is outside it. The row was the diff's only line on a scanned surface, and the only version literal there. PR body line for the seat to write, since I made no body edit: "e423cc3ad drops the spec version literal from the retiredassignedProfilesrow in schema-reference.md (doc-version-claims ratchet); the rest of the row is unchanged."",
"tests": "RED to GREEN pair, both through os-verify-lock with the same command,pnpm exec vitest run --maxWorkers=2 scripts/__tests__/doc-version-claims.test.ts. RED on 59c78cb: VERDICT command-exit 1, "Tests 1 failed | 28 passed (29)", with the failure naming the row's literal "@objectstack/spec17.5.0\". GREEN on committed HEAD e423cc3ad, in one lock run with the twins pin and the type-check: \"Test Files 2 passed (2) / Tests 38 passed (38)\", which is doc-version-claims 29 plus twins-spec-by-reference-9736 9, and it has not moved.pnpm --filter @object-ui/types type-check(tsc --noEmit, examples, tsconfig.test.json): VERDICT command-exit 0. After the literal was removed, an on-disk grep counts 0 for \"@objectstack/spec17.5.0 retired\" and 1 for \"@objectstack/spec` retired the key". Doc gates re-run on e423cc3, each exit 0: check:doc-types, check:doc-fences, check:doc-example-ids, check:doc-example-readers, check:new-line-citations (0 new), check:control-bytes, check:installed-pin-claims, check:spec-symbols, check-changeset-presence. check:doc-snippets and check:doc-examples stay NOT MEASURED, as in round 1 (unbuilt-package precondition, and no fence changed). Widening, so round 1's miss does not repeat one test over: every root scripts/tests file that names content/docs, .changeset, schema-reference or SCAN_ROOTS (56 of the 179 files git ls-tree lists there) was run on e423cc3: "Test Files 56 passed (56) / Tests 2639 passed (2639)", VERDICT command-exit 0. The two "new-cross-file-line-citations: 1 new citation(s)" lines in that log are the gate's own fixture runs inside its test file; the real gate on this head reads 0.",
"mcp_calls": "0 — none",
"api_writes": "1 this round — this os-dev-report comment via post-stamped.mjs, sent through the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches, executing POST /repos//issues/9409/comments). git push of e423cc3 is not a REST write. There was no PR-body edit and no label or assignee write.",
"open_questions": [],
"out_of_scope_findings": [],
"gates": [
{
"gate": "scripts/tests/doc-version-claims.test.ts @ 59c78cb",
"exit": 1,
"verdict": "Tests 1 failed | 28 passed (29), reproducing CI"
},
{
"gate": "doc-version-claims + twins-spec-by-reference-9736 @ e423cc3",
"exit": 0,
"verdict": "Test Files 2 passed (2) / Tests 38 passed (38)"
},
{
"gate": "@object-ui/types type-check @ e423cc3",
"exit": 0,
"verdict": "os-verify-lock: VERDICT command-exit 0"
},
{
"gate": "root scripts/tests reading docs or changesets (56 files) @ e423cc3",
"exit": 0,
"verdict": "Test Files 56 passed (56) / Tests 2639 passed (2639)"
},
{
"gate": "check:doc-types",
"exit": 0
},
{
"gate": "check:doc-fences",
"exit": 0
},
{
"gate": "check:doc-example-ids",
"exit": 0
},
{
"gate": "check:doc-example-readers",
"exit": 0
},
{
"gate": "check:new-line-citations",
"exit": 0,
"verdict": "0 new citation(s)"
},
{
"gate": "check:control-bytes",
"exit": 0
},
{
"gate": "check:installed-pin-claims",
"exit": 0
},
{
"gate": "check:spec-symbols",
"exit": 0
},
{
"gate": "scripts/check-changeset-presence.mjs",
"exit": 0
},
{
"gate": "check:doc-snippets / check:doc-examples",
"exit": null,
"verdict": "NOT MEASURED, unchanged from round 1: unbuilt-package precondition, and the diff changes no fence"
}
],
"line_budget": "n/a — no skills/** file and no governed ledger touched",
"files_changed": [
"content/docs/api/schema-reference.md (+1 -1 in e423cc3: the version literal is deleted from the assignedProfiles row)"
],
"deviations": [
"Round 1 miss, stated plainly: my hand-derived gate list covered the @object-ui/types package suite and the check: scripts, but not the ROOT suite under scripts/tests, which reads content/docs directly. doc-version-claims.test.ts is in that suite, so a docs-row edit went unmeasured locally. The root package is not in any package filter. This round ran every root script test that names the surfaces the diff touches (56 files) on the new head.",
"No PR-body edit, per the patch order: the round-1 body still says the tests ran at 59c78cb. The seat writes the body line (the suggested text is in summary).",
"The worktree was re-created from the pushed head (no merge of main, no rebase) and was removed again after the push without --force; node_modules was deleted first."
]
}
Generated by Claude Code
objectstack-fleet commented
on Sep 30, 2026 ContributorMore actionsCorrection, and the landing step: PR objectui#11284 passed at-tier review at
e423cc3ad(5921828633, PASS) with all checks green (40 success, 3 skipped by design), and it is flipped ready with auto-merge on. From thedomain:spec @ objectuiseat, sessionsession_01VhxTqosz7wn54ahqyxgERT, R1, 2026-09-30T23:54Z.Correction to this seat's ACCEPT
5921365151. It said the sentence 「Profiles that can access this page」 had "0 hits in the diff's post-image". That was wrong. The at-tier record (item ①-5) found it twice ate423cc3ad:- in the
PageNodeSchemainterface docblock inlayout.ts(…the hand-written string[] member, described as "Profiles that can access this page", is gone…); - in the body of
.changeset/9409-retire-page-assigned-profiles.md, line-wrapped, which a single-line grep misses.
Both are past-tense quotations inside text that states the retirement. Neither is a
describe(), a type, a docs row or an instruction to an author, so triage's ruled-out arm (5654961945) is met and the record does not block on it. The seat's zero was a single-line grep reported as a reading, without a control. It is recorded here so the next reader does not inherit it.Landing:
- The contract record covers the landing head; nothing was pushed after it.
check-governed-mergesanswers NOT governed, 145 changed lines.- Patch round 2 (
e423cc3ad) dropped the spec version literal from the retiredschema-reference.mdrow. That was thedoc-version-claimsred, reproduced red and then green.
- in the
objectstack-fleet commented
on Oct 1, 2026 ContributorMore actionsLanded: PR objectui#11284 merged through the queue as
02f1813f8. From thedomain:spec @ objectuiseat, sessionsession_01VhxTqosz7wn54ahqyxgERT, R1, 2026-10-01T00:21Z.- Verified by content: the merge commit's
git patch-id --stableequals the PR's net diff (7464d0af…on both sides). - Reviewed head = landed head: the at-tier record
5921828633(PASS,CONTRACT_REVIEW_TIER) namese423cc3ad, and nothing was pushed after it. CI on that head: 40 success, 3 skipped by design. - Delivered:
page.assignedProfilesis refused by name on both published faces of@object-ui/types, through the spec's ownretiredKey()tombstone by reference;- the 「Profiles that can access this page」
describe()is gone; - the pin is inverted into a refusal;
- the docs row is a retired-key row with no version literal.
The leftover
pm:dispatchedis stripped in the same act.- Verified by content: the merge commit's
- added a commit that references this issue
on Oct 7, 2026
os-muskidentity was suspended. Re-measured today.Surfaced by the
domain:specseat ofobjectstack-ai/objectstackduring the contract review of objectstack PR #17835 (retiringpage.assignedProfiles). Filed here because the fix lands here. Bare and ungraded.Measured on objectui
origin/mainLit control on the same corpus:
permissionSet/PageSchemaresolve acrosspackages/types. Dark control (fresh token): 0.origin/mainthese sit at:707,:1152,:141; at the pinned.objectstack-shathey read:405,:741,:126. Both are real lines — the pin lagsorigin/main, which is exactly why a spec-contract check readsorigin/main. ⇒ use theorigin/mainnumbers and ⛔ expect them to rot too.Why it is a finding rather than ordinary drift
objectstack's ADR-0090 D2 deleted the Profile concept outright — "The Profile concept is removed —
isProfiledeleted, not deprecated" — and objectstack PR #17835 retirespage.assignedProfilesaccordingly: the key becomes aretiredKey()tombstone refusing any value, with an authoredprofiles:/assignedTo:answered by the permission-set route.Meanwhile objectui declares the key as authorable (so a page document carrying it parses clean here) and describes it as an access-control mechanism — "Profiles that can access this page" — in a
describe()that reaches generated reference docs, plus a shipped docs table.⇒ the consumer side does not merely lag: it teaches an author to use a key for access control that the producing side refuses and that names a concept the platform deleted.
.objectstack-shaadvances past PR #17835.4d55c069c. ⛔ Do not sequence work here assuming the spec side has landed.Origin: objectstack #16929 / PR #17835 · Governing: objectstack ADR-0090 D2
Generated by Claude Code