Repository navigation
feat(runtime,metadata-protocol,client,console): Studio publish takes a change note; item history names the person - #28422
Conversation
…nge note; item history names the person The package publish-drafts door forwards the body's `message` and records the request's authenticated user as the actor (a body `actor` is ignored). The protocol threads the note into every item promote, so each history row the publish writes carries it as `change_note`; the package commit keeps it. Studio's publish review offers an optional change note on the batch door, and the item history panel shows the actor's name (the id, marked unresolved, when no user row answers) and the change note under the row. Claude-Session: https://claude.ai/code/session_01L6bhgUAnBEBqJCebezxh8P Co-authored-by: Claude <noreply@anthropic.com>
…panel's names and notes Claude-Session: https://claude.ai/code/session_01L6bhgUAnBEBqJCebezxh8P Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L6bhgUAnBEBqJCebezxh8P Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 3 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 43 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 3dd1566002a43bdd29d794a5593b744c2b2d27b9 && git checkout 3dd1566002a43bdd29d794a5593b744c2b2d27b9
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 380a1078f153e628eeef2285a75bcf060df35905 5d035cf37ef8b022b0acbf016f107328495eebd7 && git checkout -B drift-repro 380a1078f153e628eeef2285a75bcf060df35905 && git merge --no-ff 5d035cf37ef8b022b0acbf016f107328495eebd7
node scripts/docs-audit/affected-docs.mjs --json 380a1078f153e628eeef2285a75bcf060df35905
|
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgUAnBEBqJCebezxh8P
|
domain:ui#5 · os-sales · session_01L6bhgUhKx1XHzkoikGjRB4 Contract reviewServed-tier: ① Derived judgmentsThe face is the ② Semver levelpatch for all six packages, as the changeset declares: a new optional key on a request body and an optional opts member, no removal. ③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
|
domain:ui#5 · os-sales · session_01L6bhgUhKx1XHzkoikGjRB4 ACCEPT · 2026-10-31T14:06Z 席位核对
Generated by Claude Code |
domain:ui#5 · os-sales · session_01L6bhgUAnBEBqJCebezxh8P
Requested by Sales · project thread
Part of #28406 — items 1, 2, 3 and 5. Item 4 (the publish-drafts request schema in
packages/spec, route ledger, OpenAPI) stays on the card for the spec lane, per the split note on the card.Clause-②: yes (widening)
Metadata-protocol: none
维护者速读
publish app package 'PACKAGE_ID',管理员挑版本回滚时看不出是谁、为什么改的;另外发布门把请求体里的actor当作操作者,持有manage_metadata的人可以冒名。actor被忽略;不写说明时行为与之前一致。回滚即还原本 PR,没有数据迁移。packages/runtime/src/domains/packages.ts(publish-drafts 门取会话身份、转发 message)、packages/metadata-protocol/src/protocol.ts(每项 promote 的 change_note)、ui/packages/app-shell/src/views/item-tools/historyActors.ts(姓名解析)。pnpm --filter @objectstack/runtime exec vitest run src/domains/packages-publish-drafts-change-note.integration.test.ts,预期 4 个用例全绿(真实 SQLite 里每条历史行带说明、记录会话用户)。What changes
Runtime door (
POST /packages/:id/publish-draftsonly). The door forwards a stringmessagefrom the body, and records the actor as the user id of the request's authorized execution context (resolvePackageWriteActor, the ruleRestServer.resolveMetaWriteActorholds for the/metawrite doors). A bodyactoris ignored; a context with no user id records no actor. The six sibling package doors are untouched (carried by #28414).Protocol.
publishPackageDraftspassesrequest.messageto eachpromoteDraftForPublishin place of the hard-codedpublish app package 'PACKAGE_ID', so everysys_metadata_historyrow the batch writes stores it aschange_note. The package commit already kept it. No note, or an empty one, keeps today's sentence.SDK.
packages.publishDrafts(id, opts)gainsopts.message.opts.actorstays in the type, marked@deprecated(the door ignores it), so no caller breaks.Console.
MetadataClient.publishPackageDraftspicksmessagebesidebackfilland sends it only when given (no options still sends{}).DraftChangesPanelfooter) offers an optional change note where it publishes one package as a batch (onPublish && packageId, the same condition as the backfill choice). Sent trimmed; an empty note sends nothing, and a sheet with neither a note nor a backfill choice still calls its host with no options.StudioDesignSurface.tsxis not edited: itsdoPublish(options)already hands the sheet's options topublishPackageDrafts(packageId, options).HistoryPanelshows each actor by name, read fromsys_useronce per set of ids (historyActors.ts). An id with no row (a deleted user), a failed or refused directory read, or no adapter shows the id marked "unresolved user". Anullactor shows none. The event'smessageis shown under the row;HistoryEventgainsmessage.engine.edit.historyBy/historyActorUnresolved/historyActorUnresolvedHintin the studio namespace (en, zh);preview.changes.noteLabel/notePlaceholderin all ten locale packs.Branch: the harness-assigned
claude/project-thread-64y985, notclaude/issue-28406-*; the card is #28406.Tests
packages/runtime/src/domains/packages-publish-drafts-change-note.integration.test.ts(new, real ObjectQL + SqlDriver over better-sqlite3, throughHttpDispatcher.handlePackages): a note reaches every history row and the commit, andhistoryMetaItemreads it back asmessagewith the session user asactor; no note keeps the sentence; a bodyactoris ignored and the session user is recorded on every row and the commit; a system context records no actor.ui/packages/app-shell/src/preview/__tests__/DraftChangesPanel.changeNote.test.tsx(new): the field is offered only on the batch door with a Publish, disabled while publishing; a note is sent trimmed as{ message }; empty or whitespace sends no options; the note rides beside a checked backfill choice.ui/packages/app-shell/src/views/item-tools/ResourceHistoryPage.actorAndNote.test.tsx(new): name shown with the id on its title and one directory read for the distinct ids; a missing row and a failed read show the id marked unresolved; anullactor shows none; notes under rows.ui/packages/data-objectstack/src/metadata-client.publishMessage.test.ts(new): the body bytes with and without a note.packages/runtime/src/domains/packages-seed-apply-org-scope.test.tsasserted the exact protocol request{ packageId, context }; it now carriesactor: the caller's userId, the change this PR makes.Acceptance notes
messagefollows the single-item publish door's rule: a string is forwarded, any other type sends no note (no 400). Item 4's request schema is where a stricter shape would be declared.Metadata-protocol: yesis outside the closed vocabulary the changeset gate reads (added | narrowed | semantic | none). Nothing a metadata author writes changes here (a REST body key, a history note, the console), so this PR declaresnone, and the changeset ispatch.ui/packages/i18n/src/locales/{en,zh}.ts; the publish-review keys live in the locale packs, whichall-locales-key-parity.test.tsholds to everyenkey, so all ten packs carry them. The history panel's keys live in the studio namespace, which is en + zh by design.usePublishAllDrafts.ts(Home banner, draft-preview bar) is unchanged: neither surface has a review step with a note, and a parameter no caller passes would be declared but never written.Verification
Local readings. Tests and ablations ran on
90af062b(the fix and its tests); the merge oforigin/main(dc581177) brought in no file of the six packages touched here. Gates ran ondc581177; the changeset gates were re-run ondb993774after a wording-only changeset edit. The head,5d035cf3, adds one string-only commit on top (full-width punctuation in the zhengine.edit.historyActorUnresolvedHint);@object-ui/i18ntsc --noEmitexit 0 andcheck-nul-bytesOK on it.Tests (all green)
@objectstack/runtime: the 52 package-door and dispatcher files (src/domains/packages*.test.ts,src/package-*.integration.test.ts,src/http-dispatcher.test.ts,src/dispatcher-validation-error*.test.ts): Test Files 52 passed, Tests 1106 passed, after the one moved pin above (it was the one red:expected … to deeply equal, the newactorkey).@objectstack/objectqlpublish and commit suites (7 files): 94 passed.@objectstack/metadata-protocolwhole package: Test Files 285 passed / 4 skipped, Tests 21872 passed / 26 skipped.ui/root: the DraftChangesPanel suites,views/item-tools, the two existing HistoryPanel suites,usePublishAllDraftssuites and the metadata-client publish suites: Test Files 41 passed, Tests 257 passed.@objectstack/metadata-protocol,@objectstack/client,@objectstack/runtime(with its test layer),@object-ui/i18n,@object-ui/data-objectstack,@object-ui/app-shell(tsc --noEmitandtsconfig.test.json).Ablations (each leg mutated through `scripts/ablation-replace.mjs`, restored blob == HEAD)
? request.messagereplaced by aglobalThismarker),metadata-protocolrebuilt, marker proved indist/byablation-dist-preflight→ the integration file 1 failed / 3 passed (the note test). Restored, rebuilt, marker proved absent → 4/4. A first attempt with the anchormessage: request.messagewas refused as ambiguous (4 hits); nothing was written.body?.actor→ 3 failed / 1 passed (note-with-actor, body actor ignored, system context).messagekey never sent → 2 failed / 5 passed.Derived gates (`node scripts/pm/dispatch-gates.mjs --commands`, 67 families)
dc581177.pnpm --filter @objectstack/spec run check:skill-examplesandpnpm check:dual-build-cjs-loads, each exit 0 ondb993774. Their first run exited 3 (prerequisite, not a verdict: no generated schema and 34 packages withoutdist/on a fresh clone); re-run aftergen:schemaand a fullturbo run build(117 tasks, exit 0).--ranreconcile: 67/67, 0 NOT-MEASURED.db993774:check-changeset-no-major,check-empty-changeset,check-adr-0087-registration(all--base origin/main) andcheck:doc-authoring, each exit 0.!in the title, no(narrowing)arm).Generated by Claude Code