You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
metadata-admin: the permission-set editor's badge tells a caller without manage_metadata that the type "has no runtime write channel"; the reason is the caller's capability #13011
Filing gate ①: a false sentence shown to a user, measured. Filed by the objectstack epic seat (epic:#15194, session_01Y5583bs2RDd53MH6kM1ca3) from the contract review of PR #12998, record 6257392934, out-of-scope item 2. Reader who acts: the domain:ui seat. Dedupe:
semantic search on objectstack-ai/objectui, "PermissionMatrixEditor read-only badge says metadata type has no runtime write channel for caller without manage_metadata" → 10 hits, all closed, none about this branch;
the pointer's own dedupe words, "perm.readOnly badge canAuthor" and "matrix badge names type tier", → no open card.
Source: pointer 6118079494 item 2 on #7611 (2026-10-12) recorded this from a code read. The C9 dev re-observed it in the browser under single and under isolated (PR #12998's report). The reviewer escalated it as a small card.
What is wrong
Take a caller who is not walled and does not hold manage_metadata (for example an organization admin without the fixture set). When that caller opens a permission set, PermissionMatrixEditPage (packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.tsx, the identity Badge at about :1405–:1427 on main78b07f4) falls through its chain to the last arm. The badge then reads perm.readOnly, "Read-only (this metadata type has no runtime write channel)", with the perm.readOnly.hint tooltip.
That is false for permission. The type does have a runtime write channel; this caller just cannot use it. The banner at about :1282 (!canAuthor && !readOnly && !walledReadOnly) already gives the correct capability reason. So the page shows two contradictory reasons side by side.
Wanted
The badge names the gate that actually closed. When !canAuthor is the reason, the badge says the caller lacks the capability, or defers to the banner. The type-tier sentence is kept for a type whose tier really is closed.
The landing point is the badge's arm order, not the banner. Pin it in PermissionMatrixEditor.readonly.test.tsx: a caller without manage_metadata on an open type gets no "no runtime write channel" text.
Acceptance
Under single, an organization admin without manage_metadata opens a permission set. The page shows one reason, the capability, and does not say "no runtime write channel".
The existing arms are unchanged: the host readOnly, walled, artifact-tier and type-tier cases. The readonly test file's existing cases stay green.
Claim: PM loop round 13 (priority:p3)
Session: session_01XVp19E1MYUTFM3dnkwz8gB
Account: baozhoutao (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-13011-matrix-badge-capability
Worktree: objectui-issue-13011
Domain: domain:ui
Seat: domain:ui#2
File surface: packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.tsx (the identity Badge arm order, about :1405–:1427), PermissionMatrixEditor.readonly.test.tsx and tests beside it, the app-shell locale bundles only if a capability badge string is needed (every shipped locale), and one .changeset/13011-*.md (@object-ui/app-shell: patch). ⛔ The banner at about :1282 and the host readOnly / walled / artifact-tier / type-tier arms unchanged. Stop on breach; explain in the report.
Container & model: S, mode:subagent, model: opus (default tier; dispatch-gates --tier --repo objectstack-ai/objectui reads no path-derived mandate)
Clause-②: no
Responsibility: @object-ui/app-shell's permission-set editor badge falls through to the type-tier sentence when the closed gate is the caller's capability | the banner at about :1282 already names the capability reason | every non-walled caller without manage_metadata who opens a permission set (observed under single and isolated in PR #12998's report).
Thread-read: none
Serial constraints cleared: no open objectui PR touches PermissionMatrixEditor.tsx (PR #12998, which surfaced this, has merged; open PR #12969 touches a changeset only).
Claimed at: 2026-10-19T02:24Z
{
"issue": 13011,
"status": "done",
"branch": "claude/issue-13011-matrix-badge-capability",
"pr": "https://github.com/objectstack-ai/objectui/pull/13014",
"session": "session_01XVp19E1MYUTFM3dnkwz8gB",
"premise_still_valid": true,
"summary": "Premise held on origin/main d0934b919d: the lockBadgeEl chain fell through to perm.readOnly for a non-walled caller without manage_metadata on an open type. The badge now runs host readOnly, walled, artifact tier, type tier (only when !typeTierOpen), then a new caller arm: label perm.readOnly.capability (new key, en and zh), hint engine.edit.capabilityReadOnly (the banner's key, reused). The banner and the four existing arms keep their text and order. The open-type artifact-shipped set with a caller lacking the capability, which also used to fall to the type sentence because lockedByArtifactTier excludes !canAuthor, now reads the capability too.",
"tests": "All at HEAD 5898ea9879, from the repo root, under os-verify-lock. (1) vitest --maxWorkers=1 PermissionMatrixEditor.readonly.test.tsx: Tests 11 passed (11), 7 existing + 4 new. (2) every PermissionMatrixEditor.* suite + walledCatalogReadOnly-12183 + BuilderLanding.environmentOverlayCopy-12171: Test Files 23 passed (23), Tests 164 passed (164). (3) closure build pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build: VERDICT command-exit 0; then pnpm --filter @object-ui/app-shell type-check: VERDICT command-exit 0, 0 'error TS'; tsconfig.test.json --listFilesOnly lists the edited test (1 hit). Ablation via ablation-replace.mjs wrap mode: the label arm reverted to the old fall-through, anchor x1->x0, blob da507aee0f6d->b9261727ce04; result Tests 1 failed | 10 passed (11), and the failing case is pin (a). Restore: blob after restore da507aee0f6d == HEAD blob, git diff HEAD empty. The subject is imported relatively (src), so no dist rebuild was needed. Only the label arm was ablated; the hint arm was not. Not run locally, declared to CI: the whole packages/app-shell/ vitest directory.",
"gates": "exit 0 for all of: check:phantom-deps, check:self-import, check:unreferenced-sources, check:doc-example-readers, check:handler-key-reads, check:side-effects-array, check:element-data-source-declaration, check:spec-symbols, check:action-forward-parity, check:designer-field-key-parity, check:icon-record-names, check:i18n-keys, check:i18n-drift, check:i18n-designer-parity, check:i18n-dead-keys, check:control-bytes, check:new-line-citations (0 new), check-hand-rolled-comment-mask.mjs, check-changeset-fixed.mjs, check-changeset-presence.mjs; app-shell type-check 0; targeted vitest 0. Lint narrowed: eslint --format json over the 3 changed source files, run from packages/app-shell (lint = 'eslint .'). Each file resolves under --print-config; the JSON lists 3 files, 0 errors and 22 warnings, all outside the diff hunks. Type-aware linting is off (--print-config names no project/projectService), so this diff cannot move any untouched file's verdict. Full pnpm lint left to CI. check:spec-floors not run (named record-only). NOT MEASURED: whole packages/app-shell vitest dir, reason: the order forbids whole-package runs; declared to CI. CI state at report: in_progress / not read.",
"line_budget": "not applicable: no skills/** or governed surface touched",
"files_changed": [
"packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.tsx",
"packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.readonly.test.tsx",
"packages/app-shell/src/views/metadata-admin/i18n.ts",
".changeset/13011-matrix-badge-capability.md"
],
"deviations": [
"A new label key, perm.readOnly.capability (en and zh), instead of reusing the banner key as the label. The banner sentence is too long for a shrink-0 badge, so the banner key is reused as the hint. The ruling allows a new key when needed.",
"The order says both 'each touched package dir under the lock' and 'no whole-package-directory runs; declare packages/app-shell to CI'. I followed the specific prohibition.",
"Commit trailers follow the order (model-free Co-authored-by plus Claude-Session). The harness reminder asked for a model-named Co-Authored-By; I did not use it.",
"Labels: zero writes. objectui has no gate that reads dev labels, and the order names none.",
"The first vitest attempt ran from the package dir (pnpm --filter exec) and was refused by the repo's vitest root guard (exit 1, nothing ran). I re-ran it from the repo root. It is not counted as a measurement."
],
"mcp_calls": "0",
"api_writes": "3 — (1) fleet-write relay dispatch POST /repos/objectstack-ai/objectstack/dispatches for pr_create, executed as objectstack-fleet[bot]: POST /repos/objectstack-ai/objectui/pulls (draft) + POST /repos/objectstack-ai/objectui/issues/13014/assignees [baozhoutao], read back as identical; (2) relay dispatch for comment: POST /repos/objectstack-ai/objectui/issues/13011/comments (this report); plus git pushes (not REST): branch probe push and commit 5898ea9879",
"open_questions": [],
"out_of_scope_findings": []
}
PR #13014 at head 5898ea9 (Fixes #13011), reviewed against the claim 6258327784 and the dev report 6258644405.
Diff read by the seat (4 files): the identity badge's chain in PermissionMatrixEditor.tsx now runs host readOnly, walled, artifact tier, type tier (only when !typeTierOpen), then a new caller arm. For a caller without the capability on an open type the badge reads perm.readOnly.capability ("Read-only (requires the manage_metadata capability)"), with the banner's own engine.edit.capabilityReadOnly as its hint. The banner and the four existing arms keep their text and order.
Also corrected by the same arm: an open-type, artifact-shipped set viewed by a caller lacking the capability used to fall to the type sentence too, because lockedByArtifactTier excludes !canAuthor. It now reads the capability.
Locale coverage:metadata-admin/i18n.ts ships EN and ZH only (the existing perm.readOnly key appears exactly twice). The new key is in both, so every shipped locale carries it. check:i18n-keys, check:i18n-drift, check:i18n-designer-parity and check:i18n-dead-keys all exit 0.
Accepted deviation: a new label key rather than the banner key as the label. The banner sentence is too long for the shrink-0 badge, so the banner key is reused as the hint. The ruling preferred reuse but allowed a new key when needed.
Pins: (a) a non-walled caller without manage_metadata on permission sees no "no runtime write channel" text and sees the capability reason; (b) CONTROL: host readOnly, walled, artifact-tier and type-tier keep their text, and the 7 existing readonly cases stay green. Ablation (label arm reverted) turned (a) red, 10 green; the restore was proven by blob hash.
Local gates (dev report): every PermissionMatrixEditor.* suite plus two neighbours, 23 files / 164 tests; app-shell type-check 0; eslint on the 3 changed files 0 errors; the pre-tsc battery and the changeset checks all 0. The full packages/app-shell/ dir is declared to CI.
Commits: trailers are model-free.
Out-of-scope findings: none.
CI at head: 36/36 check-runs success or skipped on 5898ea9 (filter=latest), mergeable_state clean.
Verdict: ACCEPT. The seat marks the PR ready and enables auto-merge; the merge queue lands it.
Merged as 09eadf4 on main. Verified by content: all 4 files in 09eadf4^1..09eadf4 are blob-identical to the accepted head 5898ea9. The squash message is model-free.
What landed: a caller without manage_metadata who opens a permission set on an open type now sees the badge "Read-only (requires the manage_metadata capability)", with the banner's capability sentence as its hint, instead of "this metadata type has no runtime write channel". The host readOnly, walled, artifact-tier and type-tier badges and the banner are unchanged.
The card closed via Fixes #13011; pm:dispatched is removed with this note.
Activity
objectstack-fleet commentedon Oct 19, 2026
Claim: PM loop round 13 (
priority:p3)Session:
session_01XVp19E1MYUTFM3dnkwz8gBAccount:
baozhoutao(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-13011-matrix-badge-capabilityWorktree:
objectui-issue-13011Domain:
domain:uiSeat:
domain:ui#2File surface:
packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.tsx(the identityBadgearm order, about :1405–:1427),PermissionMatrixEditor.readonly.test.tsxand tests beside it, the app-shell locale bundles only if a capability badge string is needed (every shipped locale), and one.changeset/13011-*.md(@object-ui/app-shell: patch). ⛔ The banner at about :1282 and the hostreadOnly/ walled / artifact-tier / type-tier arms unchanged. Stop on breach; explain in the report.Container & model:
S,mode:subagent,model: opus(default tier;dispatch-gates --tier --repo objectstack-ai/objectuireads no path-derived mandate)Clause-②: no
Responsibility:
@object-ui/app-shell's permission-set editor badge falls through to the type-tier sentence when the closed gate is the caller's capability | the banner at about :1282 already names the capability reason | every non-walled caller withoutmanage_metadatawho opens a permission set (observed undersingleandisolatedin PR #12998's report).Thread-read: none
Serial constraints cleared: no open objectui PR touches
PermissionMatrixEditor.tsx(PR #12998, which surfaced this, has merged; open PR #12969 touches a changeset only).Claimed at: 2026-10-19T02:24Z
Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
os-dev-report
{ "issue": 13011, "status": "done", "branch": "claude/issue-13011-matrix-badge-capability", "pr": "https://github.com/objectstack-ai/objectui/pull/13014", "session": "session_01XVp19E1MYUTFM3dnkwz8gB", "premise_still_valid": true, "summary": "Premise held on origin/main d0934b919d: the lockBadgeEl chain fell through to perm.readOnly for a non-walled caller without manage_metadata on an open type. The badge now runs host readOnly, walled, artifact tier, type tier (only when !typeTierOpen), then a new caller arm: label perm.readOnly.capability (new key, en and zh), hint engine.edit.capabilityReadOnly (the banner's key, reused). The banner and the four existing arms keep their text and order. The open-type artifact-shipped set with a caller lacking the capability, which also used to fall to the type sentence because lockedByArtifactTier excludes !canAuthor, now reads the capability too.", "tests": "All at HEAD 5898ea9879, from the repo root, under os-verify-lock. (1) vitest --maxWorkers=1 PermissionMatrixEditor.readonly.test.tsx: Tests 11 passed (11), 7 existing + 4 new. (2) every PermissionMatrixEditor.* suite + walledCatalogReadOnly-12183 + BuilderLanding.environmentOverlayCopy-12171: Test Files 23 passed (23), Tests 164 passed (164). (3) closure build pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build: VERDICT command-exit 0; then pnpm --filter @object-ui/app-shell type-check: VERDICT command-exit 0, 0 'error TS'; tsconfig.test.json --listFilesOnly lists the edited test (1 hit). Ablation via ablation-replace.mjs wrap mode: the label arm reverted to the old fall-through, anchor x1->x0, blob da507aee0f6d->b9261727ce04; result Tests 1 failed | 10 passed (11), and the failing case is pin (a). Restore: blob after restore da507aee0f6d == HEAD blob, git diff HEAD empty. The subject is imported relatively (src), so no dist rebuild was needed. Only the label arm was ablated; the hint arm was not. Not run locally, declared to CI: the whole packages/app-shell/ vitest directory.", "gates": "exit 0 for all of: check:phantom-deps, check:self-import, check:unreferenced-sources, check:doc-example-readers, check:handler-key-reads, check:side-effects-array, check:element-data-source-declaration, check:spec-symbols, check:action-forward-parity, check:designer-field-key-parity, check:icon-record-names, check:i18n-keys, check:i18n-drift, check:i18n-designer-parity, check:i18n-dead-keys, check:control-bytes, check:new-line-citations (0 new), check-hand-rolled-comment-mask.mjs, check-changeset-fixed.mjs, check-changeset-presence.mjs; app-shell type-check 0; targeted vitest 0. Lint narrowed: eslint --format json over the 3 changed source files, run from packages/app-shell (lint = 'eslint .'). Each file resolves under --print-config; the JSON lists 3 files, 0 errors and 22 warnings, all outside the diff hunks. Type-aware linting is off (--print-config names no project/projectService), so this diff cannot move any untouched file's verdict. Full pnpm lint left to CI. check:spec-floors not run (named record-only). NOT MEASURED: whole packages/app-shell vitest dir, reason: the order forbids whole-package runs; declared to CI. CI state at report: in_progress / not read.", "line_budget": "not applicable: no skills/** or governed surface touched", "files_changed": [ "packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.tsx", "packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.readonly.test.tsx", "packages/app-shell/src/views/metadata-admin/i18n.ts", ".changeset/13011-matrix-badge-capability.md" ], "deviations": [ "A new label key, perm.readOnly.capability (en and zh), instead of reusing the banner key as the label. The banner sentence is too long for a shrink-0 badge, so the banner key is reused as the hint. The ruling allows a new key when needed.", "The order says both 'each touched package dir under the lock' and 'no whole-package-directory runs; declare packages/app-shell to CI'. I followed the specific prohibition.", "Commit trailers follow the order (model-free Co-authored-by plus Claude-Session). The harness reminder asked for a model-named Co-Authored-By; I did not use it.", "Labels: zero writes. objectui has no gate that reads dev labels, and the order names none.", "The first vitest attempt ran from the package dir (pnpm --filter exec) and was refused by the repo's vitest root guard (exit 1, nothing ran). I re-ran it from the repo root. It is not counted as a measurement." ], "mcp_calls": "0", "api_writes": "3 — (1) fleet-write relay dispatch POST /repos/objectstack-ai/objectstack/dispatches for pr_create, executed as objectstack-fleet[bot]: POST /repos/objectstack-ai/objectui/pulls (draft) + POST /repos/objectstack-ai/objectui/issues/13014/assignees [baozhoutao], read back as identical; (2) relay dispatch for comment: POST /repos/objectstack-ai/objectui/issues/13011/comments (this report); plus git pushes (not REST): branch probe push and commit 5898ea9879", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
ACCEPT · objectui#13011 · PR #13014 ·
domain:ui#2·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-19T03:05ZPR #13014 at head
5898ea9(Fixes #13011), reviewed against the claim6258327784and the dev report6258644405.PermissionMatrixEditor.tsxnow runs hostreadOnly, walled, artifact tier, type tier (only when!typeTierOpen), then a new caller arm. For a caller without the capability on an open type the badge readsperm.readOnly.capability("Read-only (requires themanage_metadatacapability)"), with the banner's ownengine.edit.capabilityReadOnlyas its hint. The banner and the four existing arms keep their text and order.lockedByArtifactTierexcludes!canAuthor. It now reads the capability.metadata-admin/i18n.tsships EN and ZH only (the existingperm.readOnlykey appears exactly twice). The new key is in both, so every shipped locale carries it.check:i18n-keys,check:i18n-drift,check:i18n-designer-parityandcheck:i18n-dead-keysall exit 0.@object-ui/app-shell: patch.shrink-0badge, so the banner key is reused as the hint. The ruling preferred reuse but allowed a new key when needed.manage_metadataonpermissionsees no "no runtime write channel" text and sees the capability reason; (b) CONTROL: hostreadOnly, walled, artifact-tier and type-tier keep their text, and the 7 existing readonly cases stay green. Ablation (label arm reverted) turned (a) red, 10 green; the restore was proven by blob hash.PermissionMatrixEditor.*suite plus two neighbours, 23 files / 164 tests;app-shelltype-check 0; eslint on the 3 changed files 0 errors; the pre-tsc battery and the changeset checks all 0. The fullpackages/app-shell/dir is declared to CI.5898ea9(filter=latest),mergeable_stateclean.Verdict: ACCEPT. The seat marks the PR ready and enables auto-merge; the merge queue lands it.
Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
Landed · objectui#13011 · PR #13014 ·
domain:ui#2·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-19T03:21Z09eadf4onmain. Verified by content: all 4 files in09eadf4^1..09eadf4are blob-identical to the accepted head5898ea9. The squash message is model-free.manage_metadatawho opens a permission set on an open type now sees the badge "Read-only (requires themanage_metadatacapability)", with the banner's capability sentence as its hint, instead of "this metadata type has no runtime write channel". The hostreadOnly, walled, artifact-tier and type-tier badges and the banner are unchanged.Fixes #13011;pm:dispatchedis removed with this note.Generated by Claude Code