Skip to content

Studio: publishing takes a change note and item history names the person — the note reaches publish-drafts, the actor is the session user, and it is shown by name, not id #28406

Closed

Description

@objectstack-fleet

epic:#28130#1 · hotlong · session_040dfd80c4c9411b895c698e785a6848

Filing gate: ④ child of epic #28236 (P1, cut after ADR-0146's rewrite merged in #28295) · plan of record: ADR-0146 Rollout P1, "N-06 change note and actor name (after N-05)"; N-05 is #28239, closed by PR #28296. ⛔ Not a claim.

Part of #28236 (P1, child N-06) · governs: ADR-0146 D1 (Studio owns history with rollback and audit) · lane: domain:spec (remainder: item 4 only; relaned from domain:ui on 2026-10-31, see the block below) · size: S.

Remainder — item 4 only (relaned to domain:spec). Items 1, 2, 3 and 5 landed in PR #28422 (a8d2fd7f, release note 6544702729). What is left is the contract half: packages/spec has PublishPackageDraftsResponseSchema (packages/spec/src/api/protocol.zod.ts ≈L1502) and no request schema for POST /packages/:id/publish-drafts. Wanted: the request schema, Zod first, declaring message (string, optional) and backfill as the runtime door reads them today; the route ledger's requestSchema for that door; SDK client and OpenAPI regenerated. ⛔ No domain:ui seat claims this remainder.

Serial with #28414. #28414 (domain:server, in flight) declares in its claim the packages/spec request schemas of the six sibling package doors and their regenerated outputs, and its branch already edits packages/client/src/index.ts. Both cards can write packages/spec/src/api/protocol.zod.ts, the route ledger and the generated SDK/OpenAPI. A claim on this card names #28414 under Serial constraints cleared:: if #28414's PR is open and touches any of those files, this card waits for it to merge and then works on the merged ref; if #28414 lands without a packages/spec hunk, this card goes ahead and declares only the publish-drafts request (the six sibling doors' actor keys stay #28414's).

Business reason. Studio's item toolbar (N-05) lists every version of an item, but each row reads "by owY7mdy6…", an opaque user id, and says nothing about why the item changed. Once customizations of HotCRM live in hotcrm.custom and are reviewed, exported and rolled back as a package (D2), an administrator picking a version to roll back to has to see who changed it and what they meant.

Measured (75ecaf4ad8).

  • The note is already stored. packages/metadata-core/src/objects/sys-metadata-history.object.ts:130 declares change_note; packages/metadata-protocol/src/sys-metadata-repository.ts:1241 writes it from opts.message, and the history read maps it back as message (:1048, :1936).
  • The single-item publish door forwards it: packages/rest/src/meta-item-history-routes.ts:621 reads body.message, and the actor comes from resolveMetaWriteActor (:619).
  • The package door Studio uses does not. packages/runtime/src/domains/packages.ts:1889 calls publishPackageDrafts with body.actor and backfill, never message. The protocol method accepts message (packages/metadata-protocol/src/protocol.ts:23785) but writes it only into the package commit (recordPackageCommit, :24385); the per-item promote (promoteDraftForPublish, called at :24303) hard-codes every history row's note as publish app package 'hotcrm' (:24345). The console sends an empty body (ui/packages/app-shell/src/preview/usePublishAllDrafts.ts:103).
  • The package doors take actor from the request body (:1891 and six sibling doors at :2074, :2239, :2301, :2327, :2422, :2467). Nothing between the route and the protocol rewrites it, and promoteDraftForPublish passes request.actor into each history row, so a caller holding manage_metadata can record any name as the actor. Not executed end to end: what a console publish records today.
  • The history panel shows the raw id: ui/packages/app-shell/src/views/item-tools/ResourceHistoryPage.tsx:292–295 renders by plus the id in mono, and its HistoryEvent type has no message.

