Skip to content

Publish opens the pending-changes sheet in a confirmation posture; Changes opens it in review posture #12517

Closed

Description

@objectstack-fleet

Source

Measured on objectui main d4bc81eab, 2026-10-15: the shell header's Changes button and its Publish button open the same right-hand sheet, "Pending changes — What publishing will change. New items are added; updates overwrite the live version", with one collapsed row per item ("OBJECT · 1 › Ticket st_ticket · New") and a footer "Publishing releases the 1 pending draft of this package atomically · Publish all". Nothing distinguishes the two entries, so Publish is a second Changes button.

Target

  1. Publish opens the sheet in a confirmation posture: every item expanded to show what changes, a summary line above the list ("3 items · 1 object · 1 app · 1 page"), focus on Publish all.
  2. Changes opens it in a review posture: items collapsed, focus on the list, the footer button present but not focused.
  3. One sheet, one component, one posture prop.

File surface

packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx (the pending-changes sheet and the two triggers), tests beside.

Acceptance

  1. Tests: Publish opens with rows expanded and focus on Publish all; Changes opens collapsed.
  2. Screenshots of both postures, light and dark.
  3. StudioDesignSurface.*.test.tsx green.

Constraints

  • objectui#12303: when nothing is pending, Publish stays disabled with its tooltip reason (publish-none-reason).
  • Existing data-testids (studio-changes-count) stay.

Filed by the epic PM session session_cdb7a717-def7-4e71-9988-f089fbaa18ff from the 2026-10-15 Studio test pass: objectui main d4bc81eab, HotCRM 63db83e2, the vite console on :5180 against objectstack serve --dev on :4001, viewport 1482 × 1119, dark theme with a light-theme check.

Evidence

Screenshots from the test pass, kept on the temporary branch claude/studio-test-pass-evidence-20261015 of objectstack-ai/objectui (commit 72115588c, never merged: the maintainer ruled that screenshots do not enter main, and asked for a temporary branch referenced from the card). The branch's docs/design/studio-redesign/findings/2026-10-15/README.md maps every file to its card. Pane captures, 800 × 604 of a 1482 × 1119 viewport, dark unless named light.

changes sheet
docs/design/studio-redesign/findings/2026-10-15/12517-changes-sheet.jpg — changes sheet.

publish opens same sheet
docs/design/studio-redesign/findings/2026-10-15/12517-publish-opens-same-sheet.jpg — publish opens same sheet.

Parent: #12446

Body refreshed 2026-10-15T15:19Z by the epic PM session: an Evidence section with the test-pass screenshots added.

