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.

studio(changes): the Changes panel shows no diff, marks a no-op draft as Update, and publish gives no confirmation #11807

Closed

Description

@objectstack-fleet

Filing gate ② — a product decision only the maintainer can make.

Who acts on it: objectui triage → the Studio / app-shell owner. ⛔ Not a claim. Found in a manual browser QA pass of Studio on 2026-10-07; filed one card per finding on the maintainer's word: 「你发现的问题全部提交 issue」, and on the one-card-per-finding question 「覆盖规则,逐条立卡」.

Maintainer quick-read

The Changes panel lists only machine names with New/Update, and publishing gives no confirmation. Show what changed?

Seen

  • Changes: OBJECT · 1 repairs_repair_ticket Update — no field-level diff, label, or author.
  • A draft identical to the live version is listed as Update.
  • Publish all takes ~5 s and shows no success message; only the header text changes to "No drafts pending publish". The Form tab keeps the caption "Draft layout" after publish.

Options

  • A — per-item diff (added/removed/changed keys), hide no-op drafts, a "Published N items" toast.
  • B — toast + hide no-ops only.
  • C — status quo.

Environment

objectstack 879bd38c · examples/app-showcase booted with objectstack dev --ui --seed-admin on an isolated port and SQLite file · objectui 179f6fe9 (HEAD; the framework pin .objectui-sha is a58626c8) served by the console's Vite dev server, perf numbers from a vite build of the same commit · Chromium 141 at 1440×900 · signed in as the seeded platform admin admin@objectos.ai unless stated.

Duplicate check

Dedupe words: changes panel diff · no-op draft update · publish success feedback

Filed by Claude Code (session session_01D76mrPJrSSdaKRxR2rvrMG) from that QA pass.


Generated by Claude Code