Wanted.

  1. ✅ (landed, feat(runtime,metadata-protocol,client,console): Studio publish takes a change note; item history names the person #28422) Studio's publish review takes an optional change note. It reaches POST /packages/:id/publish-drafts, the runtime door forwards it, and the protocol threads it into each item's promote in place of the hard-coded sentence, so every history row that publish writes carries it; the package commit keeps it too.
  2. ✅ (landed, feat(runtime,metadata-protocol,client,console): Studio publish takes a change note; item history names the person #28422) The publish-drafts door records the request's authenticated identity, the rule the single-item door follows; a body actor is ignored. The six sibling doors are the same defect with a wider reach and are carried by runtime: the package doors record whatever actor the request body names — publish, save, discard, revert, rollback, reassign and duplicate write a caller-chosen name into metadata history #28414 (domain:server bug, split on the maintainer's 「同意」); whichever of the two lands first owns the shared identity helper.
  3. ✅ (landed, feat(runtime,metadata-protocol,client,console): Studio publish takes a change note; item history names the person #28422) The history panel shows the actor's display name, falling back to the id marked as unresolved, and the change note under the row, wherever HistoryPanel is mounted.
  4. ⏳ (remainder, this card) Contract: the publish-drafts request schema gains message (and declares backfill) in packages/spec (Zod first); route ledger, SDK client and OpenAPI regenerated. The PR fills its Clause-② and Metadata-protocol lines truthfully.
  5. ✅ (landed, feat(runtime,metadata-protocol,client,console): Studio publish takes a change note; item history names the person #28422) zh and en strings go through the console i18n bundle.

Tests. A publish-drafts call with a note gives every history row it writes that change_note. A body actor is ignored and the session user is recorded. The panel shows the name and the note, and falls back to the id for a deleted user. A component test covers the note field in the publish review.

Not here. The rollback note wording ("Rolled back to version n"). Who-did-it display on the audit tab. The per-package commit timeline. The six sibling doors' actor fix (#28414).

Dedupe. Related: #22510 (closed: sys_activity.actor_name never written; record History, not metadata history). Searched change_note, history actor name, publish note, publish-drafts actor: no open card covers this.

Links back: #28236, #28239, PR #28296, ADR-0146 D1, ADR-0067.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#5 · os-sales · session_01L6bhgUhKx1XHzkoikGjRB4

    Claim: PM loop round 4 · 2026-10-31T12:24Z
    Session: session_01L6bhgUhKx1XHzkoikGjRB4 (seat; implementation by a card thread, mode:thread, its session named in its first report)
    Account: os-sales
    Branch: the card thread's harness-pinned branch (named in its first report; this claim is the identity record)
    Worktree: the card thread's own container checkout
    Domain: domain:ui
    Seat: domain:ui#5
    File surface: partial, Part of #28406, items 1, 2, 3 and 5 plus their tests. Runtime: the publish-drafts door only in packages/runtime/src/domains/packages.ts (≈L1872, forward body.message; record the authenticated identity, a body actor ignored). Protocol: publishPackageDrafts (packages/metadata-protocol/src/protocol.ts ≈L23781) threads message into each promoteDraftForPublish in place of the hard-coded note (≈L24355); the package commit keeps it. SDK: packages/client/src/index.ts packages.publishDrafts opts gain message. Console: ui/packages/app-shell/src/preview/{usePublishAllDrafts.ts,DraftChangesPanel.tsx} (note field in the publish review), ui/packages/app-shell/src/views/item-tools/ResourceHistoryPage.tsx (HistoryPanel: display name with id fallback marked unresolved, note under the row), ui/packages/data-objectstack/src/metadata-client.ts if the console client carries the body, ui/packages/i18n/src/locales/{en,zh}.ts, tests, .changeset/28406-*.md (stop on breach; explain in the report). Excluded: item 4 (the new request schema in packages/spec, route ledger, OpenAPI), which is domain:spec work per lanes/ui.md and dispatch-gates --tier (note to the filer below); the six sibling doors' actor fix (card "Not here"); ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx, held by #28148 (seat 4): if the note can only be wired there, stop and report.
    Container & model: M, mode:thread, model: default (dispatch-gates --repo objectstack-ai/objectstack --tier at 63a9bcb7: no path-derived mandate), effort: high
    Clause-②: yes (widening: the publish-drafts door accepts message; the SDK publishDrafts opts type gains it). No packages/spec/src/** file is in this surface, so the owed review is TIER_DEFAULT by this seat (contract-review.md); a diff that reaches packages/spec/src/** is a breach.
    Metadata-protocol: yes (publishPackageDrafts / promoteDraftForPublish history-row note and actor)
    Responsibility: item 2 is a defect (any manage_metadata caller can record any name as the publish actor); owner surface is the runtime door, fixed at that door only
    Thread-read: none
    Premise: holds at origin/main 63a9bcb7: the door at packages.ts ≈L1889 passes body.actor and backfill, never message; protocol.ts ≈L24355 hard-codes publish app package '<id>'; the protocol request type already declares message (≈L23785); ResourceHistoryPage.tsx ≈L294–295 renders by + raw id in mono; the SDK publishDrafts opts are { actor?, backfill? }; packages/spec has no publish-drafts request schema (only PublishPackageDraftsResponseSchema)
    Serial constraints cleared: no open PR (25 read) touches domains/packages.ts, metadata-protocol/src/protocol.ts, packages/client/src/index.ts, item-tools/** or usePublishAllDrafts.ts; #25467 (move-only split of protocol.ts, os-zhuang) has no open cut PR; surface notice to seat 4 (#28286) sent


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#5 · os-sales · session_01L6bhgUhKx1XHzkoikGjRB4

    To: epic:#28130#1 (filer)
    Notify: split · #28406 · 2026-10-31T12:26Z

    The ui seat takes items 1, 2, 3 and 5 (claim above). Item 4 is left on the card and is not this seat's: the publish-drafts request schema does not exist in packages/spec today (only PublishPackageDraftsResponseSchema, packages/spec/src/api/protocol.zod.ts ≈L1506), so writing it is new packages/spec work, which .claude/skills/pm-dispatch/references/lanes/ui.md L9 puts in domain:spec ("协议恒归 domain:spec,新 packages/spec 工作同此"), and dispatch-gates --tier names it a contract-surface hit that moves to the spec lane.

    Nothing in items 1–3 waits on it: the runtime door reads the JSON body without a request schema, so message threads through as actor and backfill do today. Asked of the filer: file item 4 as a domain:spec child of #28236 (request schema with message and backfill, route ledger, OpenAPI), or relane this card's remainder. When this seat's PR lands, #28406 is released with item 4 named as the remainder.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#5 · os-sales · session_01L6bhgUAnBEBqJCebezxh8P
    os-dev-report
    {
    "issue": 28406,
    "status": "done",
    "branch": "claude/project-thread-64y985",
    "pr": "#28422",
    "session": "session_01L6bhgUAnBEBqJCebezxh8P",
    "premise_still_valid": true,
    "summary": "Items 1, 2, 3 and 5 of the card; item 4 (publish-drafts request schema in packages/spec, route ledger, OpenAPI) is left for the spec lane per split note 6543345540, and no packages/spec file is touched. POST /packages/:id/publish-drafts forwards a string body message, publishPackageDrafts passes it to each item promote so every sys_metadata_history row stores it as change_note (no note keeps the old sentence), and the door records the actor from the authorized execution context userId instead of body.actor (system context records NULL). SDK packages.publishDrafts gains opts.message (opts.actor kept, deprecated, ignored); MetadataClient.publishPackageDrafts sends message only when given. In Studio the DraftChangesPanel footer offers an optional change note on the batch door (StudioDesignSurface.tsx not edited, its doPublish already passes the options through), and the item history shows the actor by sys_user name (one read per id set, unresolved user marker otherwise) and the note under each row. Card assignee untouched; PR has no assignee.",
    "tests": "runtime: new packages-publish-drafts-change-note.integration.test.ts (real ObjectQL + SqlDriver over better-sqlite3, through HttpDispatcher.handlePackages) 4/4; the 52 package-door and dispatcher files Test Files 52 passed, Tests 1106 passed after one moved pin (packages-seed-apply-org-scope.test.ts now expects actor: caller.userId). objectql publish and commit suites 7 files, 94 passed. metadata-protocol whole package Test Files 285 passed / 4 skipped, Tests 21872 passed / 26 skipped. Console from ui root (os-verify-lock VERDICT command-exit 0): Test Files 41 passed, Tests 257 passed, including new DraftChangesPanel.changeNote (7), ResourceHistoryPage.actorAndNote (4), metadata-client.publishMessage (2). Typecheck exit 0 for metadata-protocol, client, runtime (with test layer), i18n, data-objectstack, app-shell (tsc --noEmit and tsconfig.test.json). Ablations via scripts/ablation-replace.mjs, restored blob equal to HEAD each time: protocol per-item note forced to default, metadata-protocol rebuilt and marker proved in dist by ablation-dist-preflight, integration file 1 failed / 3 passed, restored and rebuilt 4/4; door actor read back from body.actor 3 failed / 1 passed; panel message key never sent 2 failed / 5 passed; history names never passed 3 failed / 1 passed; history note never drawn 1 failed / 3 passed.",
    "gates": "node scripts/pm/dispatch-gates.mjs --commands, 67 families, each exit code captured before any pipe: 65 exit 0 on dc58117. check:skill-examples and check:dual-build-cjs-loads first exited 3 (prerequisite, fresh clone); after pnpm --filter @objectstack/spec run gen:schema and a full turbo build (117 tasks, exit 0) both exit 0. The changeset gates (check-changeset-no-major, check-empty-changeset, check-adr-0087-registration, all --base origin/main) and check:doc-authoring re-run on head db99377, each exit 0. --ran reconcile: 67/67, 0 NOT-MEASURED. PR check-runs on db99377 at report time: success for Lint and Repo Gates, Check Changeset, Check PR Size, PR-body guards, Governed Surface Queue Guard, Flag docs affected by code changes, Lint PM dispatch-gates, Lint ESLint, Lint docs and code guards, Lint repo and tooling gates, Lint CI release and engine gates, the three Lint scoped jobs, Type Check source gates, Type Check consumer gates, Type Check debt ledger, Build Core, Build UI Deps, Build and E2E UI, Live E2E UI, Dogfood Regression Gate, Plan Test Core shards, Auto Label; still in progress: Test Core 1-8 of 8, Test UI 1-16 of 16, Lint UI, Type Check UI, Type Check workspace. This thread is subscribed to the PR and fixes any red this diff causes.",
    "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
    "mcp_calls": "reads only: issue_read, pull_request_read (get, get_check_runs, get_comments), search_pull_requests; plus subscribe_pr_activity (session subscription, not a GitHub write). No MCP GitHub write tool was called.",
    "api_writes": "2 — relay pr_create (draft PR 28422, run 41205672398, body read back 9597 bytes identical, no assignees) and this os-dev-report comment via post-stamped. git push is not a REST write.",
    "open_questions": [],
    "out_of_scope_findings": [
    "deviation · the claim declared Metadata-protocol: yes, which is outside the closed vocabulary the changeset gate reads (added, narrowed, semantic, none); nothing a metadata author writes changes here, so the PR and changeset declare none and the changeset is patch. Named in the PR Acceptance notes.",
    "deviation · the claim named the locale files en and zh only; the publish-review keys live in the locale packs, which all-locales-key-parity.test.ts holds to every en key, so all ten packs carry noteLabel and notePlaceholder. The history keys sit in the studio namespace, en and zh by design.",
    "deviation · the PR body Verification block says check:skill-examples and check:dual-build-cjs-loads were NOT MEASURED pending a re-run; that re-run has since finished, both exit 0 (see gates). The body is not patched by the dev; the seat may correct that line if wanted.",
    "deviation · commit trailers use the session co-author line the harness assigns; no card number appears in commit messages.",
    "carrier: none · a non-string body message is silently ignored (no 400), mirroring the single-item publish door; a stricter shape belongs to item 4 request schema. Noted in Acceptance notes, not filed.",
    "carrier: none · usePublishAllDrafts.ts (Home banner, draft-preview bar) left unchanged because neither surface has a review step with a note. Noted, not filed."
    ]
    }

  4. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#5 · os-sales · session_01L6bhgUhKx1XHzkoikGjRB4

    Landed (partial: items 1, 2, 3 and 5) · 2026-10-31T14:28Z · PR #28422 merged through the merge queue as a8d2fd7f at 2026-10-31T14:28:04Z (seat ACCEPT 6544434317 and TIER_DEFAULT contract review PASS 6544420721, both pinned to 5d035cf3).

    probe on origin/main a8d2fd7f reading
    packages/runtime/src/domains/packages.ts publish-drafts door actor from resolvePackageWriteActor(_context) (≈L1911), body message forwarded
    packages/metadata-protocol/src/protocol.ts per-item promote message: request.message ? … : 'publish app package …'
    ui/packages/app-shell/src/views/item-tools/historyActors.ts present (names behind history actor ids)

    Release: claim 6543324409 (session session_01L6bhgUhKx1XHzkoikGjRB4, domain:ui#5). Partial landing, so the card returns to pm:queue with no assignee.
    Resume-from: none (the remainder starts from main).


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#28130#1 · hotlong · session_040dfd80c4c9411b895c698e785a6848
    2026-10-31T14:34Z

    To: ui seat (split note 6543345540, release note 6544702729) · spec seat
    Notify: relane · #28406 · remainder item 4

    Relaned on the maintainer's instruction, given in this seat's direct session (epic PM for #28236), verbatim: 「#28406 剩下的第 4 项,就在原卡上把车道改成 domain:spec,回到 pm:queue;卡面注明和 #28414 的 spec 改动排先后。」

    Not a claim.

  6. added
    priority:p1High: required for production / M2
    and removed on Oct 31, 2026
  7. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V

    Claim: PM loop round 3 · remainder item 4 · 2026-10-31T14:38Z
    Session: session_017ihL2fi9zfJhUTubyJYb6V
    Account: marchtian
    Branch: claude/issue-28406-publish-drafts-request
    Worktree: objectstack-issue-28406
    Domain: domain:spec
    Seat: domain:spec#4
    Ruling-ref: the maintainer put this card ahead of the queue and asked for dispatch now (「28406 也插队」, 「这两张现在就派发」, 2026-10-31T14:37Z); the relane to domain:spec for item 4 (6544774566, the maintainer's instruction quoted there).
    File surface: item 4 only: packages/spec/src/api/protocol.zod.ts (a PublishPackageDraftsRequestSchema beside PublishPackageDraftsResponseSchema, declaring message (string, optional) and backfill as the runtime door reads them on a8d2fd7f30; actor is not declared, since the door ignores it), the route ledger's requestSchema for POST /packages/:id/publish-drafts, the barrel exports, tests, the generated SDK/OpenAPI and spec artefacts by repo tooling only, changeset (@objectstack/spec minor). packages/client/src/index.ts only if its generated part moves. The six sibling doors' request schemas and actor keys are #28414's and are not touched (stop on breach; explain in the report)
    Container & model: S, mode:subagent, model: TIER_DEFAULT (opus) under the quota-exhaustion exemption (maintainer 「你没有fable了,使用 opus开发完之后等契约复审」), effort: medium
    Clause-②: yes (widening)
    Metadata-protocol: added
    Responsibility: an SDK or API caller sees the change note in the publish-drafts contract instead of an undeclared body | PublishPackageDraftsRequestSchema with message and backfill, and the route ledger naming it | Studio's publish review (N-05/N-06) and every SDK client of the package publish door
    Thread-read: 6544774566
    Premise: holds on a8d2fd7f30: packages/spec has PublishPackageDraftsResponseSchema and no request schema for the door; the runtime door reads body.message and backfill (#28422)
    Serial constraints cleared: #28414 has no open PR; its branch claude/project-thread-a0v6t5 touches packages/runtime/src/domains/packages.ts, packages/client/src/index.ts, a test and a changeset, and no packages/spec file. So this card goes ahead and declares only the publish-drafts request. If #28414 opens a PR that touches protocol.zod.ts, the route ledger or the generated SDK/OpenAPI before this lands, whichever lands second merges main and regenerates

  8. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V
    os-dev-report
    {
    "issue": 28406,
    "status": "done",
    "branch": "claude/issue-28406-publish-drafts-request",
    "pr": "#28447",
    "head": "de4fe2c0a3",
    "session": "session_017ihL2fi9zfJhUTubyJYb6V",
    "reported_at": "2026-10-31T17:09Z",
    "premise_still_valid": true,
    "summary": "Item 4 only. @objectstack/spec/api gains PublishPackageDraftsRequestSchema (+ type PublishPackageDraftsRequest) beside the response schema: message optional string, backfill optional array of strict { object, field } non-empty strings, no default; top level non-strict (unknown keys stripped) so the SDK's deprecated opts.actor is not refused. The dispatcher route ledger had no requestSchema field; it is added (runtime-internal, JSDoc states what a filled row claims) and the publish-drafts row names the schema; the client resolver guard resolves requestSchema names. SDK client is hand-written and untouched; gen:openapi emits a fixed nine-schema list (gitignored) so nothing moved there. Spec artefacts regenerated by tooling. Changeset @objectstack/spec minor.",
    "tests": "head de4fe2c. runtime: packages-publish-drafts-request-conformance.test.ts (new) + response-conformance + change-note integration + route-ledger.conformance: Test Files 4/4, Tests 30/30. client: route-ledger-response-schema + route-ledger-coverage + client-url-conformance: Test Files 3/3, Tests 11/11. spec: protocol.test.ts + type-alias-convention.pin.test.ts: Test Files 2/2, Tests 233/233. Typecheck (tsc --noEmit + check:test-typecheck): spec 0, runtime 0, client 0. Reverse verification via scripts/ablation-replace.mjs: top-level schema made .strict() -> spec 'is not strict' pin red (1 failed / 4 passed) and runtime 'body actor stripped' pin red (1 failed / 7 passed); restored, blob == HEAD 87c51f9750fd, git diff HEAD empty. All test runs and builds under os-verify-lock (VERDICT command-exit 0), except one early single-file spec run before the lock (deviation).",
    "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack: 105 families derived on the final tree, each run with its exit code captured before any pipe; --ran reconcile: 105/105 accounted, exit 0. Final readings: 104 exit 0; 1 NOT MEASURED: check:dual-build-cjs-loads exit 3 (PREREQUISITE NOT MET, 37 packages without dist; a full workspace build was not run on this shared box; CI Lint and Repo Gates runs it). Reds fixed in-branch and re-run green: check:doc-authoring exit 1 (issue id in the backfill describe) -> 0; check:cross-package-test-inputs exit 1 (a repo path spelled in a client test comment) -> 0. Prerequisite exits re-run green: check:skill-examples 3 -> 0 after building client-react; check:type-check-debt 3 (lock timeout) -> 0 under the lock. The spec families, changeset gates, issue-citations, doc-authoring, cross-package and spec check:generated (14/14 up to date) were re-run on de4fe2c after the last source edit. PR check-runs on de4fe2c at report time: none started yet.",
    "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
    "line_budget": "15 files, +428 / -15 (generated: 7 files, +37 / -9)",
    "files_changed": [
    "packages/spec/src/api/protocol.zod.ts",
    "packages/spec/src/api/protocol.test.ts",
    "packages/spec/src/type-alias-convention.pin.test.ts",
    "packages/runtime/src/route-ledger.ts",
    "packages/runtime/src/domains/packages-publish-drafts-request-conformance.test.ts",
    "packages/client/src/route-ledger-response-schema.test.ts",
    ".changeset/28406-publish-drafts-request-schema.md",
    "generated: packages/spec/{api-surface,authorable-surface,declaration-map,export-origins,json-schema.manifest}/api.json, content/docs/references/{index,api/protocol}.mdx, docs/audits/2026-07-unknown-key-strictness-ledger.counts/api.md"
    ],
    "deviations": [
    "PM assumption refuted: the dispatcher route ledger (packages/runtime/src/route-ledger.ts) carried no requestSchema field, only responseSchema; the REST catalog's requestSchema (plugin-rest-api.zod.ts) does not list /packages and its ratchet demands the route refuse every violating body. Added an optional requestSchema field to RouteLedgerEntry (same rules as responseSchema) and extended the existing resolver guard in packages/client/src/route-ledger-response-schema.test.ts (a client test file, not index.ts).",
    "'SDK client and OpenAPI regenerated': the SDK is hand-written (packages/client/src/index.ts untouched, no generated part); gen:openapi publishes a fixed nine-schema contract list and is gitignored, so neither moved.",
    "One spec single-file vitest run happened before the verify lock was used; every later build and test went through os-verify-lock.",
    "No Fable or substitute review was run, per the order; the PR waits for the seat's contract-review hand-off."
    ],
    "overlap_28414": "No PR from branch claude/project-thread-a0v6t5 (REST read, 0 pulls, re-read just before pr_create). Its branch touches packages/runtime/src/domains/packages.ts, packages/client/src/index.ts, a runtime test and a changeset; no packages/spec file, route ledger or generated artefact. This PR touches none of its files.",
    "mcp_calls": "0 — no MCP GitHub tool called (reads through gh REST)",
    "api_writes": "2 — relay pr_create (draft PR 28447, run 41221514271, body read back 5947 bytes identical, no assignees, no labels) and this os-dev-report comment via post-stamped. git push is not a REST write.",
    "open_questions": [
    {
    "question": "Should the publish-drafts door parse its body with PublishPackageDraftsRequestSchema, so a non-string message is refused 400 instead of silently dropped?",
    "options": ["A: follow-up card, domain:server, after #28414 lands (both edit packages.ts): parse with the schema, 400 VALIDATION_FAILED, flip the divergence pin in packages-publish-drafts-request-conformance.test.ts", "B: leave the door tolerant; the schema stays a declaration of what the door reads"],
    "recommendation": "A, because a 200 that drops a malformed note is the silent-failure shape the closed-parameter rule exists to stop, and the pin is already in place to flip"
    }
    ],
    "out_of_scope_findings": [
    "class: b · reach: POST /api/v1/packages/:id/publish-drafts with body {"message": 42} answers 200 and publishes with the default note (pinned in packages-publish-drafts-request-conformance.test.ts) · Seam: spec:PublishPackageDraftsRequestSchema.message → runtime:packages/runtime/src/domains/packages.ts publish-drafts branch (typeof body.message === 'string') · dedupe words: publish-drafts body validation, request schema enforcement, message non-string, VALIDATION_FAILED packages door",
    "carrier: #28414 · the ADR-0045 app re-save after publish-drafts still forwards body.actor to saveMetaItem on main (packages.ts ≈L2102); #28414's branch removes it. Pinned in the new conformance suite, whose message says to flip the pin and drop the sentence from the route-ledger row note when that lands."
    ]
    }

  9. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V

    Landed · 2026-10-31T18:18Z · PR #28447 merged by the queue at 2026-10-31T18:17Z as 462935db87; item 4 was the card's last open item, so the card closed by Fixes. This seat strips pm:dispatched in the same act. PublishPackageDraftsRequestSchema (message?: string, backfill?: { object, field }[], unknown keys stripped) declares the body POST /packages/:id/publish-drafts reads, and the dispatcher route ledger's new optional requestSchema names it on that row. Contract review: PASS 6547060891 on the landed head. Follow-ups: #28452 (the door does not yet refuse a non-string message; after #28414) and one pin #28414 flips when it lands (noted on #28414).

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:specenhancementNew feature or requestpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions