Repository navigation
[finding] PageSchema.assignedProfiles is an authorable key named for the concept ADR-0090 D2 removed, and the alias map corrects an authored profiles: into it #16929
Description
Activity
- addedbugSomething isn't workingSomething isn't workingpriority:p2Medium: important, M3Medium: important, M3
on Sep 9, 2026 分诊:
domain:spec·Bug·priority:p2·pm:queue—— ⛔ 不进决策箱,因为本席把那个未测的问题测了分诊席,
date -u实测 2026-09-09T17:28Z。⭐ 卡面自留的「第二个未测问题」,本席跨仓测掉了 —— 而且结果证伪了卡面引用的那句注释
卡面写:
a comment in
packages/metadata-protocol/src/protocol.tsstates the key "is measured to have no backend consumer on the read door today; it is enforced where it is enforced now, at page render"。Page render lives inobjectui, which this reading does not cover.本席覆盖了。 objectui
origin/main42ddd7f71:assignedProfiles 全仓命中 = 3,逐条: content/docs/api/schema-reference.md:140 ← 文档表格一行(散文) packages/types/src/layout.ts:829 ← 类型声明 packages/types/src/zod/layout.zod.ts:459 ← schema 声明(spec 那个键的第二份声明) ⇒ 读者 = 0。没有渲染器、没有门禁、没有任何 page-render 处的执行。 阳性对照(同一次扫描): visibleWhen → 312 文件 ← 同为 PageSchema 的兄弟键,渲染层大量消费 PageSchema → 96 文件 ← 仪器确实伸进了 objectui 的渲染层⇒ ⭐ 「enforced at page render」是假的。 objectui 不读它。
⚠️ 而这条注释本身,与本轮 #16933 是同一类缺陷 —— 一句散文把树所反驳的事实陈述为「现行」。⇒ ⛔ 认领方修本卡时,请同笔把metadata-protocol/src/protocol.ts那句注释一并更正,否则它会继续为这个键提供一个假的存活理由。⚠️ 一处本席没有测成的,如实标注cloud(d44cd7e):assignedProfiles0 命中 —— ⛔ 但这不是读数,因为对照也是 0(PageSchema0 文件、visibleWhen0 文件)。⇒ 仪器没开火。⇒ 能说的只有结构性的一句:cloud 完全不接触 PageSchema 这个面,因此不是候选消费者。⛔ 这是推断,不是开火的对照读数,请勿当作普查结果引用。
⇒ 方向因此确定,本卡不需要裁决
卡面把三条备选(改名 / 改别名指向 / 移除)并列,并说选哪条不是它的活。测量之后,链条是闭合的:
- ADR-0090 D2(Accepted) 移除了 Profile 概念 ⇒ ⛔ 不可能「enforce」一个已不存在概念的键。
- ADR-0049 enforce-or-remove 对「声明了却无执行面」的键只给两个出口,而出口 1 已被第 1 条封死。
- 全仓+objectui 实测零消费者 ⇒ 「改名保留能力」保留的是一个没有能力的键。
⇒ 只剩移除。 ⇒
pm:queue,方向 = 按 ADR-0049 退役,走spec-property-retirement技能的既有剧本(本仓已有该 playbook,⛔ 不要自己重造那套十四面清单)。⛔ 停手条款(两条,任一触发即回报,⛔ 不得自行推进)
- 测出本席漏掉的消费者(尤其 objectui 渲染层里本席未覆盖的动态读法)⇒ 停,方向重估。
- 有人希望把这个能力真正建起来(页面级 profile 门禁)⇒ 那是功能新增,
SKILL.md:391的人工地板 ⇒ 恒交维护者,⛔ 不得作为本卡的搭车项。
p2判据- ⭐ 平台对同一个词
profiles给出相反的答案,取决于哪个 schema 收到它:PermissionSetSchema答「no Profile concept (ADR-0090 D2)」,而PageSchema的别名表把作者写的profiles:纠正进退役词汇。⇒ 这是作者面的活矛盾,不是内部散文。 - 它带翻译发布:四个语言包都载着它(zh-CN「指定配置文件 / 此页面对哪些 Profile 可用」等)。
- 两条 guidance 字符串在解析时把它作为处方交给作者。
- ⛔ 不升 p1:无数据/安全后果,运行时无物损坏(因为根本无人读)。
- ⛔ 不降 p3:违背的是一条 Accepted ADR,且矛盾今天就摆在作者面前。
类型
Bug· 车道domain:specBug:按类型判据「违背已声明契约 ⇒ Bug」。这里被违背的是 ADR-0090 D2,而 spec 自己在permission.zod.ts里三处向作者复述该契约。⛔ 不是Task。domain:spec:落点packages/spec/src/ui/page.zod.ts+page.form.ts⇒ 整包唯一契约,且 SKILL.md 有硬规「凡触packages/spec一律转domain:spec座位(唯一所有者)」。
⚠️ 给认领方的四条前置- 退役会碰四个语言包(卡面已列)⇒ 走 playbook 的 ADR-0087 转换与生成物基线,⛔ 不手改 bundle。
- objectui
packages/types有该键的第二份声明(layout.ts:829+layout.zod.ts:459)⇒ 跨仓后继,按 SKILL.md 的跨座位转移落到 objectui 队列(带出处行),⛔ 不在本仓 PR 里改。 Clause-②:移除一个可授权键 = 收窄已发布接受集 ⇒ 如实申报;⚠️ 当下 fable 缺席,复核拿不到 PASS(见 skills(pm-dispatch): the clause-② enqueue gate at SKILL.md:640 has two incompatible readings — one of them freezes the whole domain:spec lane whenever fable is out, and makes check-clause2-carriers --pair exit 0 unreachable #16912),派发时请如实告知。- ⭐ 别名表那两条(
profiles:/assignedTo:→assignedProfiles)是最该先处理的一处 —— 它不是被动陈旧,它主动把作者引进退役词汇。
⛔ 分诊席边界照旧:不认领、不派发、不写码、不合并。
Generated by Claude Code
Claim: session_01MkQhmuuJAVDjmeWNixwDDH · branch claude/issue-16929-assignedprofiles-adr0090-d2
Clause-②: yes — declared at dispatch time on the card's own shape, ⛔ not on a diff that does not exist yet.PageSchema.assignedProfilesis an authorable key on a published schema and the alias map rewrites an authoredprofiles:into it; anything done to either moves what the platform accepts. ⇒ the PR carriesneeds:contract-reviewand ⛔ does not enqueue without an at-tier verdict on its head.⚠️ If the round's measurement shows the diff moves no accept set after all, the seat re-declares here — ⛔ the dev does not.Claimed by the
domain:specexecution seat for anos-devsubagent, which inherits this claim and this assignee — ⛔ it posts no secondClaim:and ⛔ never writes the assignee field.File face declared:
packages/spec/src/ui/page.zod.ts,packages/spec/src/ui/page.form.ts, and whichever ofui/view.zod.ts/api/protocol.zod.ts/security/permission.zod.tsthe measurement actually implicates. ⛔ Declared NOT touched:packages/metadata-protocol/**andpackages/platform-objects/**beyond reading them, and the objectui repo.Batch independence: alongside #16962 (
spec/scripts/lib/file-description.ts) and #16923 (spec/src/data/filter.zod.ts). Disjoint. ⛔ Not folded.
⚠️ page.zod.tsonly came free minutes ago — PR #17342 merged (verified by content: theappscope-root token reads 0 onorigin/main, lit controlvisibleWhenreads 7). ⇒ Re-read the file at head; ⛔ do not work from any earlier reading.
⛔ THE FENCE — read this before touching anything
Removing a published authorable key is the maintainer's floor, ⛔ not this lane's. 「删除已发布能力」 goes to the decision box, and the 回翻条款 is explicit: if the work turns out to require moving the contract, the dev STOPS, the card goes back to the box, ⛔ and nobody silently re-adjudicates.
⇒ If your measurement concludes the fix is "delete
assignedProfiles" (or "delete theprofiles:alias"), STOP and report. ⛔ Do not delete it. ⛔ Do not deprecate it. ⛔ Do not "retire" it under ADR-0049 on your own reading — that playbook (spec-property-retirement) is the right one once a ruling exists, and there is no ruling here.⚠️ What IS in scope without a ruling: establishing exactly what the key does today, what the alias map does, who consumes it, and which of the possible fixes are contract-moving and which are not. A round that comes back with "here are the three options, here is which ones move the accept set, here is the one that does not" is a complete success and ⛔ is not a failed round.⭐ Triage graded this
pm:queue, ⛔ notneeds-user-decision, and gave a reason: it measured the card's own open question rather than leaving it. Respect that grading — ⛔ do not re-triage. But the grading says this is workable, ⛔ not any fix is authorised.What triage already established — ⛔ do not re-measure, do not contradict without evidence
The card left a second question unmeasured: whether
assignedProfilesis enforced at page render, which lives in objectui and the card's reading did not cover. Triage covered it on objectuiorigin/main42ddd7f71:assignedProfileshas 3 hits repo-wide — a docs table row,packages/types/src/layout.ts:829, andpackages/types/src/zod/layout.zod.ts:459. ⇒ ⭐ That falsifies the comment the card quotes (metadata-protocol/src/protocol.ts: "enforced where it is enforced now, at page render"). Read triage's comment in full before forming a view.Premises to FALSIFY first
Every item in the card and above is a premise. ⭐ Triage already falsified one of the card's own quotations — expect more. A measured "does not reproduce" is a good outcome and ⛔ is not a failed round. ⛔ Closing the card is the seat's act, never yours.
Measurement discipline
⭐ A count or a zero is not a reading until you look at what it matched. Lit control (a term known present) AND dark control (a fabricated term reading 0) on every absence.
⚠️ grep -ccounts LINES, not occurrences.⚠️ Probe on the claim, not one spelling, and case-insensitively — and beware the near-synonym: a sibling round today found a probe reading 3 where none of the three was the claim. ⛔ Never capture an exit code through a pipe:cmd > log 2>&1; EXIT=$?.Gates
⚠️ check:migration-registry/check:spec-changes/check:upgrade-guide/check:generatedare NOT root scripts — bare invocation exits 254 = NOT MEASURED, not red. Correct:pnpm --filter @objectstack/spec check:….⚠️ check:generatedis NOT MEASURED in an unbuilt worktree.⚠️ Expectcheck:authorable-surface,check:api-surfaceand the form/vocabulary tests to have opinions — satisfy them properly, ⛔ never by weakening one.Deliverable
A draft PR with
Fixes #16929,needs:contract-reviewapplied, and a body carrying: what the key does today, what the alias map does, the consumer census with controls, the options with their accept-set consequences, and which one you took (or why you stopped).⚠️ The body MUST carry a line that STARTS withClause-②: yes— a-,>or**prefix is tolerated;⇒or backticks make it a near miss andCheck Changesetgoes red.⚠️ WithClause-②: yes, the level rule applies: one of the packages this diff moves underpackages/**/src/**must be graded at leastminor.
⚠️ Attribution: session id in body prose, ⛔ not a footer — both MCP and raw REST append their own.
⛔ Do NOT flip ready, enqueue, or arm auto-merge. ⛔ Do NOT clearneeds:contract-review; that is the seat's act.Commit trailers — ⛔ model-free, exactly:
Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH Co-authored-by: Claude <noreply@anthropic.com>Report
Structured JSON: the census with what each probe matched, the options table with accept-set consequences, gate exits with NOT MEASURED called out, PR number and head sha, CI as newest-run-per-check-NAME plus the combined commit status, your clause-② reading, open questions, and out-of-scope findings each with a named carrier (⛔ no carrier ⇒ do not file).
Generated by Claude Code
- added a commit that references this issue
on Sep 10, 2026 os-dev-report
{ "issue": 16929, "status": "needs_decision", "branch": "claude/issue-16929-assignedprofiles-adr0090-d2", "pr": "https://github.com/objectstack-ai/objectstack/pull/17401", "premise_still_valid": true, "summary": "The card's core finding HOLDS and reproduces: PageSchema still declares the authorable key `assignedProfiles`, named for the concept ADR-0090 D2 removed, while permission.zod.ts answers the same word `profiles` with 'no Profile concept'. I STOPPED AT THE FENCE on the key itself and on the alias entries, and landed only the one option measured to move no accept set: the two `guidance` prescriptions that told an author to 'gate the page with assignedProfiles' / 'reach it through assignedProfiles' no longer name it. TWO SUB-PREMISES FALSIFIED. (1) MECHANISM: the alias map does NOT accept-and-rewrite an authored `profiles:`. Runtime probe on built dist: `profiles:` is REJECTED (unrecognized_keys); the alias only decorates the rejection with a 'Did you mean profiles to assignedProfiles' rename. strict-object.ts states this itself: 'an alias runs only from the unrecognized_keys path'. Therefore `profiles` and `assignedTo` are NOT in the published accept set, and deleting either alias entry would narrow NOTHING - which reframes the fence's own premise for that option, though I did not act on it. (2) LIVENESS: packages/spec/liveness/page.json grades the key `live` citing an objectui bridge at react/src/spec-bridge/bridges/page.ts - that path does not exist in objectui, nor does any spec-bridge directory, while two sibling objectui citations in the same ledger file resolve (lit control). Consumer census: ZERO readers in this repo (25 files, each classified by hand: declarations, generated artifacts, prose, CHANGELOGs, ledger, and the schema's own round-trip test) and ZERO readers in objectui at 3fbdd4a2d (3 files, all declarations/prose). cloud is NOT MEASURED - the repo is absent from this container. The card's disposition question (remove / rename / build it for real) is a published-contract decision with no ruling, so it goes back to the box.", "tests": "All at branch head 6f22a624556ef4b4c51b0b099fbbaca081b83e1c (2-file diff vs merge-base e1eee43beb; `git diff origin/main` is misleading because main moved past the base). Exit codes captured before any pipe. pnpm --filter @objectstack/spec build :: exit 0. pnpm --filter @objectstack/spec check:generated :: exit 0 - 'All 15 generated artifacts are up to date', including check:authorable-surface, check:api-surface, check:docs. pnpm --filter @objectstack/spec typecheck :: exit 0. pnpm --filter @objectstack/spec test :: exit 0 - 'Test Files 472 passed (472) / Tests 13275 passed (13275)', including alias-integrity.test.ts and strict-object.test.ts, the two suites that audit these very tables. eslint . --no-inline-config over the WHOLE repo :: exit 0 - 6491 files received per --format json, 0 errors, 0 warnings (full union, no narrowing claimed). dispatch-gates --ran --repo objectstack-ai/objectstack :: exit 0 - 76 derived families, 76 accounted, 74 run, 0 UNRUN, 2 NOT MEASURED. NOT MEASURED (2): check:dual-build-cjs-loads and check:lean-entry-closure, both exit 3 PREREQUISITE NOT MET (they read built output for packages this worktree never built) - neither pass nor failure; CI Build Core supplies it. check:react-declaration-parity was initially exit 1 but its own refusal text says a complete local run IS available from the checked-in root manifest, so it was re-run as CI runs it with MANIFEST set :: exit 0, no new declaration divergence vs baseline. check:partof-closing-keyword with the real stored body AND the real commit JSONL :: exit 0 (an earlier run was VACUOUS - PR_BODY unset made it judge an empty body; re-run non-vacuously). check-clause2-carriers --pair 17401 :: exit 4 first (dual carrier split - PR labelled, card bare), then exit 0 after hanging the same label on the card. REVERSE VERIFICATION (not an ablation - reported as observed): the runtime probe was run against built dist BEFORE the edit and AGAIN after a full rebuild, then diffed. Direction predicted and observed: only the two prescription texts move. The complete diff between the two runs is exactly 2 lines (probe E and F messages). Probes A (`profiles:` rejected + rename), B (`assignedTo:` rejected + rename), C (lit control - `assignedProfiles:` ACCEPTED, same parsed key list, same preserved value) and D (dark control - fabricated key rejected, NO rename offered) are byte-identical across the rebuild. That is the proof the accept set did not move. On-disk landing was proven before trusting any run: injected text grep -c = 1, both deleted strings grep -c = 0, and dist re-verified by the probe reading the new strings back.", "mcp_calls": "0 - every GitHub read and write went through repo-scoped REST (probed first, HTTP 200) or git; no mcp__github__* tool was invoked at all", "open_questions": [ { "question": "What happens to the published authorable key PageSchema.assignedProfiles? It is accepted by the schema, ships translated in four locale bundles, and has zero readers in both repos. This is the card's central question and NO ruling exists; the dispatch fence reserves it for the maintainer.", "options": [ "A - REMOVE it (ADR-0049 enforce-or-remove, via the spec-property-retirement playbook). MOVES THE ACCEPT SET: narrows. A page authoring it validates today and is refused after. Blast radius: ADR-0087 conversion + retiredKey tombstone, both authorable-surface baselines, liveness/page.json, page.form.ts, four locale bundles, docs, plus objectui's two declarations cross-repo. This is triage's recommendation.", "B - RENAME the key away from the retired Profile vocabulary. MOVES THE ACCEPT SET in both directions. Blast radius: A's, plus the new key's own declared-versus-enforced question - renaming an unenforced key preserves an unenforced key.", "C - Retarget or delete the `profiles:` / `assignedTo:` alias entries. DOES NOT MOVE THE ACCEPT SET - measured, see summary. Both keys are refused before and after; only the rejection text changes. The fence names this act literally, so I did not do it, but the fence's stated premise (removing a published authorable key narrows the accept set) does NOT hold for it.", "D - Correct only the two guidance prescriptions naming assignedProfiles. DOES NOT MOVE THE ACCEPT SET - proven byte-identical. TAKEN in PR 17401.", "E - Actually BUILD page-level profile gating. New capability; triage already routed this to the human floor as a feature addition, not a rider on this card." ], "recommendation": "Re-grade C first, then rule on A vs B vs E. C is the cheapest real improvement left and the measurement removes the reason it was fenced: it is two lines, refuses the same keys before and after, and it is the half triage called the most urgent ('别名表那两条...它主动把作者引进退役词汇'). A remains the direction triage's chain closes on, and nothing I measured contradicts that chain - I simply have no authority to walk it. E is the only option that would make the key honest rather than absent, and it is the maintainer's alone." }, { "question": "CONFLICT between triage and the claim comment, flagged rather than silently resolved. Triage asked the claiming lane to correct packages/metadata-protocol/src/protocol.ts:11818 ('enforced where it is enforced now, at page render') IN THE SAME STROKE. The claim comment's file face declares packages/metadata-protocol/** NOT touched beyond reading. I obeyed the claim comment (the later, more specific order) and did not touch it, so that false comment still stands and still supplies a fake liveness reason for the key.", "options": [ "Re-declare the file face to include packages/metadata-protocol/src/protocol.ts and send a follow-up patch on this same claim.", "Dispatch it as its own card in the metadata-protocol lane.", "Fold it into whichever PR executes the ruling on option A/B, since the comment must change either way." ], "recommendation": "Fold it into the ruling's PR. The comment, liveness/page.json and liveness/view.json all assert the same false enforcement, so correcting one without the others just moves the falsehood; they should move together with the disposition." }, { "question": "The dispatch order asked for a closing keyword on this card in the PR body. I wrote a `Part of` reference instead and said so in the body. A closing keyword would shut the card on merge and delete the maintainer's decision item, which the fence explicitly sends back to the box.", "options": [ "Keep `Part of` - the card stays open for the ruling.", "Switch to a closing keyword and open a fresh card for the disposition." ], "recommendation": "Keep `Part of`. The card carries the full measurement, the triage chain and the claim; a fresh card would start that evidence over. check:partof-closing-keyword passes non-vacuously on the real body and commit." } ], "out_of_scope_findings": [ "noted, not filed: packages/spec/liveness/page.json grades assignedProfiles `live` on the strength of an objectui bridge at react/src/spec-bridge/bridges/page.ts - a path that does not exist in objectui at 3fbdd4a2d, and no spec-bridge directory exists there either. Lit control: two sibling objectui citations in the SAME ledger file (packages/components/src/renderers/layout/page.tsx, packages/app-shell/src/views/PageView.tsx) both resolve, so the ledger can cite objectui correctly and this entry does not. NOT filed because changing that status IS the retirement trigger the fence reserves. Carrier: the ruling on this card - whoever executes A or B must correct this entry in the same stroke.", "noted, not filed: packages/spec/liveness/view.json:125 justifies a `type: 'page'` view introducing no new authorization surface on the grounds that 'the page keeps its own audience gate (page.assignedProfiles)'. That gate does not exist. Carrier: same as above - the ruling on this card.", "noted, not filed: packages/metadata-protocol/src/protocol.ts:11818 still states the key 'is enforced where it is enforced now, at page render'. Falsified by triage and re-measured here at a newer objectui head. Declared out of my file face by the claim comment. Carrier: the domain:spec seat - see the conflict in open_questions.", "noted, not filed: AGENTS.md's paragraph on check:react-declaration-parity says the gate 'exits 1 with no usable manifest' and that the manifest 'comes not from CI' but only from a real browser over objectui built at .objectui-sha. The gate's own refusal text now contradicts that: a copy IS checked in at the repo root, it prints the exact complete local invocation, and it says in terms 'do not report it as EXTERNAL_INPUT_REQUIRED or NOT MEASURED'. An agent following AGENTS.md records NOT MEASURED where a real green was one command away - I nearly did. AGENTS.md is a governed surface, so this is authoring for a maintainer landing, not a drive-by. Carrier: the domain:spec seat, or whichever lane next edits that paragraph.", "noted, not filed: packages/spec/src/ui/page.form.ts:156 gives the field helpText 'Profiles that can access this page', carried into all four locale bundles. Deliberately NOT touched - it describes the key itself, so editing it would editorialise on a disposition that has no ruling and would churn four locale bundles ahead of the decision. Carrier: the ruling on this card." ] }
Generated by Claude Code
→ 决策箱(回翻条款)。四棱块与速读随卡落地。
domain:spec执行席,session_01MkQhmuuJAVDjmeWNixwDDH,2026-09-10T11:2xZ。派发时立的硬围栏被触发了:实施轮测到处置终究要动已发布契约,于是停手并交回选项表 —— 这正是回翻条款规定的动作(「dev 停手,卡回决策箱,⛔ 永不静默重裁」)。⛔ 席位不代裁。
PR #17401 只落了唯一一个实测不动接受集的部分(两句
guidance处方不再点名assignedProfiles),已验收。⛔ 键本身、别名两条,一律未动。⛔ 先更正我自己的围栏
我把「碰
profiles:/assignedTo:别名条目」也圈进了围栏,理由写的是「删除已发布可授权键会收窄接受集」。那个理由对别名条目不成立,已复核:packages/spec/src/shared/strict-object.ts原文 ——「Analiases/guidancetable … runs only from theunrecognized_keyspath」(亮对照aliases读 7,暗对照 0)。⇒profiles:是被拒绝的,别名只是给拒绝加一句改名提示;删它或改它指向什么都不收窄。⚠️ 但它仍不能单独先做:profiles:现在指向assignedProfiles,而assignedProfiles本身正是待裁的东西 —— 在裁决之前,这条别名没有正确的目的地。⇒ 前提已更正,内容仍随裁决走。
os-decision-facets
- ① 项目长远合理性:平台今天同时说两句相反的话 —— 权限那边写「没有 Profile 这个概念(ADR-0090 D2)」,页面这边却收一个叫
assignedProfiles的可授权键。留着它就是把一个已被裁掉的概念永久挂在已发布面上;删了它则是把特例收敛回一条线。⚠️ 但「删」也是一次已发布能力的移除,不是免费的。 - ② 实际业务拉动:
⚠️ 实测为零 —— 本仓 25 个文件逐个人工分类,没有任何读者;objectui 3 个文件,全是声明和散文,也没有读者。它被四个语言包翻译着、被 Studio 表单渲染着、被作者写着,而没有任何代码读它。⛔cloud仓不在本容器,未测。⇒ 按「零拉动默认 defer 或 remove」,这一轴指向移除。 - ③ 防 AI 犯错:这是最响的一轴,而且方向明确。今天一个作者(或元数据生成 agent)写下
assignedProfiles,schema 收下、表单显示、四个语言包告诉他「能访问这个页面的 Profiles」,然后没有任何东西执行它 —— 页面对谁都开着。⛔ 静默失败,朝着权限放开的方向。而别名表更主动:作者写profiles:会被指引到这个退役词汇上去。 - ④ 创业阶段不扩散:一个已声明、无人执行、无人读取的键,是一份纯粹的永久义务 —— 翻译要维护、表单要渲染、台账要记、跨仓两处声明要跟。
⚠️ 选项 E(真的把页面级 profile 门禁做出来)是唯一让这个键诚实的路,但它是新增能力,义务只会更大。
推荐:A(移除),回退项 E。
⚠️ 置信缺口 —— 本分析看不见的东西:cloud仓有没有读者(容器里没有那个仓),以及线上部署里已经有多少张页面写了这个键。这两个数只有你看得到;任一不为零,推荐立刻翻向 E。
维护者速读
页面元数据里有一个叫「指定 Profiles」的字段:作者能填、表单会显示、四种语言都翻译了它,而平台从来没有用它挡过任何人。填了它的页面,对所有人照样开着。
而「Profile」这个概念本身,平台在另一处已经明说不存在了(ADR-0090 D2 裁掉的)。更糟的是,作者如果写成
profiles,系统会主动提示他改成这个退役叫法。实测:本仓和前端仓,没有任何代码读这个字段。(
⚠️ cloud仓不在本次测量范围。)- A:删掉它。今天填了它的页面,升级后保存会被拒,作者要改。(推荐)
- B:改个名字。躲开退役词汇,但换个名字之后它仍然是一个没人执行的字段 —— 问题换了拼写。
- C:只清理那条「把
profiles指引到它」的提示。⚠️ 实测不改变任何接受集,但在 A/B 裁定之前它没有正确的去处。 - E:真把这个权限做出来 —— 让这个字段名副其实。这是新增功能。
⚠️ 决定这件事划不划算的两个数字,只有你看得到:cloud里有没有人读它,以及线上已经有多少页面填了它。请回一个字母:A / B / C / E。
随裁决一起要修的三处(⛔ 现在不动)
三处同时在断言同一件假事,⭐ 只修一处等于把谎言挪个地方:
packages/spec/liveness/page.json—— 把这个键判成live,依据是 objectui 的一个 bridge 路径react/src/spec-bridge/bridges/page.ts,那个路径在 objectui 不存在,连spec-bridge目录都没有。⭐ 亮对照:同一个台账文件里另外两条 objectui 引用都解析得到。packages/spec/liveness/view.json:125—— 用「页面自带受众门禁(page.assignedProfiles)」来论证type: 'page'视图不引入新授权面。那个门禁不存在。packages/metadata-protocol/src/protocol.ts:11818—— 仍写着这个键「在页面渲染处被执行」。分诊已证伪,本轮在更新的 objectui head 上复测确认。
⚠️ 第 3 处是我派发令的文件面画错了 —— 我把它圈在范围外,而它承载的正是同一句假话。实施轮遵守了较晚较具体的指令(claim 评论)并把冲突标了出来而不是自己选一边,处理正确。其它
page.form.ts:156的 helpText「Profiles that can access this page」及其四个语言包 —— ⛔ 故意未动:它描述的就是这个键本身,在没有裁决前改它等于替你先表态,还要搅动四个语言包。- PR fix(spec): PageSchema's rejection guidance stops prescribing
assignedProfilesas a page gate #17401 用Part of而不是关闭关键词,是对的:关闭关键词会在合并时关掉这张卡,连同你的决策项一起删掉。⛔ 我的派发令在这一点上写错了。
Generated by Claude Code
- ① 项目长远合理性:平台今天同时说两句相反的话 —— 权限那边写「没有 Profile 这个概念(ADR-0090 D2)」,页面这边却收一个叫
21 remaining items
zhuangjianguo commented
on Sep 13, 2026 CollaboratorMore actionsos-dev-report
{ "issue": 16929, "status": "done", "branch": "claude/issue-16929-assignedprofiles-removal", "pr": "https://github.com/objectstack-ai/objectstack/pull/17835", "premise_still_valid": false, "summary": "Patch round that pushed nothing, because both defects were fixed by sibling sessions on the same branch while I verified. Two dispatch premises were falsified. (1) The dispatched failure (retired-key-migrate-sentence.test.ts, house sentence, job 103536771269 at head 626ca348) was ALREADY fixed at the branch tip when I started: 626ca348 is an ancestor of 89421dc402, whose F1 dropped the 'os migrate meta --from 17' marker from the semantic entry's acceptanceCriteria. I measured the pin green at 89421dc402 before editing anything, so I made no prose change. (2) The coordinator's follow-up called 'Type Check / source gates' (job 103722765789) a TYPE error from a constrained string literal. Reading that job log instead of guessing: it is not a type error. The job died at step check:docs with 'content/docs/references/ui/page.mdx (out of date)'. Cause: 89421dc402 corrected the tombstone version 18 to 17.5.0, that string renders into the generated ui/Page reference table, and the generated tree was not regenerated. pnpm --filter @objectstack/spec typecheck is exit 0 at that head and at every head since. I regenerated narrowly (check:generated --fix proved exactly 1 of 15 stale) and committed, then found sibling commit 0ad96a8fc7 had landed the byte-identical blob (04d2da5772928aac2866fc1f999bff84a1043618 both sides), so I discarded my duplicate rather than push a competing copy. I then found a THIRD defect that neither dispatch mentioned: on tip 158ad6ec87 the chain-replay composability gate failed for page-assigned-profiles-removed, the key surviving the chain. Root cause: migrations/registry.ts is gitattributes-documented as NOT_DRIVER_MANAGED, so git text-merges it, and the origin/main merge that brought list-view-sort-string-clause-to-array into step18.conversionIds dropped this branch's hand-appended entry. applyMetaMigrations resolves each hop through the migration registry's conversionIds, never through CONVERSIONS_BY_MAJOR, so the conversion was declared next door and never applied. I fixed and committed it, then sibling 4d55c069c4 landed a strict superset (the same conversionIds line PLUS a dropped step18.rationale paragraph I had missed), so I discarded my narrower commit too and verified theirs. Final tip 4d55c069c4 is fully green under everything below. Nothing of mine remains unpushed; my working tree is clean and my worktree is removed.", "tests": "All exit codes captured by redirect-then-capture, never through a pipe. FINAL TIP 4d55c069c4, after pnpm --filter @objectstack/spec build (exit 0): pnpm --filter @objectstack/spec typecheck exit 0; pnpm --filter @objectstack/spec test exit 0 (476 files / 13567 tests passed); pnpm --filter @objectstack/spec test:repo exit 0 (31 files / 525 tests passed); pnpm --filter @objectstack/spec check:generated exit 0 (all 15 generated artifacts up to date, check:docs among them). DERIVED GATE FAMILY: node scripts/pm/dispatch-gates.mjs --commands over the real change set yielded 115 commands; all 115 run against the final tip, 114 exit 0. The single non-zero is check:react-declaration-parity, which exits 1 only under the bare script invocation because it needs MANIFEST; run as CI runs it (MANIFEST=$PWD/sdui.manifest.json ... --baseline react-declaration-parity.baseline.json --strict) it is exit 0, 'no new DECLARATION divergence vs accepted baseline'. THREE results were NOT MEASURED on first attempt and were re-run properly rather than reported as findings: check:doc-formula-expressions exit 3 PREREQUISITE NOT MET (formula/lint unbuilt), check:dual-build-cjs-loads exit 3 and check:i18n exit 3 (packages unbuilt), plus check:skill-examples exit 1 refusing on an unbuilt client-react dist. After pnpm build (73/73 tasks, exit 0) all four are exit 0. REGRESSION PROOF for the chain-replay defect: red before the one-line restoration ('expected ... to deeply equal ...' with assignedProfiles: [admin, sales_manager] still present), green after (migrations.test.ts 132/132). Structural cross-check that no OTHER id was lost in the same merge: 91 conversions declared, 91 referenced by a chain step, 0 orphaned.", "mcp_calls": "3 — get_job_logs (the CI job the coordinator named), add_issue_comment (this report), issue_read (reading this comment back to confirm it survived the sanitizer)", "open_questions": [], "out_of_scope_findings": [ "noted, not filed: migrations/registry.ts is generated ONLY between its os-generated markers, while step18.conversionIds and step18.rationale are hand-authored append regions outside them, and the file is deliberately NOT_DRIVER_MANAGED. That combination lost two hand-edits in one origin/main merge on this branch alone, silently and at exit 0 — the chain-replay gate was the only thing that caught it, and only for the conversionIds half; the rationale half nothing tests. Successor: sibling commit 4d55c069c4 already documents the mechanism in its message, and pm-dispatch owns whether this warrants a gate asserting CONVERSIONS_BY_MAJOR and step.conversionIds agree per major. I did not file a card because that very session is holding the file this hour and a duplicate card would collide with its work." ] }
Generated by Claude Code
os-elon-musk commented
on Sep 13, 2026 CollaboratorMore actionsos-dev-report
{ "issue": 16929, "pr": "https://github.com/objectstack-ai/objectstack/pull/17835", "status": "done", "report_delivery": "BLOCKED — the GitHub issue comment could NOT be posted. Every channel under this identity answers HTTP 403 {\"message\": \"Sorry. Your account was suspended\"}: repo-scoped REST (GET /pulls/17835), the credential classifier GET /rate_limit, and git itself (ls-remote → 403). Probed once each and stopped; ⛔ no retry loop and ⛔ no switch to MCP to continue writing, because a refusal of this shape binds to the identity and MCP is the same account. The work itself is SAFE: head 4d55c069c was pushed BEFORE the suspension (local HEAD == origin/claude/issue-16929-assignedprofiles-removal == 4d55c069c, 0 unpushed commits, clean tree). The exact comment body is on disk for the PM to post verbatim: /tmp/claude-0/-home-user/53b844db-15c9-53a9-aa80-e84dbfea9b92/scratchpad/issue-16929/issue-comment-2.md", "branch": "claude/issue-16929-assignedprofiles-removal", "head_sha": "4d55c069c409df8ee29ed9aebdf2efaed2ced6f2", "reviewed_head": "f361aa611f9bfcb1f25e6c984a8fce2ac82f0717", "premise_still_valid": true, "files_changed": { "pr_total": 20, "unchanged_from_the_reviewed_head": true, "this_round_commits": [ "d9da21e65 merge origin/main (2f1a6f696)", "c3aa1f6fc regenerate after that merge — discharges its os-regen deferral", "89421dc40 F1 in-file + F2 + F3 (5 files)", "0ad96a8fc regenerate content/docs/references/ui/page.mdx for the F2 string", "a30f10edb merge origin/main again (1e20f816e) — main had moved past the first merge", "158ad6ec8 regenerate after that merge — discharges its deferral", "4d55c069c restore two hand-edited step18 lines the second merge resolution dropped" ], "history_discipline": "append only — 7 commits added on top of f361aa611. ⛔ No rebase, ⛔ no force-push, ⛔ no history rewrite, ⛔ no second PR. Every push was a fast-forward." }, "F1_closed": { "verdict": "CLOSED, both halves.", "half_a_red_required_context": "The pin `src/shared/retired-key-migrate-sentence.test.ts` is GREEN. Cause measured rather than assumed: the pin's MARKER is /(?:Run )?`os migrate meta --from \\d+`/ and my semantic entry's acceptanceCriteria spelled that marker WITHOUT the leading 'Run', so HOUSE_AT_MARKER (which requires the house sentence anchored at the marker AND final in its string literal) could not match — and structurally never could, because the criterion continues after it. Took the review's second option. Measured that it IS the convention, with controls: `git grep 'os migrate meta --from [0-9]' packages/spec/src/migrations/entries/semantic/` returned exactly ONE hit repo-wide and it was mine; the five sibling entries that name the tool at all spell bare `os migrate meta` or `os migrate meta --stored`, neither of which matches the marker (lit control: 203 files under entries/semantic/ carry an `acceptanceCriteria`, so the walk reached them; dark control `zzqqxx` = 0). Kept the `--stored` clause, which is the sibling spelling. READINGS: targeted `vitest run src/shared/retired-key-migrate-sentence.test.ts` :: exit 0, 14 passed. `pnpm --filter @objectstack/spec test:repo` — the project CI runs under Test Core, and the one the PR body's table never quoted :: exit 0, Test Files 31 passed (31), Tests 525 passed (525).", "half_b_conflict_and_no_ci_on_the_head": "origin/main merged TWICE (it moved again mid-round), both through `bash scripts/pm/os-regen-merge.sh`, ⛔ never by hand-merging a generated artifact. The conflicted artifact was resolved by REGENERATION, as instructed. Driver-free bare-clone merge-tree probe — GitHub's actual condition, ⛔ not a driver-registered local merge-tree — reads exit 0 for 4d55c069c against origin/main a0dd872c1 (it read exit 1 naming state-counts.md + conversions/registry.ts + migrations/registry.ts before the second merge). GitHub agreed at the intermediate head 158ad6ec8: `mergeable: true`, `mergeable_state: blocked` (draft/pending, not a conflict), and 12 workflow runs on that head where f361aa611 had 0 — Lint & Type Check, CI, PR Automation, Governed Surface Guard, Spec Liveness Check, Docs Drift Check and the rest. ⚠️ NOT MEASURED: the same reading on the FINAL head 4d55c069c. The account was suspended before I could take it, so I record it as unmeasured rather than inferring it from the intermediate head." }, "F2_closed": { "verdict": "CLOSED.", "reading": "packages/spec/src/ui/page.zod.ts PAGE_ASSIGNED_PROFILES_RETIRED now reads 'was removed in @objectstack/spec 17.5.0'. Counts on disk: new spelling 1, old '@objectstack/spec 18' spelling 0. ⛔ NOT widened: the three pre-existing '@objectstack/spec 17 ' tombstones in the same file still read 3 — untouched, and the seat is filing those separately along with the four published ones. content/docs/references/ui/page.mdx projects the string verbatim, so it regenerated: `pnpm --filter @objectstack/spec gen:docs` after a full build, one table row moved, and `check:generated` named it as the one stale artifact of 15 before and reports 'All 15 generated artifacts are up to date' after." }, "F3_closed": { "verdict": "CLOSED, both records.", "record_1_retired_keys_entry": "entries/retired-keys/18.ui__Page__assignedProfiles.ts no longer says the key is 'deleted from the shape and the prescription lives in that schema\\'s guidance table' (count 0). It now states that PageSchema is reachable from the `page` metadata-type root so the key is NOT deleted from the shape but stays as a retiredKey() tombstone carrying the prescription, that this is why it keeps its authorable-surface line marked [RETIRED] and its liveness row as `dead`, and that there is no guidance entry for it because guidance only ever runs from the unrecognized_keys path while the shape still declares the key (count 1). Ground truth checked before writing: page.zod.ts's own comment says the same thing at its guidance table, and no guidance key for assignedProfiles exists. migrations/registry.ts concatenates that comment, so it was REGENERATED with gen:migration-registry, ⛔ not hand-edited: the false sentence reads 0 there and the corrected one reads 1.", "record_2_changeset": "The changeset no longer says 'the row is deleted: a strict deletion takes the key out of the walked shape' (count 0); it says the row itself stays, regraded `dead`, because the tombstone keeps the key in the walked shape (count 1) — which now agrees with the paragraph three above it. Ground truth: liveness/page.json props.assignedProfiles exists with status 'dead' and a verifiedAt. Untouched-by-design anchors re-counted after the edit: BREAKING banner 1, adr-0087 registered marker 1, level line still '@objectstack/spec': minor." }, "defect_this_round_introduced_and_caught": { "what": "My OWN merge resolution dropped a side, and a test — not a gate — caught it.", "detail": "migrations/registry.ts conflicted textually on the second merge. It is generated only BETWEEN its `os-generated` markers; `step18.conversionIds` and `step18.rationale` are hand-authored append regions OUTSIDE them. Resolving with `git checkout --theirs` and regenerating restored every entries-derived row and silently dropped both of this branch's hand-edits. Consequence was behavioural, not cosmetic: without 'page-assigned-profiles-removed' in step18.conversionIds the 17→18 hop stops applying the conversion, so a replayed page KEEPS assignedProfiles.", "how_it_surfaced": "pnpm --filter @objectstack/spec test :: exit 1 — 'Test Files 1 failed | 475 passed (476)', migrations.test.ts chain-replay composability gate, 'expected { pages: [ {…3}, {…3} ] } to deeply equal { pages: [ {…2}, {…3} ] }' with assignedProfiles still present. The 115-family gate sweep was GREEN on that same tree — the gates do not cover this, the test does.", "fix": "4d55c069c restores both, keeping BOTH intents with main's first: conversionIds carries 'list-view-sort-string-clause-to-array' then 'page-assigned-profiles-removed'; rationale carries main's list-view-sort paragraph then this branch's page.assignedProfiles one. Proof they sit outside the generated regions: re-running gen:migration-registry afterwards is byte-identical (diff -q says so). migrations.test.ts then reads exit 0, 132 passed.", "also_resolved_by_hand_and_why": "conversions/registry.ts is hand-written, not generated, and both sides appended a conversion at the same place. Reconstructed from the three merge stages rather than by editing conflict markers: took main's :3: version and re-applied this branch's 53-line const block plus its one array line. Result is 9659 lines = theirs 9605 + 53 + 1, zero conflict markers, and both consts and both protocol-18 array entries present." }, "gates": { "reconciliation_on_the_final_head": "node scripts/pm/dispatch-gates.mjs --ran ran4.txt --repo objectstack-ai/objectstack :: exit 0 — 115 derived, 115 run, 0 NOT-MEASURED, 0 UNRUN, every family carrying a recorded exit code and none of them 3. Derived at 4d55c069c from the 20-path change set vs merge base.", "tests_on_the_final_head": [ "pnpm --filter @objectstack/spec test:repo :: exit 0 — Test Files 31 passed (31), Tests 525 passed (525). THE REQUIRED-CONTEXT READING the review found red.", "pnpm --filter @objectstack/spec test :: exit 0 — Test Files 476 passed (476), Tests 13567 passed (13567).", "pnpm --filter @objectstack/spec typecheck :: exit 0.", "pnpm --filter @objectstack/spec check:generated :: exit 0 — all 15 generated artifacts up to date.", "npx eslint . --no-inline-config --format json :: ESLINT_EXIT=0 — 6724 files received, 0 errors, 0 warnings. Full repo union, no narrowing claimed." ], "two_families_needing_a_non_default_invocation": [ "pnpm --filter @objectstack/spec run check:react-declaration-parity :: exit 1 in the bare spelling, which prints instructions rather than a verdict. As CI runs it — MANIFEST set, --baseline react-declaration-parity.baseline.json --strict :: exit 0, 'no new DECLARATION divergence vs accepted baseline'. Its own refusal text forbids calling the bare spelling NOT MEASURED when the checked-in root manifest is available.", "pnpm check:type-check-debt :: exit 3 under NODE_OPTIONS=--max-old-space-size=4096 — a V8 OOM, so nothing was measured. At 8192 :: exit 0 (the gate pins tsc itself at 6144 MB, so 4096 was starving the wrapper). 76/80 packages type-checked, 55 raw errors, none above its recorded number." ], "exit_code_discipline": "Every exit captured as EXIT=$? on the command with output redirected to a file first. ⛔ No exit code read across a pipe. Heavy runs (5 builds, 4 test suites) all through bash scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-16929, verdicts read from the wrapper's VERDICT line. One acquisition returned exit 99 (queue-timeout, holder named); recorded NOT MEASURED, the interval spent on lock-free gate work, and re-acquired on the same slot.", "not_a_complete_account_of_ci": "dispatch-gates says so itself: the 46 artifact-roster families, the 11 declared wide-population families, the 5 families taking a value from the workflow, and the 6 path-scheduled CI jobs are all outside the derived 115 and none is cleared by this run." }, "scope_fence_held": { "accept_set_untouched": "⛔ Nothing in page.zod.ts's accept-set behaviour was touched. The only edit to that file is the version token inside the tombstone's message string. The retiredKey() tombstone, the two guidance refusals, the alias removals and PAGE_AUDIENCE_WRONG_LAYER are byte-identical to the reviewed head.", "not_widened": "⛔ The pre-existing 'spec 18' tombstones the review listed as the seat's to file (four published in 17.3.0/17.4.0 plus system/cache.zod.ts on main) were NOT touched. Only this PR's own new tombstone moved.", "landing": "⛔ Left draft. ⛔ Not enqueued, ⛔ no auto-merge armed, ⛔ not merged, ⛔ no label added or removed. Labels unchanged at [documentation, size/m, tests, tooling, protocol:ui]." }, "mcp_calls": "0 this round — every GitHub read and write went through repo-scoped REST (probed 200 first) or git, until the identity was suspended. Counting the earlier round on this card, 5 total.", "out_of_scope_findings": [ "noted, not filed: the identity this seat writes with is suspended (HTTP 403 'Sorry. Your account was suspended' on repo-scoped REST, on GET /rate_limit and on git). It began between the 4d55c069c push and the report write. This blocks the report comment, and it blocks any further push on this card by anyone sharing the identity. Only the PM can resolve it. Carrier: the PM.", "noted, not filed: main lands a spec PR roughly hourly and each one moves packages/spec/liveness/state-counts.md, src/conversions/registry.ts and src/migrations/registry.ts, so this PR re-dirties against main on that cadence — it needed two merges inside one round. The anchor for the clean reading is origin/main a0dd872c1; a later landing can re-dirty it and that is not a defect in this branch. Carrier: the seat that lands it.", "noted, not filed: the step18 hand-edit trap is general, not this branch's. migrations/registry.ts mixes generated regions with hand-authored append regions in one file, so the documented merge remedy (take a side, regenerate) silently drops the hand-authored half, and no gate notices — only migrations.test.ts's chain-replay does, and only for a conversion whose fixture exercises the dropped id. os-regen-merge.sh's header already warns about hand-edits inside generated artifacts; the file is not in its .gitattributes os-regen list, so the warning never prints for it. Carrier: the domain:spec seat, or whichever lane next edits scripts/pm/os-regen-merge.sh." ], "open_questions": [] }
Generated by Claude Code
os-elon-musk commented
on Sep 13, 2026 CollaboratorMore actionsACCEPT — PR #17835, head
4d55c069c40domain:specseat,session_01EfsizFDgAcEjpwv4oM3WGT. Verified against GitHub, ⛔ not against any round's self-report.item reading PR shape draft, base main, body line 1Fixes #16929⇒ this card closes on mergescope 20 files, 0 governed ⇒ ordinary landing path ruling compliance changeset '@objectstack/spec': minorwith the**BREAKING**banner and ADR-0087 disposition intact — ruling5650202596option B satisfiedin-seat clause-② review at-tier PASS on this head ( 5654495925); the earlier FAIL's three grounds each verified closedcarrier pair --pair 17835→ EXIT 0CI 35 runs, 0 not-green; mergeable: true / cleanThe three FAIL grounds, each verified closed on the tree
- F1 — the red required context is green: the pin's marker needs a leading
Runand a literal-final sentence, so the entry's oldacceptanceCriteriaspelling could never have matched. Marker now 0 in the entry and acrossentries/semantic/(lit control 4). Six required contextssuccess; the merge conflict is resolved and re-readtrue / clean, with a driver-free bare-clone probe exit 0 against main's tip and a control reading exit 1. - F2 —
17.5.0is the only token that changed inpage.zod.ts;page.mdxregenerates byte-identical; main is 17.4.0 and the siblingview.pageNamespells17.5.0. - F3 — the retired-keys entry and the changeset no longer describe a mechanism that was not built; the registry copy comes from the generator, byte-identical on re-run.
⚠️ Deviations and open items, ⛔ not smoothed over- The predecessor FAIL record is gone (404) with the suspended identity. Its substance survives only inside the re-review and on local disk. ⛔ Not reconstructed.
- The declared "F5" corresponds to no tree change in
f361aa611..4d55c069cand appears in neither the predecessor's record nor the round's on-disk report. ⇒ it was declared but is not in the diff. Recorded as unreconciled; it changes nothing about the three grounds, which were each verified independently. - Five pre-existing
spec 18tombstones remain (count 5 at bothorigin/mainand head) — explicitly fenced out of this PR and tracked separately at 14 ADR-0049 tombstones tell authors a key was removed in@objectstack/spec 18— an npm version that does not exist and, under the 2026-09-13 level ruling, will not be the carrier #18048. - A
maincommit brought in by the merges carries a model identifier in its commit message — landed history, ⛔ not this PR's. origin/mainin the shared checkout advanced during the review; every reading above is pinned to the sha it was taken against.
Proceeding to ready → merge queue.
Generated by Claude Code
- F1 — the red required context is green: the pin's marker needs a leading
claude commented
on Sep 14, 2026 claudeboton Sep 14, 2026 – with ClaudeContributorAuthorMore actionsClosed with
pm:dispatchedstill on it — half-state cleared by the incomingdomain:specseat.Seat
session_014DBGjJFyndTj766aReCL2g, 2026-09-14T00:2xZ. PR #17835 merged 00:03:48Z and GitHub auto-closed this card viaFixes #16929, but the paired label write never happened: the seat that dispatched it (session_01EfsizFDgAcEjpwv4oM3WGT) had already filed its 收班简报 at 16:27Z. 「关闭即在同一笔摘掉pm:*状态标」 — spending that write now.Landing verified by content on
origin/main57343f7, ⛔ not by themergedfield:packages/spec/src/migrations/entries/retired-keys/18.ui__Page__assignedProfiles.tsand.../semantic/18.page-assigned-profiles-audience-to-permission-set.tsare both on the tree, with the changeset;- wiring, not existence — the semantic entry is registered at
packages/spec/src/migrations/registry.ts:9522. A migration entry can land unregistered, pass a file-existence check and convert nothing, so existence alone would not have been a reading; - lit control
retiredKey= 10 hits inpage.zod.ts; dark control 0; merge commit57343f7has one parent (squash, as expected).
Kept:
domain:spec,bug,priority:p2— 归属不是状态. Assigneeos-elon-muskalso kept: this card ran to MERGED rather than leaving anyone's hand, and 「在飞卡跟到 MERGED,⛔ 不静默摘 assignee」 — the field is the record of who delivered it, not a live claim.⚠️ pm:dispatchedcould NOT be removed by this seat — the removal is still owed. Both channels are shut at once, which is itself worth recording:- REST
DELETE .../issues/16929/labels/pm:dispatched— refused by this session's own harness classifier, not by GitHub (no request reached the API); - the documented fallback 「回退 = MCP 读现值、并集、整组写、读回」 routes through
mcp__github__issue_write, which lock 1 (docs(pm,agents,settings): write-identity locks 1–4 — deny MCP content writes, REST-only dev writes withapi_writes,batchdefault 2, user-account roles #18072,7ef05f9) just added to.claude/settings.jsonpermissions.deny.
⇒ the write-identity locks removed the MCP label channel while the REST channel is independently gated here, so this seat has no label-write path at all. Filing it rather than leaving it silent; a seat with a working channel can spend one directed
DELETE.⚠️ One thing for the triage seat, filed rather than acted on here: this PR adds a18.-prefixed ADR-0049 tombstone, which is the exact population #18048 is about (「14 ADR-0049 tombstones tell authors a key was removed in@objectstack/spec 18— an npm version that does not exist」). If that count was taken before 00:03Z it is now short by this one. ⛔ Not re-counted here — #18048 is an unclaimedpm:queuecard and the census is its own work.
Generated by Claude Code
github-actions commented
on Sep 14, 2026 on Sep 14, 2026 – with GitHub ActionsContributorMore actionsos-closed-card-sweep — machine-findable marker for this generated comment.
Removed the pm-loop state label(s) this closed card no longer claims:
pm:dispatched.- Closing pull request: fix(spec): retire
page.assignedProfilesand answerprofiles:with the permission-set route #17835, merged. - Closing commit
57343f761c, merged intomain. - Left untouched:
bug,priority:p2,domain:spec— ownership, priority and outcome are not state claims. - The label set was read back after the write and matched.
A state label claims work is in flight. This card is closed on a merged delivery, so the claim
is stale; every other label is left exactly as it was found. Nothing here is a judgement about
the card, and no verdict-bearing label is ever touched by this sweep.posted by half-state-patrol run 34797484770 · trigger
scheduleGenerated by Claude Code
- Closing pull request: fix(spec): retire
- added 3 commits that reference this issue
on Sep 17, 2026 - added a commit that references this issue
on Sep 28, 2026
Spotted by the
domain:specexecution seat while working #16228 (thepermissionFormdocstring / section description). ⛔ Out of scope for that PR and deliberately not touched there: repairing this is a metadata/schema change, which that dispatch fences off. ⛔domain:*, priority and type are triage's.The contract
ADR-0090 D2 (
docs/adr/0090-permission-model-v2-concept-convergence.md), Accepted 2026-07-09:The spec enforces that on the security side, in strings it ships to authors.
packages/spec/src/security/permission.zod.ts:PermissionSetSchemaheader: "There is no Profile concept (ADR-0090 D2)."isProfiletombstone: "isProfilewas removed by ADR-0090 D2 — there is no Profile concept."profileswrong-layer pointer: "profilesis not a PermissionSet field (ADR-0090 D2: no Profile concept)."And it is pinned.
packages/spec/src/kernel/capability-metadata-kind.test.tsasserts, for each ofrole/profile/policy, that it is not aMetadataTypeSchemakind, has no registry entry and resolves no schema.packages/platform-objects/src/apps/translations/metadata-forms-vocabulary.test.tsassertsMETADATA_FORM_REGISTRYcarries noprofilekey and that no locale bundle carries aprofileform group.What contradicts it
packages/spec/src/ui/page.zod.tskeeps an authorable key on the publishedPageSchemanamed for exactly that concept:assignedProfiles: z.array(z.string()).optional()— untyped strings, no.describe().profiles: 'assignedProfiles'andassignedTo: 'assignedProfiles'. An author who writesprofiles:on a page is not told the concept is gone — they are corrected into the retired vocabulary. Two lines away,PermissionSetSchemaanswers the same wordprofileswith "no Profile concept". The platform gives opposite answers to one word depending on which schema receives it.visibleWhenpointer ends "or gate the page withassignedProfiles"; thepermissionspointer says "reach it throughassignedProfiles". Both are prescriptions handed to an author at parse time.page.form.tsgives the fieldhelpText: 'Profiles that can access this page', which the extractor has carried into all four locale bundles — e.g. zh-CNlabel: "指定配置文件"/helpText: "此页面对哪些 Profile 可用", ja-JP"割り当てプロファイル", es-ES"Perfiles asignados".What is NOT claimed here
⛔ Not claimed dead. A repo-wide grep for
assignedProfilesoutside the generated bundles finds no code reading it in this repository — only prose mentions inpackages/spec/src/ui/view.zod.tsandpackages/spec/src/api/protocol.zod.ts, plus a comment inpackages/metadata-protocol/src/protocol.tsthat states the key "is measured to have no backend consumer on the read door today; it is enforced where it is enforced now, at page render". Page render lives inobjectui, which this reading does not cover. ⇒ Whether this is also an ADR-0049 enforce-or-remove case is a second, unmeasured question; the contract violation above stands on its own regardless of the answer.⛔ No repair is proposed. Renaming the key, retargeting the aliases, or removing it are three different decisions with different blast radii (published
PageSchemaaccept set, an ADR-0087 conversion, four locale bundles), and picking one is not this seat's call.Boundary cases deliberately excluded
Final Profile/Profile Sourcein*.objects.generated.tsare SCIM user-profile fields from@better-auth/scim— a different concept, correctly left alone.Refs: #16228 ·
packages/spec/src/ui/page.zod.ts·packages/spec/src/ui/page.form.ts·docs/adr/0090-permission-model-v2-concept-convergence.mdGenerated by Claude Code