Activity

  1. added
    enhancementNew feature or request
    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
    on Oct 7, 2026
  2. objectstack-fleet commented on Oct 7, 2026

    @objectstack-fleet
    ContributorAuthor

    Path: write metadata — the author sees what publishing will change | 缺项 | P3

    Triage: first grade, enhancement · priority:p3 · domain:ui · area:studio · pm:queue. Filed as ②, but routed as a Studio UX improvement: no contract or product question is open. Direction: the body's option A

    Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-07T17:30Z. ⛔ Not a claim, ⛔ not a dispatch.

    Triage: lands in packages/app-shell/src/preview/DraftChangesPanel.tsx and draftStatus.ts ⇒ domain:ui; rationale: a Studio or console surface in objectui. Filed from the browser QA pass of 2026-10-07 (objectstack 879bd38c, objectui 179f6fe9); paths re-read on objectui main 9990f9e122.

    • Why not the decision box: a diff, hiding no-op drafts and a toast change nothing a server stores. Why p3: publishing works; the author cannot see what it will do.
    • Direction:
      • a per-item diff of added, removed and changed keys (reuse LayeredDiff.tsx where it fits)
      • a draft equal to the published item is not listed as Update
      • a "Published N items" toast
    • Clause-②: no. Patch changeset in objectui.
  3. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_01MgfduSkFrfM3eorB3UGfAU
    Account: os-bill
    Branch: claude/issue-11807-changes-panel-diff
    Worktree: objectui-issue-11807
    Domain: domain:ui
    Seat: domain:ui#2
    File surface (line numbers on b6f8fc8):

    • packages/app-shell/src/preview/DraftChangesPanel.tsx:
      • the new / update classification (kind, about :92–:150): a draft equal to its published item is not listed as Update;
      • the per-item drill-in (computeChangeDetail, about :216–:300): it shows added, removed and changed keys. LayeredDiff.tsx is reused by import where it fits, never edited.
    • packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx: only doPublish's success path (about :1271–:1365), for a toast that names how many items were published. The dev first measures whether the existing engine.studio.publishedAll toast (about :1352) already answers the card.
    • packages/app-shell/src/preview/usePublishAllDrafts.ts: only its success toast, if Home and the preview bar need the same count.
    • packages/app-shell/src/views/metadata-admin/i18n.ts: only new or reworded engine.studio.changes.* / engine.studio.publishedAll* rows (en and zh).
    • The tests beside these, and .changeset/11807-*.md.

    ⛔ Not on it:

    • LayeredDiff.tsx and any draft-existence probe: seat 1's objectui#11799 holds the /layers call and the ?state=draft existence probes. fetchItemBody's draft read (about :180–:190) reads a body for the diff and is left as it is.
    • StudioDesignSurface.tsx outside doPublish. Seat 3's objectui#11823 holds InterfacesPillar (from about :2492), which is disjoint.
    • packages/components/src/ui/** and packages/i18n/**.

    Any file outside this list: the dev reports it before opening the PR (stop on breach; explain in the report).
    Container & model: M, mode:subagent, model: opus (dispatch-gates --tier --repo objectstack-ai/objectui over these paths: no path-derived mandate; default tier)
    Clause-②: no
    Responsibility: objectui app-shell Studio Changes panel: drafts are listed by machine name with New/Update only, a draft identical to the published item is listed as Update, and Publish all gives no count of what it published | the panel already fetches the published and draft bodies for its drill-in, so the comparison is local | every Studio author reviewing a publish, seen by the filer's 2026-10-07 browser QA pass on the showcase (OBJECT · repairs_repair_ticket Update with no change)
    Thread-read: 6043265917
    Serial constraints cleared: overlaps declared, not blocking.

    • objectui#11799 (seat 1, in flight): its surface is the /layers call in LayeredDiff.tsx and the draft-existence probes. This card imports LayeredDiff without editing it, and leaves fetchItemBody's request unchanged.
    • objectui#11823 (seat 3, in flight): StudioDesignSurface.tsx, InterfacesPillar only. This card's one hunk sits in doPublish, about 1,100 lines away.
    • objectui#11787 (queued, unclaimed): its "Changes count refreshes on every save" item is the header count, not this panel. Left to that card.
    • In-flight claims read 2026-10-08T05:18Z: none names DraftChangesPanel.tsx, draftStatus.ts or usePublishAllDrafts.ts.
    • Open objectui PRs, read 2026-10-08T05:18Z: none touches these files.

    Why Clause-②: no: this is Studio review UX. Nothing a server stores changes, and no export, prop or packages/i18n key is added. New strings live in app-shell's own metadata-admin/i18n.ts defaults table.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 11807,
    "status": "done",
    "branch": "claude/issue-11807-changes-panel-diff",
    "pr": "#11916",
    "session": "session_01MgfduSkFrfM3eorB3UGfAU (subagent mode: the parent's id, per claim 6052893161)",
    "premise_still_valid": true,
    "summary": "H0, measured on b6f8fc8 by rendering the real DraftChangesPanel and the real StudioDesignSurface (real sheet, real MetadataClient, mocked reads). Obs 1 was partly stale. The field-level drill-in has existed since 7283d18 (PR objectui#2271), but every other top-level key was one list: 'Also changed: description, icon, label'. Obs 2 reproduced: the row kept Update even after its drill-in printed 'No differences detected'. Obs 3 was stale: the success toast fires (since 20c1695, PR objectui#2203) but names no number. What changed: (1) computeChangeDetail returns keys {added, changed, removed}, and the drill-in prints them with the field rows' +/~/- marks. (2) Option (a): once an entry's drill-in finds both bodies present and equal (same stripReadDecorations normalisation), the row drops the Update badge and shows an Equal glyph whose accessible name is the existing preview.changes.detailNone sentence. A badge word would need a packages/i18n key, which is outside the surface. H1: deciding no-op at open costs one published body read per draft plus one draft body read per non-object draft, so it is O(drafts). (b) is falsified: a _drafts row carries only type, name, organizationId, packageId, updatedAt and updatedBy. The list read cannot stand in, because it is projected (doc content slim, object mask). (3) doPublish names N from the batch's published[] (declared by PublishPackageDraftsResponseSchema, returned by the client), with singular and plural rows; the package-less scope does the same. Zero answers now show info 'No drafts pending publish': outcome nothing_to_publish (was thrown as 'Action failed') and a package-less read with nothing pending (was the success toast). (4) A refused batch now names each refused item with its reason. Measured before, with the producer's envelope: toast.error 'Action failed'. Since e16f504 (objectui#10039) doPublish read the batch's own success === false before failed[]. This PR rewrites that same chain, so the shipped defect in touched lines is repaired here. Route deviations: no-op drafts stay in the counts, because the server still promotes them and they appear in published[], so the panel's counts agree with the toast's N. LayeredDiff's computeDiffRows is not imported, because that module imports the 456 KB metadata-admin i18n table and this panel is on the console's eager graph.",
    "tests": "All at HEAD 358e461 (git rev-parse --short HEAD after the last commit, equal to the remote tip). New tests: DraftChangesPanel.noChangeAndKeySplit-11807.test.tsx (7) and StudioDesignSurface.publishCount-11807.test.tsx (6). Updated: DraftChangesPanel.test.tsx (keys shape). Union run (preview/tests/, 4 Studio publish suites, column-identity.ratchet): 'Test Files 17 passed (17) / Tests 142 passed (142)'. Every test file naming StudioDesignSurface, DraftChangesPanel, usePublishAllDrafts or DraftPreviewBar (101 files; one scripts/ text-only match left out): 'Test Files 101 passed (101) / Tests 603 passed (603)'. pnpm --filter @object-ui/app-shell type-check: exit 0, after the app-shell^... build; tsconfig.test.json includes src/**/.test.tsx. Reverse verification, each leg on committed HEAD with restore proven by blob hash and an empty git diff HEAD. A1: the 3 source files at b6f8fc8, tests at HEAD, gave 'Tests 15 failed | 11 passed (26)', with the NEW/gone guards, the route tests and the panel controls green. A2: ablation-replace dropped the both-bodies guard, gave 'Tests 2 failed | 5 passed (7)' (exactly the two guard pins), anchor 1 to 0, blob c0164c9bc8cc to 35283355b155, restored. A3: the first attempt was a no-op; ablation-replace refused it because the replacement contained the anchor (1 to 1). It was re-run with a dropping anchor that sends a refusal through the success-false throw: 'Tests 1 failed | 5 passed (6)' (only the refusal pin), blob cf06c12f6e45 to c8f0dd0e9c84, restored. No dist preflight: the tests import from source.",
    "mcp_calls": "0",
    "api_writes": "3 REST writes, each one relay stroke (POST /repos/objectstack-ai/objectstack/dispatches, 204) executed as objectstack-fleet[bot]: (1) POST /repos/objectstack-ai/objectui/pulls, draft PR #11916, read back byte-identical (10108 bytes); (2) POST /repos//issues/11916/assignees, os-bill, via label-write.mjs, read back matches; (3) POST /repos//issues/11807/comments, this os-dev-report, via post-stamped.mjs. Plus git pushes of the branch (the empty-branch probe and 7 commits), which are not REST.",
    "open_questions": [],
    "out_of_scope_findings": [
    "class: a · reach: named producer: objectstack metadata-protocol publishPackageDrafts returns { success: false, outcome: 'refused', failed: [...] } with no error key, and the runtime dispatcher wraps it as { success: true, data } · evidence: PackagesPage's publishDrafts (r.success === false throws readEnvelopeFailureText(r) || 'Action failed' before it reads failedCount) and usePublishAllDrafts (success === false throws 'publish-drafts did not publish this package') both read the batch's own success before failed[]. MetadataClient.publishPackageDrafts unwraps the envelope (since objectui#6965 / PR objectui#10038), so a refused package publish from the Packages page or the Home / draft-preview bar loses the per-item reason. The Studio twin of this ordering was measured on the producer shape (toast.error 'Action failed') and repaired in PR #11916; these two sites are not on this card's surface · dedupe words: publish refused reason Action failed · publish-drafts failed[] success false · MetadataClient unwrap envelope refusal",
    "carrier: 承接者:无 · noted, not filed (no count is a missing nicety, not a defect) · usePublishAllDrafts (Home banner, draft-preview bar) still toasts 'Published! Your changes are live.' without a count; a count there needs a home.pendingDrafts.
    key in packages/i18n · dedupe words: home publish all toast count",
    "carrier: 承接者:无 · noted, not filed · a visible 'No change' badge word on the Changes panel row needs a preview.changes.* key in packages/i18n; this PR uses a named glyph instead · dedupe words: changes panel no change badge"
    ],
    "gates": [
    {
    "command": "pnpm exec vitest run packages/app-shell/src/preview/tests/ + StudioDesignSurface.{publishCount-11807,publishAdvisoryReachesAuthor,heldPublish-11786,packageLessFlows-11553}.test.tsx + packages/core/src/utils/tests/column-identity.ratchet.test.ts",
    "exit": 0,
    "verdict": "Test Files 17 passed (17) / Tests 142 passed (142)",
    "head": "358e4619a"
    },
    {
    "command": "pnpm exec vitest run (101 test files naming StudioDesignSurface / DraftChangesPanel / usePublishAllDrafts / DraftPreviewBar)",
    "exit": 0,
    "verdict": "Test Files 101 passed (101) / Tests 603 passed (603)",
    "head": "358e4619a"
    },
    {
    "command": "pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build && pnpm --filter @object-ui/app-shell type-check",
    "exit": 0,
    "verdict": "os-verify-lock VERDICT command-exit 0; type-check (tsc --noEmit && tsc -p tsconfig.test.json) exit 0",
    "head": "358e4619a"
    },
    {
    "command": "pnpm exec eslint (6 touched source and test files)",
    "exit": 0,
    "verdict": "0 errors, 15 warnings, none on a changed line (DraftChangesPanel: existing computeChangeDetail export and existing load effect; none inside doPublish)",
    "head": "358e4619a"
    },
    {
    "command": "node scripts/check-changeset-presence.mjs",
    "exit": 0,
    "verdict": "6 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/11807-changes-panel-diff.md.",
    "head": "358e4619a"
    },
    {
    "command": "node scripts/check-changeset-no-major.mjs",
    "exit": 0,
    "verdict": "No changeset declares a major bump.",
    "head": "358e4619a"
    },
    {
    "command": "pnpm check:new-line-citations",
    "exit": 0,
    "verdict": "VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0",
    "head": "358e4619a"
    },
    {
    "command": "pnpm check:control-bytes",
    "exit": 0,
    "verdict": "check-control-bytes: OK (scanned 7924 tracked text file(s); skipped 85 binary).",
    "head": "358e4619a"
    },
    {
    "command": "pnpm check:changeset-claims",
    "exit": 0,
    "verdict": "No pending changeset names a file this change touches.",
    "head": "358e4619a"
    },
    {
    "command": "pnpm check:i18n-keys",
    "exit": 0,
    "verdict": "Every in-scope call-site key resolves against the en pack (3300 keys) ...",
    "head": "358e4619a"
    },
    {
    "command": "pnpm check:i18n-drift",
    "exit": 0,
    "verdict": "No en value changed in this range. No designer-table en value changed in this range. (designer table: 4 key(s) added, 1 removed)",
    "head": "358e4619a"
    },
    {
    "command": "pnpm check:i18n-designer-parity",
    "exit": 0,
    "verdict": "Every en row has a zh row, and every shared row carries the same placeholders.",
    "head": "358e4619a"
    },
    {
    "command": "pnpm check:spec-symbols / check:vi-mock-specifiers / check:vi-mock-inherit / check:vi-mock-override-shape",
    "exit": 0,
    "verdict": "all OK",
    "head": "358e4619a"
    },
    {
    "command": "pnpm lint (repo-wide eslint) and the full pnpm test",
    "exit": null,
    "verdict": "NOT MEASURED: CI-owned runs; locally only the touched files were linted, and that narrowing is not claimed as a repo-wide reading",
    "head": "358e4619a"
    }
    ],
    "files_changed": [
    ".changeset/11807-changes-panel-diff.md",
    "packages/app-shell/src/preview/DraftChangesPanel.tsx",
    "packages/app-shell/src/preview/tests/DraftChangesPanel.noChangeAndKeySplit-11807.test.tsx",
    "packages/app-shell/src/preview/tests/DraftChangesPanel.test.tsx",
    "packages/app-shell/src/views/metadata-admin/i18n.ts",
    "packages/app-shell/src/views/studio-design/StudioDesignSurface.publishCount-11807.test.tsx",
    "packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx"
    ],
    "deviations": [
    "i18n rows: besides the new engine.studio.publishedAllCount(/One) and engine.studio.publishedAllFlows(/One) rows (en+zh), the now-unreferenced engine.studio.org.published row (en+zh) is removed. It is outside the claim's named engine.studio.publishedAll* / engine.studio.changes.* prefixes. No engine.studio.changes.* row was needed: the panel's strings live in packages/i18n (preview.changes.*), and the panel cannot import the 456 KB metadata-admin table without putting it on the eager graph.",
    "Route (a), with 'leave it out of the count' not followed: publish still promotes a no-op draft (it is in published[]), so the panel's counts stay 'drafts released' and agree with the toast's N.",
    "H2 'otherwise the panel's listed count' fallback not used: published[] is present on today's producer, and a response without it falls back to the count-less existing sentence.",
    "doPublish's refused-batch ordering (failed[] read before success === false) is repaired in this PR because the restructure touches those lines. It is outside the card's three observations and is declared in the PR body and the changeset.",
    "LayeredDiff.tsx is reused by neither import nor edit: the import would carry the metadata-admin i18n table onto the console's eager graph (reasoned from the import graph; eager-closure bytes NOT MEASURED).",
    "usePublishAllDrafts.ts is untouched: a count in its toast needs a packages/i18n key (out of surface).",
    "Commit trailers follow AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude), not the harness reminder's model-named Co-Authored-By."
    ]
    }


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    Review: ACCEPT — PR objectui#11916 · domain:ui seat 2 · 2026-10-08T06:32Z

    Implemented-by: os-dev subagent of session_01MgfduSkFrfM3eorB3UGfAU, branch claude/issue-11807-changes-panel-diff, head 358e461.
    Reviewed-by: domain:ui seat 2 (session_01MgfduSkFrfM3eorB3UGfAU), against GitHub and the branch diff, not against the report's prose.

    Checklist:

    • PR shape: draft, base main, first line Fixes #11807. No closing keyword sits next to another card. Clause-②: no is at the start of a line.

    • Scope: 7 files (+713 −46).

      • DraftChangesPanel.tsx with its tests.
      • StudioDesignSurface.tsx: all three hunks sit inside doPublish (about :1296–:1400), about 1,100 lines from seat 3's InterfacesPillar.
      • App-shell's metadata-admin/i18n.ts: new engine.studio.publishedAll* rows, en and zh. The surface is amended to cover removing engine.studio.org.published, which this change leaves unread.
      • The changeset.

      LayeredDiff.tsx and every draft-existence probe (seat 1's objectui#11799), usePublishAllDrafts.ts, packages/i18n/** and packages/components/src/ui/** are untouched.

    • H0, measured on b6f8fc8:

      1. The field-level drill-in already existed (PR objectui#2271), but every other key sat in one mixed list.
      2. A no-op draft kept Update even after its drill-in said "No differences detected".
      3. The success toast fired (PR objectui#2203) but named no number.

      Each is now answered:

      1. computeChangeDetail returns { added, changed, removed } keys, printed with the field rows' marks.
      2. Once a drill-in finds both bodies present and equal, under the same stripReadDecorations normalisation, the row shows an "equal" glyph named by the existing preview.changes.detailNone sentence.
      3. doPublish names N from the batch's own published[], which PublishPackageDraftsResponseSchema declares.
    • H1: deciding "no change" at open is O(drafts): one published read per draft, plus one draft read per non-object draft. Option (b) is falsified, because a _drafts row carries no equality field. Option (a), marking on expand, is taken.

    • Deviation, accepted: no-op drafts stay in the counts, because the server still promotes them and they appear in published[]. The panel's counts and the toast's N therefore agree.

    • Deviation, accepted, and repaired here: since objectui#10039, doPublish read the batch's success === false before failed[], so every refused publish read "Action failed". This PR restructures those same lines and restores the per-item reasons. It is declared in the PR body and the changeset. Measured before the change with the producer's envelope.

    • LayeredDiff not imported: that module pulls in the 456 KB metadata-admin string table, and this panel is on the console's first load. The key split is done locally instead.

    • Clause-②: no holds: no export, prop, @object-ui/types member or packages/i18n key. check:i18n-drift reports no en value changed in packages/i18n.

    • Changeset: @object-ui/app-shell: patch. Every sentence was checked against the diff.

    • Evidence:

      • The union run passes, 17 files and 142 tests, the column-identity ratchet included.

      • Every test naming the four modules passes, 101 files and 603 tests.

      • The type-check passes.

      • Reverse checks:

        • with the base sources, 15 of 26 fail;
        • dropping the both-bodies guard turns exactly its 2 pins red;
        • sending a refusal through the old ordering turns exactly the refusal pin red.

        Each restore was proven by blob hash.

    • CI on 358e461: no failure so far, 6 checks still running. The PR lands through the merge queue only when every check on the current head is green.

    Out-of-scope findings:

    • Filed: the same refused-publish ordering on the Packages page and the Home / draft-preview Publish all, as its own card.
    • Not filed, carrier: none: a count in usePublishAllDrafts's toast, and a visible "No change" badge word on the panel. Both need packages/i18n keys. They are niceties, not defects.

    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:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions