client: meta.publishItem cannot name a package, so the ?package= remedy that PR #23654's narrowed publish answers name is unreachable through the SDK (#23293's twin) #23658
Description
Filing gate: ① a product defect with a named producer, finding class (c): a refusal whose remedy an SDK caller cannot send. It comes from #23495's dev report 6189982386 (open_questions[1]) and the contract review 6190278990 on PR #23654 (③ OQ2, which escalates it for this card). Filed by domain:engine seat 1 (#6367) · sunhaolin · session_01GP3comALHRcRBixS2TEnLF. ⛔ Not a claim. Reader who acts: triage, to grade and route (the producer is packages/client). Order: after PR #23654 (#23495) lands. Its new refusals are what name the remedy.
reach: client.meta.publishItem, the named producer in @objectstack/client, which has no package option.
What happens
After PR #23654, a per-item publish naming no package is refused with a remedy that is the request's ?package=:
409 RESOURCE_CONFLICT, for an item two or more packages hold;404 NO_DRAFT, when the only draft is bound to another package.
client.meta.publishItem cannot send ?package=, so an SDK caller, and an AI author working through the SDK, is told a remedy it cannot take. The remedy is reachable over REST and in process.
This is #23293's twin. That closed card covered the same gap for meta.deleteItem after #23165's refusals: the SDK could not pass the ?package= remedy. saveItem and deleteItem now carry the option, and publishItem does not.
Wanted
publishItemtakes an optional package (opts.packageId), sent as?package=, mirroringsaveItemanddeleteItem, as client:meta.deleteItemcannot name a package — after #23165 a delete of a name two packages hold answers 409, and the SDK has no way to pass the?package=remedy #23293 settled for delete.- Pins:
- the option reaches the wire, and an absent option sends no
packagekey; - a named publish of the two-package item succeeds through the SDK.
- the option reaches the wire, and an absent option sends no
Clause-②: yes(it widens@objectstack/client's public surface). The claimant's changeset is at leastminor.
Dedupe
MCP search_issues on this repo, closed included: "client.meta.publishItem package option packageId ?package= SDK publish cannot name package" returns 10 hits.
- The nearest is client:
meta.deleteItemcannot name a package — after #23165 a delete of a name two packages hold answers 409, and the SDK has no way to pass the?package=remedy #23293 (closed:meta.deleteItemcannot name a package), the twin.
None is this.
Dedupe words: publishItem package option; SDK publish ?package=; per-item publish client packageId.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 15, 2026
Triage: first grade ·
bug·priority:p2·domain:cli·pm:queue· land after PR #23654 · triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-15T11:56ZRead fresh. After PR #23654, a per-item publish naming no package is refused with a
?package=remedy (409for a multi-package item,404 NO_DRAFTwhen the only draft is another package's).client.meta.publishItemhas no package option, so an SDK caller, or an AI author working through the SDK, is told a remedy it cannot send. This is #23293's twin fordeleteItem.Grade p2: a refusal whose remedy one producer cannot take. The remedy is reachable over REST and in process.
Clause-②: yesfor@objectstack/client, so the changeset isminor.Lane
domain:cli(packages/client*, by the lane table).Order: it lands after PR #23654 (#23495), whose refusals name the remedy.
Scope:
publishItemtakes an optionalopts.packageId, sent as?package=, mirroringsaveItemanddeleteItem.findingleaves on grading.objectstack-fleet commentedon Oct 15, 2026
Claim: PM loop round 18 (
client.meta.publishItemtakes an optionalpackageId, sent as?package=, so the remedy PR #23654's narrowed publish names is reachable through the SDK; #23293's twin) · 2026-10-15T12:05ZSession:
session_01Q9PZ5Pzqcus4woVvTZwWNUAccount:
sunhaolin(the seat's linked user asGET /useranswers it; the card's assignee from this act)Branch:
claude/issue-23658-client-publish-packageWorktree:
objectstack-issue-23658Domain:
domain:cliSeat:
domain:cli#2(seat post #22648)Scope:
batch3, by 「并发恢复3」. #23531's remainder (PR #23667) is in the merge queue and #23546's build is in flight; this card takes the free third slot.Order, read at this act:
6191473535orders this card after PR fix(metadata-protocol)!: the per-item publish and the draft discard that name no package act on the draft the draft save writes (#23495) #23654 (metadata-protocol: the draft doors naming no package resolve three addresses — the save and read inherit the active binding, the discard reads the draft rows, the publish takes the first draft row a package-agnostic findOne returns #23495).57994c311f(noworigin/main).priority:p1lane card is inpm:queue. This is the only p2 one.File surface (read on
origin/main; stop on a breach and explain it in the report):packages/client/src/index.ts:meta.publishItem(:2239) and its docblock. The query is built the waymetaSaveQuery(:787) andmetaDeleteQuery(:946) build theirs: only a non-empty id reaches the wire.packages/client/src/. The wire rows (the option reaches the wire aspackage=;undefinedand''send nothing), plus the real-door row: a publish naming the package of an item two packages hold succeeds through the SDK, where the unnamed one is refused. The real-door rig is client:meta.deleteItemcannot name a package — after #23165 a delete of a name two packages hold answers 409, and the SDK has no way to pass the?package=remedy #23293's (meta-delete-item-carriers.test.ts)..changeset/23658-*.md,@objectstack/clientminor.metadata-protocol,packages/restorpackages/runtime(the doors are not edited here);saveItemordeleteItem;meta(if it lackspublishItem, that is a report line, not this card).Container & model:
S, judged (one optional member on one method, the twin's rig),mode:subagent,model: default tier. Ceiling:Clause-②: yes (widening),minor.Clause-②: yes (widening)
Responsibility:
@objectstack/client's meta.publishItem option (this lane) | the publish door's ?package= read (metadata-protocol / rest, PR #23654), read | SDK callers and AI authors publishing a multi-package itemThread-read: 6191473535
Serial constraints cleared: no open PR touches
packages/client/src/(read at this act). The Version Packages PR chore: version packages (next) #23481 touches onlypackages/client's CHANGELOG and package.json.Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
os-dev-report
{ "issue": 23658, "status": "done", "branch": "claude/issue-23658-client-publish-package", "pr": "https://github.com/objectstack-ai/objectstack/pull/23680", "session": "session_01Q9PZ5Pzqcus4woVvTZwWNU (subagent; the parent PM's harness-stamped id, the session of claim 6191624811)", "premise_still_valid": true, "summary": "client.meta.publishItem(type, name, opts?) gains opts.packageId?: string, sent as ?package=ID on POST /meta/:type/:name/publish with metaSaveQuery's guard (URLSearchParams.set; undefined and '' send nothing; an options-less publish is byte-identical to before). message still rides the body. The docblock names the option and both refusals PR #23654 added (409 RESOURCE_CONFLICT, two or more packages hold the item; 404 NO_DRAFT, the item's one package has no draft and another package's draft is stored). Changeset .changeset/23658-client-publish-package.md: @objectstack/client minor, feat(client):, Clause-②: yes (widening), in #23293's house form. Pins in a NEW file, packages/client/src/meta-publish-item-package.test.ts: the twin's rig is local to its test file, and importing it would re-register every suite of that file; a helper under src/ would sit in the build program. So the rig is copied (about 90 lines), and its imports equal the twin's, leaving check:test-source-alias's @objectstack/client set unchanged. The claim's newest Claim: was verified as 6191624811 before any work. origin/main was merged in before the PR (AGENTS §10) as dbdcf90560. Worktree removed after the PR opened (node_modules first, git worktree remove without --force, observed gone).", "hypotheses": { "a_one_declaration": "HOLDS. publishItem is declared only on the unscoped ObjectStackClient.meta. The scoped client.environment(id).meta (index.ts :7698-:7814 at base 57994c311f) declares getTypes, getItems, getItem, saveItem, deleteItem and getHistory, and no publishItem. No method added there. A wire row pins the absence, with deleteItem on the same object as its positive control.", "b_doors_read_package": "REST door: reads it (rest-server.ts :7973 metaItemPackageBinding(req.query?.package), spread into publishMetaItem's request at :8010); the real-door rows drive it. Dispatcher (createHonoApp catch-all): serves NO per-item publish door. Measured on runtime dist built at 57994c311f: HttpDispatcher.handleMetadata('/view/x/publish', ctx, 'POST', {}, {package:'pkg'}) answered handled:true, 404 ROUTE_NOT_FOUND 'Route Not Found: /meta/view/x/publish', spy publishMetaItem called 0 times. The control on the same instrument, PUT /view/x?package=pkg, answered 200, and saveMetaItem received packageId 'pkg'. createMetaDomain's handler and handleMetadata both call handleMetadataRequest. The route is RestServer-owned (rest-route-ledger.ts :268, source route-manager), and the runtime route ledger has no row for it. So there is no ?package= forwarding question on the dispatcher, and no fix; see out_of_scope_findings. NOT MEASURED: the full dispatch() leg (401 UNAUTHENTICATED with identity stubbed, and the same 401 on its PUT control, so it never reached the domain)." }, "files_changed": [ ".changeset/23658-client-publish-package.md", "packages/client/src/index.ts (meta.publishItem and its docblock only)", "packages/client/src/meta-publish-item-package.test.ts (new)" ], "tests": "Final head dbdcf90560 (git rev-parse --short HEAD), main merged at a4b87ef556, all through os-verify-lock with OS_VERIFY_LOCK_SLOT=issue-23658. (1) turbo run build --filter=@objectstack/client... : 34/34 successful, VERDICT command-exit 0. (2) pnpm --filter @objectstack/spec check:generated: 'All 14 generated artifacts are up to date', exit 0. (3) pnpm --filter @objectstack/client typecheck: exit 0, with 'check:test-typecheck: OK ... 0 error(s)'. (4) pnpm --filter @objectstack/client exec vitest run --maxWorkers=2: 'Test Files 52 passed (52) / Tests 671 passed (671)', exit 0. At 0405b4f4cd, before the merge: client^... closure build 33/33; typecheck exit 0; by path (meta-publish-item-package.test.ts + client.test.ts) 'Test Files 2 passed / Tests 220 passed'; the new file verbose 8/8 passed (wire 5, real door 3, tests 609ms); full suite 52 files / 671 passed. ABLATION (one-shot, at 0405b4f4cd): node scripts/ablation-replace.mjs replaced the params.set('package', ...) line with a marker. On disk: anchor x1 -> x0, marker x0 -> x1, set-line grep -c 2 -> 1 (getDiagnostics keeps its own), blob 6fcd25dbf4 -> c5a85383a8. Traps: the tool's own restore trap plus an outer trap EXIT INT TERM doing git checkout HEAD on an absolute path with a blob-hash check. No build leg: the test imports ObjectStackClient from ./index (source), not through exports. Declared direction 5 red / 3 green; observed 'Tests 5 failed | 3 passed (8)'. Red: the ?package= wire row; the message row (expected '' to be '?package=com.qa.alpha'); the encoding row; real-door NAMING ONE (door refused RESOURCE_CONFLICT httpStatus 409); the NO_DRAFT row (refused NO_DRAFT 404). Green: the negative control, the scoped absence, NAMING NONE. Restore proven twice: blob 6fcd25dbf4 == HEAD blob, git diff HEAD empty, porcelain empty.", "gates": "LOCAL at dbdcf90560: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 63 commands (stderr: tree objectstack-ai/objectstack at dbdcf90560; same 63 as at 0405b4f4cd). I ran all 63, plus the 4 roster families with a roster in my paths' directories (node scripts/check-changeset-fixed.mjs, check:authz-resolver, check:error-code-casing, check:filter-alias-parity), each exit code captured before any pipe: 67/67 exit 0. pnpm check:type-check-debt first exited 3 (its internal closure build got no lock turn, a sibling held 1409s); re-run whole under os-verify-lock: 're-measure: OK ... 26 raw tsc error(s) total, none above its recorded number', VERDICT command-exit 0. --ran: 'Run reconciliation - 63 derived, 63 run, 0 NOT-MEASURED, 0 UNRUN', exit 0. At 0405b4f4cd the same 63 gave 61 exit 0 and 2 exit 3 PREREQUISITE NOT MET (spec check:skill-examples wanted client-react dist; check:dual-build-cjs-loads unbuilt siblings), and --ran said 61 run / 2 NOT-MEASURED. Both measured green at dbdcf90560: skill-examples '263 prose examples type-check'; dual-build-cjs-loads exit 0. Verdict lines: adr-0087-registration 'no declared-breaking changeset (1 non-breaking)'; changeset-fixed '72 public workspace packages in sync'; test-source-alias OK; cross-package-test-inputs OK; dts-closure 120/120; nul-bytes OK; published-files pass. LINT: pnpm lint narrowed (declared) to eslint --no-inline-config on the 2 changed TS files, with 3 pieces of evidence: (1) exit 0; (2) --print-config applies 6 and 5 rules, so the files are not ignored; (3) positive control via --stdin (a restricted @objectstack/spec root import appended to the new file) exits 1 with no-restricted-imports. CI's 'Lint · ESLint' lane runs the full pnpm lint. main moved 2 commits past dbdcf90560 (to 8d1fb1fb2d; 22 paths, 0 in packages/client); CI runs on the merge ref. CI on PR #23680 head dbdcf90560 at 2026-10-15T13:42Z, 37 check-runs: 14 success (Governed Surface Queue Guard, Check Changeset, Lint · PM dispatch-gates, Lint · scoped · tooling self-tests, Dogfood Regression Gate, Check PR Size, Auto Label, Check Documentation Links, the four claim/closing guards, filter, Flag docs affected), 6 skipped (path-filtered: Build Docs, Console Pin Gate, Dogfood matrix template, Dogfood Verify CLI, Temporal Conformance, Packed-tarball smoke), 17 in_progress (Test Core 1-6, Build Core, Type Check lanes, Lint · ESLint, Lint · repo & tooling gates, and the other Lint lanes). None red. Lint & Repo Gates and TypeScript Type Check aggregates not yet posted.", "mcp_calls": "0", "api_writes": "2 relay strokes (POST /repos/objectstack-ai/objectstack/dispatches, 204 each, run as objectstack-fleet[bot]). Stroke 1, run 38744667364 success: pr_create = POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos/objectstack-ai/objectstack/issues/23680/assignees ['sunhaolin']; read-back 10304 bytes sent, 10304 stored, identical. Stroke 2: comment = POST /repos/objectstack-ai/objectstack/issues/23658/comments (this report). Label writes: 0 (the dispatch names none; skip-changeset does not apply because the diff publishes). git push x4, not REST: empty branch probe, implementation, pins, the main merge.", "deviations": [ "Lint: the dispatch asks for pnpm lint or the proven narrowing; the role file holds repo-wide scans as CI's. I ran the narrowing with three pieces of evidence (see gates) and did not run pnpm lint locally. Conflict surfaced here, not resolved silently.", "Pins: a third real-door row (404 NO_DRAFT, then the named package promoted) beyond the card's two, inside the claimed file. The docblock names both refusals, so both are pinned.", "Merged origin/main (a4b87ef556) into the branch BEFORE opening the PR, per AGENTS §10, after --ran flagged the tree 7 commits stale. Overlap measured: only metadata-protocol protocol.ts (an object-type read filter, not the publish path). Every reading was re-taken at the merged head dbdcf90560.", "Commit trailers use the model-free pair AGENTS.md prescribes (Claude-Session + Co-authored-by: Claude), not the harness reminder's model-named Co-Authored-By. The PR footer is AGENTS.md's session-URL form.", "One pipe-trap slip, caught and re-measured: a 'git diff --name-only | grep | head' overlap check read head's exit code. Re-run unpiped: grep -c 0, exit 1, so no overlap. Nothing reported rests on the slipped reading." ], "open_questions": [], "out_of_scope_findings": [ "carrier: none · noted in PR #23680 Acceptance notes, not filed (an observation, no contract violated). The dispatcher path a createHonoApp catch-all reaches serves no POST /meta/:type/:name/publish: measured 404 ROUTE_NOT_FOUND, protocol not called (hypothesis b). The route is RestServer-owned (rest-route-ledger.ts row, source route-manager; no runtime route-ledger row), and the runtime ledger has no DELETE /meta/:type/:name row either. So on a host composing only the dispatcher catch-all without RestServer, client.meta.publishItem and client.meta.deleteItem answer a located 404 whatever the package. That fits route rule 1 (one owner) and rule 3 (absence is loud). Dedupe words: dispatcher meta per-item publish ROUTE_NOT_FOUND; createHonoApp RestServer-only meta item routes; SDK publishItem deleteItem adapter host 404." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
ACCEPT → PR #23680 comment
6193956523·TIER_DEFAULTrecord6193948392·domain:cliseat 2 · 2026-10-15T14:03ZGenerated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
Landed: PR #23680 →
361a1c46d3· 2026-10-15T14:43Zdomain:cliseat 2 (#22648) ·sunhaolin· sessionsession_01Q9PZ5Pzqcus4woVvTZwWNU· holder of claim6191624811, released by this landing. Review: this seat'sTIER_DEFAULTrecord6193948392(PASS) and ACCEPT6193956523on the PR (pointer on this card).merged: true,merged_at2026-10-15T14:42Z, through the merge queue (added_to_merge_queue2026-10-15T14:06Z)gh-readonly-queue/main/pr-23680-*git ls-remote, 0 refs)origin/main361a1c46d3is an ancestor; one parent (c24598658d); 3 files, +473 / −2, as reviewed atdbdcf90560Fixes #23658onlydomain:clicards (read at this stamp), #23658 no longer among themState change: #23658 closed
completedby the merge;pm:dispatchedremoved in this act.client.meta.publishItemnow sends?package=, so the remedy PR #23654's narrowed publish names is reachable through the SDK.Noted, not filed (「承接者:无」): the dispatcher catch-all serves no per-item publish or delete route (
RestServer-owned); no deployment composing the dispatcher withoutRestServeris named.Generated by Claude Code