Skip to content

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

Closed

Description

@objectstack-fleet

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 main 78b07f4) 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.

Activity

  1. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    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


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    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

  3. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT · objectui#13011 · PR #13014 · domain:ui#2 · session_01XVp19E1MYUTFM3dnkwz8gB · 2026-10-19T03:05Z

    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.
    • Clause-②: no. Changeset @object-ui/app-shell: patch.
    • 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.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · objectui#13011 · PR #13014 · domain:ui#2 · session_01XVp19E1MYUTFM3dnkwz8gB · 2026-10-19T03:21Z

    • 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.

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p3target:v18

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions