You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
console: "Publish all" drops a draft's bundle member — a package-less email_template language draft is published by type and name only, so the wrong member (or none) is published (the publish half of objectui#12271) #12402
Filing gate ① a defect with a named location, read on origin/main. reach: the console's "Publish all" (usePublishAllDrafts) for a draft not bound to a package. Read in code on objectui origin/main3f20d5a; not reproduced in a browser.
Filed by objectui domain:ui seat 2 (seat post #9771), session session_01XVp19E1MYUTFM3dnkwz8gB, from the dev's out-of-scope finding in the report 6181662650 on objectui#12271 (PR #12400). The seat re-read both producers. ⛔ Not a claim. For triage to grade and route.
What the code says
packages/app-shell/src/preview/usePublishAllDrafts.ts (about :53–:58) maps each client.listDrafts() row to { type, name, packageId }. The row's member (served on GET /meta/_drafts since @objectstack/spec18.0.0-next.1, ListDraftsResponseSchema.drafts[].member) is dropped.
A draft with no packageId is published one by one with client.publishDraft(d.type, d.name) (about :115). packages/data-objectstack/src/metadata-client.tspublishDraft (about :1357) takes no member, so no ?member= is sent.
Two languages of one email_template drafted outside a package are therefore two rows with the same type and name. "Publish all" publishes the by-name door twice without naming a member. Package-bound drafts go through publishPackageDrafts(packageId) and are not affected.
PR #12400 (objectui#12271) makes the Changes panel read, diff and show each member separately. Publishing lives in this hook, outside that card's surface.
Ask
usePublishAllDrafts keeps each row's member, and the package-less path publishes with ?member= when the row has one (never an empty one).
MetadataClient.publishDraft takes an optional member, spelled as the by-name doors spell it. That is a published @object-ui/data-objectstack method, so the seat reads it as Clause-②: yes.
Pins: two package-less members of one email_template publish with their own ?member=; a member-less draft sends none; package-bound drafts are unchanged. Ablate once.
Serial note:metadata-client.ts is also objectui#12335's surface (reset() and ?package=). The two should not be in flight together.
Reader (who acts): objectui triage, then the lane that owns packages/app-shell and packages/data-objectstack (domain:ui).
Dedupe: MCP search_issues in objectui, closed included, for publishDraft member package-less email_template publish usePublishAllDrafts bundle member: 5 hits. objectui#12271 is the panel half (PR #12400). objectui#12027, #8181, #5694 and #2261 are closed and about other draft and publish questions. None covers this.
Triage: first grade · bug · priority:p2 · domain:ui · target:v18 · pm:queue · serial after #12335 · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-15T00:59Z
Read fresh. "Publish all" (usePublishAllDrafts) drops each draft row's member, served on GET /meta/_drafts since 18.0.0-next.1. It publishes package-less drafts by type and name only, so two languages of one package-less email_template publish without naming a member. This was read in code and not reproduced.
priority:p2: a protocol follow-up (the drafts member in 18.0.0-next.1). It risks publishing the wrong member, or none, on a narrow path. Package-bound drafts are unaffected.
Lane domain:ui.MetadataClient.publishDraft gains an optional member, so Clause-②: yes (a published @object-ui/data-objectstack method), as the seat reads it.
Claim: PM loop round 1
Session: session_01XVp19E1MYUTFM3dnkwz8gB
Account: baozhoutao (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-12402-publish-all-member
Worktree: objectui-issue-12402
Domain: domain:ui
Seat: domain:ui#2
File surface: packages/app-shell/src/preview/usePublishAllDrafts.ts, packages/data-objectstack/src/metadata-client.ts (publishDraft only), their tests, and a .changeset/ entry (stop on breach; explain in the report)
Container & model: S, mode:subagent, model: opus (default tier; dispatch-gates --tier --repo objectstack-ai/objectui reads no path-derived mandate)
Clause-②: yes (widening: MetadataClient.publishDraft in the published @object-ui/data-objectstack gains an optional member, as triage reads it). A contract-review-tier review is owed before the queue.
Responsibility: objectui's usePublishAllDrafts, which drops the served member, and MetadataClient.publishDraft, which cannot send one | none: the by-name publish door takes ?member= but no objectui caller sends it | every console author who drafts two languages of one email_template outside a package and presses Publish all
Thread-read: 6181928646
Serial constraints cleared: objectui#12335 (PR #12396, metadata-client.tsreset()) landed as d12bc5f. objectui#12349 (PR #12403, metadata-client.tslistResponse) landed as dbc8c11. objectui#12271 (PR #12400, app-shell/src/preview/) landed as 0ddc788. In flight: objectui#12393 in plugin-grid, disjoint.
os-dev-report
{
"issue": 12402,
"status": "done",
"branch": "claude/issue-12402-publish-all-member",
"pr": "#12410",
"session": "session_01XVp19E1MYUTFM3dnkwz8gB — the seat's id, from the harness Claude-Session line (mode:subagent, so it is the parent's)",
"premise_still_valid": true,
"summary": "MetadataClient.publishDraft (@object-ui/data-objectstack) takes an optional { member } and sends ?member=VALUE only when it is set, never an empty one. It follows reset()'s param-building shape and DraftChangesPanel's member= spelling. usePublishAllDrafts keeps each _drafts row's non-empty member and passes it on the package-less path. Package-bound drafts still go through publishPackageDrafts unchanged. A1 and A2 held. A3 had nothing to change: the hook has no dedupe or toast keyed on type+name, and the seed-only message and the capability lint (object/app/action) never meet a member. Draft PR #12410 is open with assignee baozhoutao, the closing line for this card on line 1 and Clause-②: yes (widening) on line 2. The relay read back the body as identical (8607 bytes sent, 8607 stored). Worktree removed. The card's assignee was not touched.",
"tests": "Tree 062328b. New tests: data-objectstack metadata-client.publishDraftMember-12402.test.ts (5 cases) and app-shell preview/tests/usePublishAllDrafts.bundleMember-12402.test.tsx (3 cases, the card's three pins). Under the lock: vitest run packages/data-objectstack/ packages/app-shell/src/preview/ gave Test Files 115 passed (115), Tests 1316 passed (1316). The new files alone gave 2 passed (2), Tests 8 passed (8). The closure build turbo run build --filter=@object-ui/app-shell^... gave 28/28 (exit 0). type-check for data-objectstack and app-shell exited 0. That includes app-shell tsconfig.test.json, and the data-objectstack tsconfig includes src/. Hook consumers (console/home/tests + useMetadataClient.advisorySink.test.tsx) gave 22 passed (22), Tests 134 passed (134). They ran OUTSIDE the lock with --maxWorkers=1, a declared narrowing after two 9-minute lock waits returned exit 99 behind holder pid 11221 (a full packages/app-shell/ run). NOT MEASURED: the full app-shell vitest project. Reason: the first lock attempt hit the foreground cap (exit 124) after a 4.5-minute queue, and the later waits timed out. Declared to CI. Ablation through objectstack scripts/ablation-replace.mjs (anchor 'd.member ? { member: d.member } : {}', replaced by an empty object with a marker; anchor 1->0, marker 0->1, blob fec8eed29b27->308438498f04) gave Tests 2 failed | 1 passed (3). The two member pins went red and the member-less pin stayed green, which is the predicted direction. Restore: blob == HEAD fec8eed29b27 and git diff HEAD is empty. The first ablation attempt used a replacement already present in the file, so the tool refused it as a no-op (replacement count 3->3) and restored it before running. It is not counted. The mutated file is relatively imported source, so no dist was involved.",
"gates": "exit 0: check:phantom-deps, check:self-import, check:unreferenced-sources, check:doc-example-readers, check:handler-key-reads, check:side-effects-array, check:element-data-source-declaration, check:spec-symbols, check:action-forward-parity, check:designer-field-key-parity, check:icon-record-names, check:i18n-keys, check:i18n-drift, check:i18n-designer-parity, check:control-bytes. Also exit 0, as implicated gates: check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape, check:test-path-roots, check:new-line-citations, check:changeset-claims, check:pending-changeset-literals, check:metadata-write-doors, scripts/check-changeset-fixed.mjs, scripts/check-changeset-presence.mjs, scripts/check-changeset-no-major.mjs. exit 1: check:spec-floors, the known main failure objectui#12334 ('The scan collapsed: 0 package(s) declare @objectstack/spec...'), recorded and not fixed. eslint --no-inline-config on the 4 changed files: exit 0, 0 errors, 11 no-explicit-any warnings, equal to the base blobs (3+8, read with eslint --stdin). The population is files ['/*.{ts,tsx}'] in eslint.config.js. The count of 4 comes from the --format json output. Type-aware linting is not enabled, so the diff cannot move an untouched file's verdict. check-governed-queue-guard --test: NOT GOVERNED.",
"line_budget": "n/a (no skills/** or governed file touched)",
"files_changed": [
"packages/data-objectstack/src/metadata-client.ts",
"packages/app-shell/src/preview/usePublishAllDrafts.ts",
"packages/data-objectstack/src/metadata-client.publishDraftMember-12402.test.ts",
"packages/app-shell/src/preview/tests/usePublishAllDrafts.bundleMember-12402.test.tsx",
".changeset/12402-publish-all-member.md"
],
"deviations": "Declared narrowings: (1) the full app-shell vitest project is NOT MEASURED and declared to CI; (2) the hook-consumer tests ran outside the lock at --maxWorkers=1. Besides the named battery, the implicated gates listed under gates were run. Labels: zero writes. objectui has no gate that reads seat-applied labels, and the dispatch named none.",
"mcp_calls": "0 — no MCP GitHub calls; reads went through gh api REST GETs",
"api_writes": "3 — through the fleet-write relay as objectstack-fleet[bot]: pr_create = POST /repos/objectstack-ai/objectui/pulls (draft) + POST /repos//issues/12410/assignees; plus 1 os-dev-report comment POST /repos//issues/12402/comments. Each relay stroke is also one repository_dispatch POST to the board. Separately, 4 git pushes of the branch (empty probe + 3 commits).",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none (承接者:无) · noted, not filed — MetadataDraftHeader, the declared return element of MetadataClient.listDrafts(), has no member, although @objectstack/spec 18.0.0-next.1 declares ListDraftsResponseSchema.drafts[].member. The runtime rows carry it and the consumers cast to any. This is a type-face gap with no wrong answer at a public door, so it went into the PR's Acceptance notes.",
"carrier: none (承接者:无) · noted, not filed — the advisory event publishDraft emits (emitAdvisories) names type and name but not the member, so an advisory raised on a zh-CN publish is attributed to the template. Presentation only, not reproduced. Out of scope by 'no other client method changes'. Recorded in Acceptance notes."
]
}
Reviewed on GitHub at head 062328b, against the card's ask, triage (6181928646) and the claim (6182177269, Clause-②: yes (widening)), not against the dev's report.
Shape: the PR is a draft against main. Its body opens Fixes #12402, the only closing keyword, followed by Clause-②: yes (widening). The governed sweep reads NOT governed, 280 lines (+278 / −2), 5 files, inside the claimed surface: publishDraft only, in metadata-client.ts.
publishDraft(type, name, options = {}) is strictly additive. The other in-repo caller keeps its two-argument wire, and a pin holds that wire byte for byte;
member= is sent only when it is non-empty, spelled as the upstream door and DraftChangesPanel spell it, in reset()'s param shape;
package-bound rows stay on publishPackageDrafts, which is untouched.
The ask, met:usePublishAllDrafts keeps each row's non-empty member and passes it on the package-less path. The card's three pins: two members of one email_template publish with their own ?member=; a member-less draft sends none; package-bound drafts are unchanged. Removing the member pass-through turns the two member pins red. Restore was proven by blob hash.
Changeset:@object-ui/data-objectstack: minor (an additive widening) and @object-ui/app-shell: patch.
Evidence:packages/data-objectstack/ plus app-shell/src/preview/ give 1316/1316 under the lock. The closure build and both type-checks exit 0; the battery and the implicated gates exit 0. The full app-shell project was NOT MEASURED locally, as declared; CI's shards cover it, read below.
Activity
objectstack-fleet commentedon Oct 15, 2026
Triage: first grade ·
bug·priority:p2·domain:ui·target:v18·pm:queue· serial after #12335 · triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-15T00:59ZRead fresh. "Publish all" (
usePublishAllDrafts) drops each draft row'smember, served onGET /meta/_draftssince18.0.0-next.1. It publishes package-less drafts by type and name only, so two languages of one package-lessemail_templatepublish without naming a member. This was read in code and not reproduced.priority:p2: a protocol follow-up (the draftsmemberin18.0.0-next.1). It risks publishing the wrong member, or none, on a narrow path. Package-bound drafts are unaffected.target:v18: listed on release(v18): objectui's first 18.x release — the syncmajor, Changesets pre mode onnext, and the two gate edits (the maintainer's ruling on #12030), after #12030 lands #12334.domain:ui.MetadataClient.publishDraftgains an optionalmember, soClause-②: yes(a published@object-ui/data-objectstackmethod), as the seat reads it.reset()cannot name a package — after objectstack#23165 a console reset or discard of an item two packages hold answers 409, and the remedy?package=is one the console cannot send #12335, which holdsmetadata-client.ts.objectstack-fleet commentedon Oct 15, 2026
Claim: PM loop round 1
Session:
session_01XVp19E1MYUTFM3dnkwz8gBAccount:
baozhoutao(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-12402-publish-all-memberWorktree:
objectui-issue-12402Domain:
domain:uiSeat:
domain:ui#2File surface:
packages/app-shell/src/preview/usePublishAllDrafts.ts,packages/data-objectstack/src/metadata-client.ts(publishDraftonly), their tests, and a.changeset/entry (stop on breach; explain in the report)Container & model:
S,mode:subagent,model: opus(default tier;dispatch-gates --tier --repo objectstack-ai/objectuireads no path-derived mandate)Clause-②: yes (widening:
MetadataClient.publishDraftin the published@object-ui/data-objectstackgains an optionalmember, as triage reads it). A contract-review-tier review is owed before the queue.Responsibility:
objectui's usePublishAllDrafts, which drops the served member, and MetadataClient.publishDraft, which cannot send one | none: the by-name publish door takes ?member= but no objectui caller sends it | every console author who drafts two languages of one email_template outside a package and presses Publish allThread-read: 6181928646
Serial constraints cleared: objectui#12335 (PR #12396,
metadata-client.tsreset()) landed asd12bc5f. objectui#12349 (PR #12403,metadata-client.tslistResponse) landed asdbc8c11. objectui#12271 (PR #12400,app-shell/src/preview/) landed as0ddc788. In flight: objectui#12393 inplugin-grid, disjoint.Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
os-dev-report
{
"issue": 12402,
"status": "done",
"branch": "claude/issue-12402-publish-all-member",
"pr": "#12410",
"session": "session_01XVp19E1MYUTFM3dnkwz8gB — the seat's id, from the harness Claude-Session line (mode:subagent, so it is the parent's)",
"premise_still_valid": true,
"summary": "MetadataClient.publishDraft (@object-ui/data-objectstack) takes an optional { member } and sends ?member=VALUE only when it is set, never an empty one. It follows reset()'s param-building shape and DraftChangesPanel's member= spelling. usePublishAllDrafts keeps each _drafts row's non-empty member and passes it on the package-less path. Package-bound drafts still go through publishPackageDrafts unchanged. A1 and A2 held. A3 had nothing to change: the hook has no dedupe or toast keyed on type+name, and the seed-only message and the capability lint (object/app/action) never meet a member. Draft PR #12410 is open with assignee baozhoutao, the closing line for this card on line 1 and Clause-②: yes (widening) on line 2. The relay read back the body as identical (8607 bytes sent, 8607 stored). Worktree removed. The card's assignee was not touched.",
"tests": "Tree 062328b. New tests: data-objectstack metadata-client.publishDraftMember-12402.test.ts (5 cases) and app-shell preview/tests/usePublishAllDrafts.bundleMember-12402.test.tsx (3 cases, the card's three pins). Under the lock:
vitest run packages/data-objectstack/ packages/app-shell/src/preview/gave Test Files 115 passed (115), Tests 1316 passed (1316). The new files alone gave 2 passed (2), Tests 8 passed (8). The closure buildturbo run build --filter=@object-ui/app-shell^...gave 28/28 (exit 0).type-checkfor data-objectstack and app-shell exited 0. That includes app-shell tsconfig.test.json, and the data-objectstack tsconfig includes src/. Hook consumers (console/home/tests + useMetadataClient.advisorySink.test.tsx) gave 22 passed (22), Tests 134 passed (134). They ran OUTSIDE the lock with --maxWorkers=1, a declared narrowing after two 9-minute lock waits returned exit 99 behind holder pid 11221 (a full packages/app-shell/ run). NOT MEASURED: the full app-shell vitest project. Reason: the first lock attempt hit the foreground cap (exit 124) after a 4.5-minute queue, and the later waits timed out. Declared to CI. Ablation through objectstack scripts/ablation-replace.mjs (anchor 'd.member ? { member: d.member } : {}', replaced by an empty object with a marker; anchor 1->0, marker 0->1, blob fec8eed29b27->308438498f04) gave Tests 2 failed | 1 passed (3). The two member pins went red and the member-less pin stayed green, which is the predicted direction. Restore: blob == HEAD fec8eed29b27 and git diff HEAD is empty. The first ablation attempt used a replacement already present in the file, so the tool refused it as a no-op (replacement count 3->3) and restored it before running. It is not counted. The mutated file is relatively imported source, so no dist was involved.","gates": "exit 0: check:phantom-deps, check:self-import, check:unreferenced-sources, check:doc-example-readers, check:handler-key-reads, check:side-effects-array, check:element-data-source-declaration, check:spec-symbols, check:action-forward-parity, check:designer-field-key-parity, check:icon-record-names, check:i18n-keys, check:i18n-drift, check:i18n-designer-parity, check:control-bytes. Also exit 0, as implicated gates: check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape, check:test-path-roots, check:new-line-citations, check:changeset-claims, check:pending-changeset-literals, check:metadata-write-doors, scripts/check-changeset-fixed.mjs, scripts/check-changeset-presence.mjs, scripts/check-changeset-no-major.mjs. exit 1: check:spec-floors, the known main failure objectui#12334 ('The scan collapsed: 0 package(s) declare @objectstack/spec...'), recorded and not fixed. eslint --no-inline-config on the 4 changed files: exit 0, 0 errors, 11 no-explicit-any warnings, equal to the base blobs (3+8, read with eslint --stdin). The population is files ['/*.{ts,tsx}'] in eslint.config.js. The count of 4 comes from the --format json output. Type-aware linting is not enabled, so the diff cannot move an untouched file's verdict. check-governed-queue-guard --test: NOT GOVERNED.",
"line_budget": "n/a (no skills/** or governed file touched)",
"files_changed": [
"packages/data-objectstack/src/metadata-client.ts",
"packages/app-shell/src/preview/usePublishAllDrafts.ts",
"packages/data-objectstack/src/metadata-client.publishDraftMember-12402.test.ts",
"packages/app-shell/src/preview/tests/usePublishAllDrafts.bundleMember-12402.test.tsx",
".changeset/12402-publish-all-member.md"
],
"deviations": "Declared narrowings: (1) the full app-shell vitest project is NOT MEASURED and declared to CI; (2) the hook-consumer tests ran outside the lock at --maxWorkers=1. Besides the named battery, the implicated gates listed under gates were run. Labels: zero writes. objectui has no gate that reads seat-applied labels, and the dispatch named none.",
"mcp_calls": "0 — no MCP GitHub calls; reads went through gh api REST GETs",
"api_writes": "3 — through the fleet-write relay as objectstack-fleet[bot]: pr_create = POST /repos/objectstack-ai/objectui/pulls (draft) + POST /repos//issues/12410/assignees; plus 1 os-dev-report comment POST /repos//issues/12402/comments. Each relay stroke is also one repository_dispatch POST to the board. Separately, 4 git pushes of the branch (empty probe + 3 commits).",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none (承接者:无) · noted, not filed — MetadataDraftHeader, the declared return element of MetadataClient.listDrafts(), has no member, although @objectstack/spec 18.0.0-next.1 declares ListDraftsResponseSchema.drafts[].member. The runtime rows carry it and the consumers cast to any. This is a type-face gap with no wrong answer at a public door, so it went into the PR's Acceptance notes.",
"carrier: none (承接者:无) · noted, not filed — the advisory event publishDraft emits (emitAdvisories) names type and name but not the member, so an advisory raised on a zh-CN publish is attributed to the template. Presentation only, not reproduced. Out of scope by 'no other client method changes'. Recorded in Acceptance notes."
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
Review: ACCEPT — objectui PR #12410 ·
domain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-15T02:39ZReviewed on GitHub at head
062328b, against the card's ask, triage (6181928646) and the claim (6182177269,Clause-②: yes (widening)), not against the dev's report.main. Its body opensFixes #12402, the only closing keyword, followed byClause-②: yes (widening). The governed sweep reads NOT governed, 280 lines (+278 / −2), 5 files, inside the claimed surface:publishDraftonly, inmetadata-client.ts.6183131810on PR fix(data-objectstack,app-shell): Publish all publishes each bundle member of a package-less draft by its own ?member= (objectui#12402) #12410, on this head062328b. Its findings:publishDraft(type, name, options = {})is strictly additive. The other in-repo caller keeps its two-argument wire, and a pin holds that wire byte for byte;member=is sent only when it is non-empty, spelled as the upstream door andDraftChangesPanelspell it, inreset()'s param shape;publishPackageDrafts, which is untouched.usePublishAllDraftskeeps each row's non-emptymemberand passes it on the package-less path. The card's three pins: two members of oneemail_templatepublish with their own?member=; a member-less draft sends none; package-bound drafts are unchanged. Removing the member pass-through turns the two member pins red. Restore was proven by blob hash.@object-ui/data-objectstack: minor(an additive widening) and@object-ui/app-shell: patch.packages/data-objectstack/plusapp-shell/src/preview/give 1316/1316 under the lock. The closure build and both type-checks exit 0; the battery and the implicated gates exit 0. The full app-shell project was NOT MEASURED locally, as declared; CI's shards cover it, read below.MetadataDraftHeader, the declared row oflistDrafts(), has nomemberalthough the spec declares it, so the hooks read the key throughany. The publish advisory event omitsmember. The review asked for a follow-up card: filed data-objectstack:MetadataDraftHeader(the declared row oflistDrafts()) has nomember, though@objectstack/spec18 declaresdrafts[].memberand the rows carry it — consumers cast toanyto read it #12413 for both.filter=latest), withSpec Main Shape Gate,Type Checkand all 12 test shards included.needs:contract-reviewis removed from the PR in this stroke.Next:
pr_ready+automerge_enable.Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
Landed ·
domain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-15T02:54Zmain919d5cd. The card closedcompletedby the PR'sFixes #12402, which is the only closing keyword. No other card closed with it.origin/main, all 5 files the PR touches are blob-identical to the accepted, contract-reviewed head062328b(PASS6183131810). They aremetadata-client.ts(publishDraft),usePublishAllDrafts.ts, the two console: "Publish all" drops a draft's bundlemember— a package-lessemail_templatelanguage draft is published by type and name only, so the wrong member (or none) is published (the publish half of objectui#12271) #12402 pins and.changeset/12402-publish-all-member.md.MetadataDraftHeadertype gap and the advisory event's missingmemberare data-objectstack:MetadataDraftHeader(the declared row oflistDrafts()) has nomember, though@objectstack/spec18 declaresdrafts[].memberand the rows carry it — consumers cast toanyto read it #12413, for triage.pm:dispatchedis removed in the same stroke as this comment.Generated by Claude Code