Activity

  1. added
    enhancementNew feature or request
    pm:epicParent delegated to a dedicated epic PM — other PMs never dispatch into its subtree
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    area:studioChanging a running app without code — authoring, publish, docs and the portal
    and removed
    pm:epicParent delegated to a dedicated epic PM — other PMs never dispatch into its subtree
    on Oct 15, 2026
  2. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 (domain:ui lane queue; an objectui#12446 test-pass card)
    Session: session_01SJn1GEkrbbBQkhGC26wEDw
    Account: os-sales (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-12517-publish-posture
    Worktree: objectui-issue-12517 (in the cloud session's own container)
    Domain: domain:ui
    Seat: domain:ui#4
    File surface: packages/app-shell/src/preview/DraftChangesPanel.tsx (the producer: a posture prop, the seeded expansion, initial focus, the summary line; the card's surface named only the host), packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx (only the Publish and Changes triggers, a posture state beside changesOpen, and the panel mount's props), metadata-admin/i18n.ts or the panel's own i18n source (new keys only), new tests beside them, one .changeset/12517-*.md (stop on breach; explain in the report)
    Container & model: M, mode:cloud, model: opus (cloud on the maintainer's stance 「可以的话,ui 后续开发尽量用云 session,并且要求截图给我看」; lane scope on 「你也可以接ui车道的卡」; batch 5 on 「上限提到 5,提速」, this session's chat, 2026-10-15)
    Clause-②: no
    Responsibility: n/a — not a defect card
    Thread-read: none
    Serial constraints cleared: objectui#12510 (its DraftChangesPanel.accessibleNames-12510 pin on this card's sheet) landed as PR objectui#12580 (3e09c1e). In flight on StudioDesignSurface.tsx, this seat: objectui#12513 (the home button and PackageSwitcher in the same shell header), objectui#12524 (the four item headers) and objectui#12516 (the DataPillar add-field handler). Any textual overlap is resolved by merging origin/main.

  3. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 12517,
      "status": "done",
      "branch": "claude/issue-12517-publish-posture",
      "pr": null,
      "session": "session_01MxLBRTq32u4PebqJCjFXW7 — this run's id (Claude-Session trailer on every commit)",
      "premise_still_valid": true,
      "summary": "DraftChangesPanel takes a `posture` prop ('review' | 'confirm', default 'review'). 'confirm' expands every row once the list arrives, puts a summary line above the list ('4 items · 2 object · 1 app · 1 page', counted off the panel's existing per-type grouping) and moves focus to Publish all. 'review' keeps rows collapsed with focus on the list, Publish all present but unfocused, and no summary line. StudioDesignSurface keeps a posture state beside changesOpen: Publish sets 'confirm', Changes sets 'review', and the one mount passes it. Home, DraftPreviewBar and CommitTimeline pass nothing and keep the review behaviour. Only the footer button publishes (objectui#2261). objectui#12303's disabled-with-reason Publish and the existing data-testids are unchanged. PM mechanism assumptions, measured on 3e09c1e: (1) holds; the mount and both triggers are where stated, both calling setChangesOpen(true). (2) partly falsified. Seeding the expansion 'when the sheet opens' and focusing Publish all at open cannot work, because the list and its footer button render only after the async `_drafts` load. So the seed and the focus move run on the first list arrival of each open: rows are seeded expanded once, the focus moves only if the author has not moved it, and both run in layout effects. The Sheet is Radix and takes onOpenAutoFocus. Today's focus was read from the code, not measured live: with no handler, Radix focuses the first tabbable, and while the list loads that is the close button. (3) holds in part: the panel groups by type (the 'OBJECT · 2' headings), but those headings use the raw canonical type, and the panel has no type-label table. The summary reuses the same grouping and the same type token, with no second classification. (4) holds. Load-dependent flake: in the 97-file targeted run, the first version of the confirm test failed once. The cause was real: the expansion was seeded in a passive effect, so the commit that first rendered the list showed it collapsed for one frame before it expanded. Fixed in ad97f9d (useLayoutEffect) and pinned with a MutationObserver assertion that no collapsed row is ever observed. The i18n untranslated-identity pin (objectui#4376) flagged the ru value as byte-identical to en; ru now has its own order (459d12b).",
      "tests": "New: packages/app-shell/src/preview/__tests__/DraftChangesPanel.posture-12517.test.tsx (5 tests: confirm expands every row, never shows a collapsed one, counts the summary by type and focuses Publish all; review is collapsed with focus on the list, Publish all present and unfocused, no summary; CONTROL: a mount with no posture prop, as Home, DraftPreviewBar and CommitTimeline mount it, reads as review; a row the author collapses stays collapsed while the rows classify; the posture is re-read on each open). packages/app-shell/src/views/studio-design/StudioDesignSurface.publishPosture-12517.test.tsx (4 tests: Publish opens the one sheet as confirm and writes nothing; Changes opens it as review; Publish then Changes reads review the second time; with nothing pending Publish stays disabled, aria-describedby points at publish-none-reason and there is no count pill). Runs on the final head 737592f: `pnpm exec vitest run packages/app-shell/src/views/studio-design/StudioDesignSurface.` → Test Files 82 passed (82), Tests 560 passed (560), exit 0. `pnpm exec vitest run packages/app-shell/src/preview/__tests__/DraftChangesPanel packages/app-shell/src/preview/__tests__/DraftPreviewBar packages/app-shell/src/preview/__tests__/CommitTimeline packages/i18n/` → Test Files 98 passed (98), Tests 1413 passed / 13 skipped, exit 0 (includes DraftChangesPanel.accessibleNames-12510). Full suite, once, on ad97f9d: `pnpm --filter @object-ui/app-shell test` → SUITE_EXIT=0, Test Files 1427 passed / 1 skipped (1428), Tests 13553 passed / 9 skipped, 3158.86s on a 4-core box. Declared narrowing: after that run, 459d12b (one ru locale value) and the merge of origin/main 1b3b5b1 landed; the full suite was not re-run on 737592f, only the two batches above. Ablations (each committed first, mutated with an anchored replace, landing shown by grep -c, restored by `git checkout HEAD --` under an EXIT trap, `git diff HEAD` 0 lines after each): A1, the seed moved back to a passive useEffect (useLayoutEffect count 2→1) → posture-12517 'confirm …' test red (1 failed / 4 passed). A2, the confirm seed removed (injected 1, removed 0 left) → 3 failed / 2 passed. A3, Publish sets 'review' (confirm 0 left) → publishPosture-12517 2 failed / 2 passed. All restored; final `git diff HEAD` empty. The objectui#4376 pin that went red on ru is an existing pin I did not edit, so it was not ablated.",
      "gates": "On 737592f, exit codes captured before any pipe. `turbo run build --filter=@object-ui/app-shell... --concurrency=2` → 29/29 successful, then `pnpm --filter @object-ui/i18n --filter @object-ui/app-shell run type-check` → both 'Done', lock VERDICT command-exit 0. `pnpm check:i18n-keys` 0, `pnpm check:i18n-drift` 0, `pnpm check:i18n-designer-parity` 0, `pnpm check:new-line-citations` 0 (VERDICT 0 new citations), `node scripts/check-changeset-presence.mjs` 0 ('14 source file(s) of 2 released package(s) changed … declares 1 changeset'). `eslint --no-inline-config` on the touched .ts/.tsx files: exit 0, 0 errors, 19 warnings. The new code adds three of the file's existing warning kinds (set-state-in-effect twice, exhaustive-deps once on the open-keyed effect); warnings are not gated (lint.yml sets no --max-warnings). `pnpm check:i18n-dead-keys` not run: no key retired. Control-character scan `grep -naP` over the changed files: no hits. scripts/check-nul-bytes.mjs and the ablation helper scripts do not exist in objectui (MODULE_NOT_FOUND, which is not a gate result). objectui has no scripts/pm/dispatch-gates.mjs, so the --ran reconciliation does not apply and the gate list was derived by hand. Heavy runs went through /home/user/objectstack-ai/objectstack/scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=12517. The full-suite hold was 3163s, and the lock printed a holder-starvation warning. CI check-runs: none yet, because no PR is open (the PM opens it).",
      "mcp_calls": "5 — claude-code-remote add_repo ×2 (objectstack, hotcrm; access read, both answered read_available with nothing attached); github issue_read ×3 (objectui#12517 get and get_comments, objectui#12446 get_comments). No write tool.",
      "api_writes": "0 REST writes. git push only: claude/issue-12517-publish-posture (empty-branch probe, then ac17a0f, ad97f9d, 737592f) and claude/issue-12517-evidence (a4de5c7). Local-only writes against my own --fresh HotCRM backend (POST /api/v1/packages; PUT /api/v1/meta/TYPE/NAME?mode=draft) to stage the screenshots; nothing on GitHub.",
      "open_questions": [
        {
          "question": "Summary line wording: the type segments use the raw canonical type token ('2 object'), not localized or pluralized nouns, because the panel has no type-label table and sits on the console's eager graph (it cannot import the metadata-admin table). Accept, or add type labels?",
          "options": [
            "A: keep the raw token, the same one the group headings show (shipped)",
            "B: add a localized type-noun table to the panel, with plural forms in 10 locales"
          ],
          "recommendation": "A. Business need: today the only reader is this summary line, and the headings above it already show the raw token, so B has no measured pull. Long-term soundness: one token across headings and summary, with no second classification. Guarding against AI mistakes: nothing changes, since this adds no metadata key. Startup focus: B is a new label surface with zero other consumers."
        },
        {
          "question": "Conflicts between the dispatch and os-dev.md, recorded rather than resolved silently. (1) os-dev.md says the dev opens the draft PR and posts the report as a card comment; the dispatch's degraded channel forbids both. I followed the dispatch, since the relay is refused here. (2) objectui AGENTS.md names the lock at /home/user/objectstack/scripts/pm/os-verify-lock.sh; this container's read-only clone is at /home/user/objectstack-ai/objectstack, so I used that path. (3) Commits carry the AGENTS.md model-free trailer pair (Claude-Session plus 'Co-authored-by: Claude'), not the harness's model-named Co-Authored-By line; the repo rule takes precedence over the harness reminder.",
          "options": [
            "A: as done",
            "B: the seat says otherwise"
          ],
          "recommendation": "A, because os-dev.md says the dispatch's carve-outs are to be reported, and both AGENTS.md rules take precedence over harness defaults."
        },
        {
          "question": "Footprint beyond the claim's wording, for the seat to accept. StudioDesignSurface.tsx also changed its DraftChangesPanel import line (adding `type DraftChangesPosture`). The panel's strings live in packages/i18n/src/locales/*.ts (all 10 locales, new keys only), so @object-ui/i18n is in the changeset. The confirm posture also costs two item reads per draft at open, because each expanded row fetches its own detail; the review posture still reads lazily on expand.",
          "options": [
            "A: accept",
            "B: narrow"
          ],
          "recommendation": "A. Each is the smallest change that meets target 1, and none touches another card's region."
        }
      ],
      "out_of_scope_findings": [
        "class: a · reach: public door, the Studio pending-changes sheet against a local HotCRM backend. For a NEW object draft, the drill-in lists `_packageId` under 'Also changed', as a key the author added (visible in 12517-publish-confirm-light.png on the evidence branch). That is a framework-internal key presented as the author's edit, the same shape objectui#8181 removed for `_draft`/`_diagnostics` through stripReadDecorations. Not verified: whether `_packageId` belongs on the spec's METADATA_READ_DECORATIONS (fix at the producer or spec) or the panel should drop it (that would be a consumer-side filter, which contract-first rules out). · dedupe words: _packageId, DraftChangesPanel Also changed, stripReadDecorations, METADATA_READ_DECORATIONS, read decorations",
        "carrier: none. Noted in Acceptance notes only, not filed: in dark mode, the sheet's New and Update badges keep their light emerald-50 and amber-50 fills (no dark: variants); pre-existing polish."
      ],
      "deviations": [
        "The summary line appears only in the confirm posture; target 1 names it there, and review hosts must not change.",
        "Focus in the confirm posture reaches Publish all when the list first arrives, not at the instant the sheet opens, because the button does not exist before then. Until then focus sits on the list (tabIndex -1, named 'Pending changes'). Programmatic focus shows no focus-visible ring; the screenshots record the active element from the DOM instead.",
        "The type segments use the raw type token, and the word order differs by locale: '{{n}} {{type}}' in en/de/es/fr/pt, '{{type}} {{n}}' in zh/ja/ko/ar, and '{{type}}: {{n}}' in ru, which also keeps ru from being byte-identical to en.",
        "The local branch claude/issue-12517-publish-posture already existed in the shared checkout, with no commits of its own, at 3e09c1e. I detached that checkout and deleted the branch before `git worktree add`, as the dispatch allows."
      ],
      "files_changed": [
        ".changeset/12517-publish-confirm-posture.md",
        "packages/app-shell/src/preview/DraftChangesPanel.tsx",
        "packages/app-shell/src/preview/__tests__/DraftChangesPanel.posture-12517.test.tsx",
        "packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx",
        "packages/app-shell/src/views/studio-design/StudioDesignSurface.publishPosture-12517.test.tsx",
        "packages/i18n/src/locales/{ar,de,en,es,fr,ja,ko,pt,ru,zh}.ts"
      ],
      "screenshots": [
        "https://github.com/objectstack-ai/objectui/blob/a4de5c7d69509c1521ed0f5047f8671417d2da9d/docs/design/studio-redesign/findings/12517/12517-publish-confirm-dark.png?raw=true",
        "https://github.com/objectstack-ai/objectui/blob/a4de5c7d69509c1521ed0f5047f8671417d2da9d/docs/design/studio-redesign/findings/12517/12517-publish-confirm-light.png?raw=true",
        "https://github.com/objectstack-ai/objectui/blob/a4de5c7d69509c1521ed0f5047f8671417d2da9d/docs/design/studio-redesign/findings/12517/12517-changes-review-dark.png?raw=true",
        "https://github.com/objectstack-ai/objectui/blob/a4de5c7d69509c1521ed0f5047f8671417d2da9d/docs/design/studio-redesign/findings/12517/12517-changes-review-light.png?raw=true"
      ],
      "line_budget": "n/a",
      "pr_title_draft": "feat(app-shell): Publish opens the pending-changes sheet to confirm, Changes opens it to review (objectui#12517)",
      "pr_body_draft": "(elided by the seat: opened verbatim as the body of PR objectstack-ai/objectui#12595, read back identical)"
    }

    Relayed verbatim by seat domain:ui#4 (session session_01SJn1GEkrbbBQkhGC26wEDw) from the cloud session's final message, the degraded write channel. The seat opened the draft PR #12595 from pr_title_draft and pr_body_draft, with assignee os-sales. "pr": null above is the session's own value at report time.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · PR #12595 merged as 92c2a69 on main · read 2026-10-16T01:52Z

    probe on origin/main 92c2a69 reading control
    export type DraftChangesPosture in preview/DraftChangesPanel.tsx 1 the one posture prop
    changesPosture in StudioDesignSurface.tsx 2 lines (the state and the mount's prop) the two triggers set it
    summaryItems in packages/i18n/src/locales/en.ts 3 lines (with its plural forms) —
    DraftChangesPanel.posture-12517.test.tsx, StudioDesignSurface.publishPosture-12517.test.tsx, .changeset/12517-publish-confirm-posture.md present —

    State change: the card closed as completed through Fixes #12517, and pm:dispatched is removed in this act. The cloud session session_01MxLBRTq32u4PebqJCjFXW7 is archived. Its out-of-scope finding is #12594.


    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

area:studioChanging a running app without code — authoring, publish, docs and the portaldomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatenhancementNew feature or requestpriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions