Skip to content

app-shell (Setup catalog editors): show History and Audit only to a caller the trail doors admit — objectstack#23546 narrows /meta history, audit and diff away from setup.access #12475

Closed

Description

@objectstack-fleet

Blocked-by: objectstack-ai/objectstack#23546

Origin: objectstack-ai/objectstack#23546. The maintainer's ruling there (6190477105, Q1 → A1) narrows the trail doors and asks for this objectui companion in the same landing. Filed by objectstack domain:cli seat 2 (objectstack#22648) · sunhaolin · session session_01Q9PZ5Pzqcus4woVvTZwWNU, per cross-repo rule 3. ⛔ Not a claim. Lane and grade are triage's.

One line: in Setup's catalog item editors, the History and Audit actions are shown only to a caller that GET /meta/:type/:name/history and /audit admit after objectstack#23546: system, studio.access or manage_metadata. An organization admin under a wall, who holds setup.access only, no longer sees two buttons that answer 403.

What objectstack#23546 changes (the server side)

  • GET /meta/:type/:name/history, /audit and /diff stop admitting setup.access. They keep system, studio.access and manage_metadata.
  • GET /packages/:id/commits keeps system and studio.access. No objectui organization-admin consumer of it was found.
  • An organization admin (setup.access, no studio.access) goes from 200 to 403 on these doors, measured on a walled kernel (os-dev-report 6189071639 on objectstack#23546).

Where objectui reads these doors (read at objectui main d25585c, not driven in a browser)

  • Setup's Access Control group (Positions, Capabilities, Permission Sets) opens the metadata-admin editors under setup.access. Under a wall they are read-only (useWalledCatalogReadOnly, packages/app-shell/src/views/metadata-admin/catalog-scope.ts:136; objectui#12183), not hidden.
  • ResourceEditPage.tsx: the History sheet (HistoryPanel) and the Audit sheet (AuditPanel.tsx:133, client.audit) appear under !createMode, with no capability gate.
  • PermissionMatrixEditor.tsx: the History button routes to ResourceHistoryPage.tsx:162 (client.history), with no capability gate.

Acceptance (a proposal; the lane's call)

  • The History and Audit entries render only when the caller holds studio.access or manage_metadata, or is system. Read the caller's capabilities the way the app shell already gates other Studio-only actions; find that idiom rather than adding a new one.
  • A Setup user with setup.access only sees neither entry, under a wall or not. A studio.access author sees both, as today.
  • The pin bump follows through the repo's bump script, after objectstack#23546's PR lands, so that the server and the UI agree from one release.

Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade · enhancement · priority:p1 · target:v18 · domain:ui · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-15T11:59Z

    Read fresh. This is the objectui companion that the maintainer's ruling on objectstack-ai/objectstack#23546 (6190477105, Q1 A1, /diff included) asks for.

    • Setup's catalog editors show History and Audit to a walled organization admin (ResourceEditPage.tsx, PermissionMatrixEditor.tsx, with no capability gate).
    • Those doors are narrowing to system, studio.access and manage_metadata.

    Grade p1 + target:v18: it is the consumer follow-up of a protocol-class narrowing, and without it users meet buttons that always answer 403 (A2's consequence, which the ruling chose A1 to avoid).

    Lane domain:ui (packages/app-shell, metadata-admin).

    Not blocked, despite the body's Blocked-by: line:

    • Hiding the buttons from callers the narrowed doors refuse only removes what the ruling removes, so the two PRs may land in either order.
    • The claimant links the objectstack PR (domain:cli seat 2, under claim 6188504102) and aims to land in the same window, as the ruling asks.

    Scope:

    • History, Audit and the diff entry are shown only to a caller holding system, studio.access or manage_metadata.
    • Pins:
      • a setup.access-only caller sees neither button;
      • a studio.access author sees both;
      • CONTROL: create mode is unchanged.
  2. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_01XVp19E1MYUTFM3dnkwz8gB
    Account: baozhoutao (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-12475-trail-doors-gate
    Worktree: objectui-issue-12475
    Domain: domain:ui
    Seat: domain:ui#2
    File surface: packages/app-shell/src/views/metadata-admin/ResourceEditPage.tsx (the History and Audit entries only), packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.tsx (its History button only), any diff entry that reads /meta/:type/:name/diff from these editors, the existing capability-reading idiom's module only to reuse it (⛔ no new gate idiom), the tests beside each, and one .changeset/12475-*.md (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: n/a — not a defect card
    Thread-read: 6191531169
    Serial constraints cleared: no in-flight claim or open PR touches ResourceEditPage.tsx or PermissionMatrixEditor.tsx. objectui#12440 (PR #12473, ListView.tsx / ObjectView.tsx), objectui#12464 (PR #12485, types / page.tsx / the console sample) and objectui#12478 (claimed alongside, one app-shell test) are disjoint. The pin bump the body mentions is not this card's: triage scoped it to the buttons, and they may land in either order with objectstack-ai/objectstack#23546.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 12475,
      "status": "done",
      "branch": "claude/issue-12475-trail-doors-gate",
      "pr": "https://github.com/objectstack-ai/objectui/pull/12495",
      "session": "session_01XVp19E1MYUTFM3dnkwz8gB — subagent, parent's id",
      "premise_still_valid": true,
      "summary": "Added one predicate, useCanReadAuthoringTrail(), in packages/app-shell/src/hooks/useCanAuthorMetadata.ts beside useCanAuthorMetadata. It uses the same usePermissions().hasCapabilities idiom and answers ANY of studio.access or manage_metadata; an unreported capability set fails open. ResourceEditPage now renders the History and Audit buttons and both sheets only under !createMode && canReadTrail. PermissionMatrixEditor renders its History button (routed and embedded) and the embedded sheet only under canReadTrail. A1 held, with one difference: in the current PermissionMatrixEditor, embedded mode opens HistoryPanel in a sheet, and standalone mode navigates to ./history. No entry in these editors calls /meta/:type/:name/diff. DraftReviewPanel and LayeredDiff compare locally from the layered read. `system` is an isSystem principal flag that /auth/me/permissions does not report, so it has no client half. File-surface note: three existing tests mocked the hook module without spreading the original module (ResourceEditPage.setupCatalog-7611, PermissionMatrixEditor.setupCatalog-7611, PermissionMatrixEditor.cloneToCustomize). Each now spreads importOriginal(); that is a 2-line edit per file. A3: the routed MetadataResourceHistoryPage is unguarded. Reached by URL, a setup-only caller sees HistoryPanel's error state with the server's 403 message, and no toast. Guarding it needs a refusal render, which is not a one-line reuse, so it is left as is and reported.",
      "tests": "All at head b5b00b7f0. (1) Pins: `pnpm exec vitest run --maxWorkers=2 packages/app-shell/src/views/metadata-admin/trailDoorsGate-12475.test.tsx` gives Tests 7 passed (7). (2) Ablation via objectstack scripts/ablation-replace.mjs: the predicate body becomes `return true`. The tool reported 'ok mutation landed: anchor 1 to 0, blob a310f6a66200 to cfd334cfc0c0', then Tests 2 failed | 5 passed (7). The failures were 'a setup.access-only caller sees neither entry' and 'a setup.access-only caller sees no History, routed or embedded'. The tool then reported 'ok restored: blob == HEAD (a310f6a66200) and git diff HEAD is empty'. Tests import the source, so no dist rebuild was needed. A first draft pinned the ?tab=audit deep link; that pin stayed green under the ablation, the deep link was found dead on main, and the pin was dropped. (3) Closure build `pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build`: VERDICT command-exit 0. (4) `pnpm --filter @object-ui/app-shell run type-check` (tsc --noEmit plus tsconfig.test.json, which includes *.test.tsx): VERDICT command-exit 0. (5) `pnpm exec vitest run --maxWorkers=2 packages/app-shell/src/views/metadata-admin/ packages/app-shell/src/hooks/`: Test Files 557 passed (557), Tests 6184 passed | 1 skipped, VERDICT command-exit 0, lock held 28m11s on a shared box. (6) Lint narrowed to the 7 changed files with the package's own `eslint .` config (inline config on): exit 0, eslint --format json counts 7 files, 0 errors, 0 messages on changed lines. The config has no projectService or type-checked preset, so the diff cannot move a verdict on an untouched file. Run with --no-inline-config, it shows 3 errors at the existing eslint-disable sites for react-hooks/static-components in ResourceEditPage; those lines are untouched. (7) Pre-tsc battery, all exit 0: check:phantom-deps, self-import, unreferenced-sources, doc-example-readers, handler-key-reads, side-effects-array, element-data-source-declaration, spec-symbols, action-forward-parity, designer-field-key-parity, icon-record-names, i18n-keys, i18n-drift, i18n-designer-parity, control-bytes. check-changeset-fixed exit 0; check-changeset-presence exit 0. check:spec-floors exits 1 with no-artifact; it is known red on main (objectui#12334) and is recorded here, not fixed. CI: in_progress, not awaited.",
      "mcp_calls": "0 — no MCP tools used",
      "api_writes": "1 — fleet-write relay pr_create (POST /repos/objectstack-ai/objectui/pulls, draft, plus POST /repos/objectstack-ai/objectui/issues/12495/assignees baozhoutao). Read-back: 5865 bytes identical. This report comment is the second write (POST /repos/objectstack-ai/objectui/issues/12475/comments). Labels: zero writes, none named by the dispatch. git pushes are not REST writes: 1 empty branch push and 3 commit pushes.",
      "open_questions": [],
      "out_of_scope_findings": [
        "class: a · reach: public door is the console URL `?tab=audit`, `layers`, `references` or `related` on a metadata item page, measured with the real MetadataResourceEditPage in a vitest dom env, not a browser: window.location.search was ?tab=audit before mount and the reader effect logged an empty search · evidence: ResourceEditPage declares the effect that syncs openSheet into ?tab= (the comment 'keep the URL `?tab=` query in sync so deep-links round-trip') before the first-mount effect that reads ?tab=. On mount the sync runs first with openSheet null and deletes tab, so the reader sees nothing and the deep link opens no sheet for any caller. The links the page writes for refresh or share therefore do not round-trip · dedupe words: ResourceEditPage tab deep link, openSheet replaceState, ?tab=references, deep-link round-trip",
        "carrier: 承接者:无 · routed MetadataResourceHistoryPage is unguarded for a setup-only caller (shows HistoryPanel's error with the server 403 message). With the entry hidden, no editor leads there. Noted in the PR's Acceptance notes; no card filed."
      ]
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Review: ACCEPT — objectui PR #12495 · domain:ui seat 2 · session_01XVp19E1MYUTFM3dnkwz8gB · 2026-10-15T13:47Z

    Reviewed on GitHub at head b5b00b7, against triage's scope (6191531169) and the claim (6191990090), not against the dev's report.

    • Shape: the PR is a draft against main. Its body opens Fixes #12475, the only closing keyword, followed by Clause-②: no, and it names rest/runtime: under a wall, the /meta history, audit and package-commits doors admit setup.access, which organization admins hold, so an organization admin reads the environment-wide authoring trail of any item objectstack#23546 as the server half. The governed sweep reads NOT governed, 8 files.
    • One predicate, the existing idiom:
      • useCanReadAuthoringTrail() sits beside useCanAuthorMetadata in hooks/useCanAuthorMetadata.ts. It answers ANY of studio.access and manage_metadata through the same usePermissions().hasCapabilities idiom, asking each capability on its own because hasCapabilities answers "holds all".
      • Unknown fails open, as useCanAuthorMetadata does, and the server enforces regardless.
      • system is a principal flag that /auth/me/permissions does not report, so it has no client half.
      • The module is not re-exported from the package entry (exports is . and ./styles.css, and src/index.ts names neither symbol), so Clause-②: no holds.
    • The gated entries:
      • ResourceEditPage: the History and Audit buttons AND both sheets render only under !createMode && canReadTrail, so the CONTROL (create mode) is unchanged.
      • PermissionMatrixEditor: the History button (routed and embedded) and the embedded History sheet render only under canReadTrail.
      • No entry in these editors calls /meta/:type/:name/diff; DraftReviewPanel and LayeredDiff compare locally from the layered read.
    • Pins and ablation: 7 pins. A setup.access-only caller sees neither entry, routed or embedded; a studio.access author and a manage_metadata author see both; create mode is unchanged. Making the predicate return true fails the 2 setup-only pins. Restore was proven by blob hash.
    • Test harness edits: three existing tests mocked the hook module without spreading the original. Each now spreads importOriginal(), a 2-line edit with no assertion changed.
    • Changeset: @object-ui/app-shell: patch.
    • Evidence: metadata-admin/ plus hooks/ give 6184 passed and 1 skipped under the lock. The closure build and the app-shell type-check exit 0, and so does the battery.
    • Acceptance note (not filed): the routed MetadataResourceHistoryPage is unguarded. A setup-only caller who reaches it by URL sees HistoryPanel's error state with the server's 403 message. No editor leads there any more.
    • Filed from the dev report (class a): objectui#12496. A ?tab=<sheet> deep link on a metadata item page never opens its sheet, because the URL-sync effect deletes tab before the first-mount reader runs.
    • Landing order: triage ruled the two halves may land in either order with rest/runtime: under a wall, the /meta history, audit and package-commits doors admit setup.access, which organization admins hold, so an organization admin reads the environment-wide authoring trail of any item objectstack#23546.
    • CI at head: 34 success, 3 skipped, 0 failures (filter=latest, 37 runs), all 16 test shards included.

    Next: pr_ready + automerge_enable.


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:ui seat 2 · session_01XVp19E1MYUTFM3dnkwz8gB · 2026-10-15T13:59Z


    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 seatenhancementNew feature or requestpriority:p1target:v18

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions