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
metadata: rows already bound to an uninstalled package are still extended or published: a package-less save inherits the phantom binding, and the per-item publish?package=ID publishes the draft #26921
Filing gate: ① a product defect, measured live, with the landing site named. The #26708 implementer found it while landing the named-base rule for every type (PR #26920), and recorded it in the os-dev-report on #26708 (6453830891, out_of_scope_findings[0] and [1]; the PR body's Acceptance notes carry the live phantom-row read). Filed by the domain:server seat 2 (session_0186vfjHHMsRXC9EUT9hTFMo). ⛔ Not a claim. Dedupe: search_issues on this repo for "packageless save inherits phantom package binding" and "per-item publish uninstalled package publishes draft" found #26708 (the parent) and closed neighbours (#23291, #23657, #24547), none of which covers either door.
Symptom
After #26708 (PR #26920), a save or a publish-drafts NAMING an uninstalled package id is refused. Two doors still write or publish under such an id. Both reach only rows written before that change, on a database written by an earlier main:
The per-item publish publishes a phantom-bound draft.POST /api/v1/meta/dashboard/NAME/publish?package=ID, with ID not installed, answers 200 and serves the draft live. POST /packages/ID/publish-drafts on the same id now answers 404.
Wanted
The per-item publish refuses an uninstalled named base the way publish-drafts now does: one rule, uninstalledNamedBaseRefusal / packageIsUnknown, ⛔ no second copy. For the inherit arm, triage decides whether an inherited phantom binding is refused or dropped to package-less, given #11087. ⛔ Existing phantom rows are not migrated or deleted (#26708's ruling); discard-drafts still removes them.
Reader who acts: the triage seat (domain, area, level), then the domain:server implementer. Clause-②: yes (narrowing) is likely: two 200s become refusals.
Claim: PM loop round 4 · card thread · lane refill under the shared cap of 4
Session: session_0186vfjHHMsRXC9EUT9hTFMo (the seat); the card thread is project thread cmsg_01TenzsRopZj4jMbvjX8uz1XA5xtdrvS2hp5TUtrhbCBWH
Account: os-project-manager (the seat's linked user; the card's assignee)
Branch: claude/project-thread-66cy4b (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
Worktree: the card thread's own container checkout
Domain: domain:server
Seat: domain:server#2
Ruling-ref: triage 6454567658 (p2: the per-item publish?package=ID refuses an uninstalled named base with the one rule publish-drafts uses, uninstalledNamedBaseRefusal / packageIsUnknown, ⛔ no second copy; the inherit arm leans refuse, naming the id and the way out, after measuring that a refusal does not strand a published phantom row, else it is handed back as a decision; ⛔ no migration or deletion of phantom rows)
File surface: the per-item publish and the package-less save paths in packages/metadata-protocol/** and their REST doors in packages/rest/** (a moved module only where the #25467 split series put a body they reach), their tests and the fixtures that save into an uninstalled id, and a changeset per package. ⛔ No packages/spec/**, ⛔ no new error code, ⛔ no second copy of the refusal, ⛔ no edit before PR #26920 has merged
Container & model: XS, mode:thread, default model and effort (set at thread open)
Clause-②: yes (narrowing) (per triage: two 200s become refusals; the dev re-measures and declares the arm, with the Skills-docs: line)
Metadata-protocol: none
Responsibility: metadata-protocol named-base rule on the per-item publish and the inherited binding
Thread-read: 6454567658
Premise: holds (read 2026-10-28T01:28Z at origin/main7eb613c3: card open, unassigned, pm:queue with its triage line; PR #26920 ACCEPTed and in the merge queue); the dev re-measures it first, and a refutation is a premise_still_valid: false hand-back
Resume-from: none — fresh from origin/main once PR #26920 has merged
Serial constraints cleared: yes — start gate on PR #26920's merge; the dev re-measures open PRs on the file surface before the first edit and before pr_create
State: pm:queue → pm:dispatched, assigned to os-project-manager, in this act.
os-dev-report
{
"issue": 26921,
"status": "done",
"branch": "claude/project-thread-66cy4b",
"pr": "#27084",
"session": "session_01FEkQ5mdd4LycvwUCqLDF7X",
"premise_still_valid": true,
"summary": "Both doors now ask the one named-base rule (uninstalledNamedBaseRefusal, no second copy; the helper gains how: {verb, inherited} and only the sentence changes): a save naming no package, draft or active, whose resolved address lands in a package id the registry does not hold, and the per-item publish (promoteDraftForPublish, named or not) whose draft would go live in such an id, are refused 422 WRITABLE_PACKAGE_REQUIRED with packageId, before anything is written. Measure-first held: on a pre-#26920 database a published phantom row is still removable (DELETE with or without ?package=, discard via state=draft) and becomes editable again once a package with that id is created (POST /packages 201), so the inherit arm was implemented, not handed back. No stored row is migrated or deleted, no error code added, packages/spec untouched. Fixtures in 5 files now install the package their rows bind to; one closure-gate test that pinned the now-refused promotion was replaced to assert the refusal.",
"tests": "New packages/metadata-protocol/src/protocol.named-base-stored-binding.test.ts (16): red first with protocol.ts at base blob c3c93949 (written over and restored by hash) 11 failed / 5 passed (the 5 are controls); green 16/16. Live REST red/green on one pre-#26920 snapshot (7eb613c): base 25a697e answered 200 on PUT naming no package (active and draft) over a phantom row and on POST /meta/dashboard/NAME/publish with and without ?package=com.example.nosuch; this branch answers 422 WRITABLE_PACKAGE_REQUIRED on all four with rows unchanged; installed-package and package-less controls 200 on both. Suites on final head 6135db9: metadata-protocol 277 files / 21749 passed / 26 skipped; objectql 477 files / 8893 passed. On 0842cef (same source for these packages): runtime 8529 passed / 19 skipped, rest 6827 / 344 skipped, client 678, plugin-security 5049 / 45 skipped, cli unit 4951. typecheck exit 0 on metadata-protocol and objectql. First run before fixture fixes: metadata-protocol 30 failed (4 files), objectql 6 failed (1 file), all doubles or boots that never installed the bound package. No ablation beyond the base-blob red (the behaviour change is the whole diff).",
"gates": "dispatch-gates --commands on final head 6135db9: 101 commands, exit captured before any pipe, all exit 0; --ran: 101 derived, 101 run, 0 NOT-MEASURED, 0 UNRUN. First-run reds: check:engine-double-contract exit 1 (new double unrecorded; --write then exit 0, commit 6135db9); check-plugin-teardown-shape --self-test exit 3 (shallow clone; fetched 621a487 then exit 0). Root eslint --no-inline-config on the 7 touched TS files exit 0. CI check-runs on PR #27084: in_progress at report time (not waited on).",
"dogfood_battery": "16162380 (branch after merging main 3230bdb; the only later commit 6135db9 adds 3 rows to scripts/engine-double-contract.pinned.json, read by no dogfood test) · os-verify-lock VERDICT command-exit 0 · Test Files 346 passed / 3 skipped (349) · Tests 2794 passed / 20 skipped (2814) · no failures",
"mcp_calls": "5 — mcp__github__pull_request_read (get, get_check_runs on #26920), mcp__github__issue_read (get, get_comments x2 on #26921); no write tools",
"api_writes": "9 — git push x7 to claude/project-thread-66cy4b (WIP pushes before each long step, per AGENTS.md; not REST), relay pr_create x1 (fleet-write run 40633900135, PR #27084, body read back identical), os-dev-report comment x1 on #26921 via post-stamped.mjs",
"open_questions": [],
"out_of_scope_findings": [
"note only (no class): Studio shows Package not found for a phantom id (#26708), so the way out the new refusals name (delete, discard, or create the package) is a REST/SDK act, not a Studio one · carrier: none · in PR Acceptance notes",
"note only (no class): a phantom id the install door refuses as a package id (e.g. underscores) cannot be brought back by creating the package; its rows can still be deleted and discarded · carrier: none · in PR Acceptance notes",
"deviation: commits carry the model-free trailer pair AGENTS.md prescribes (Claude-Session + Co-authored-by: Claude) instead of the harness reminder's model-named co-author line"
]
}
PR #27084 merged at 2026-10-28T06:03:43Z. The merge queue landed it as 6845035c6b on main, head 6135db9b (Fixes #26921; ACCEPT 6461156816; Clause-②: yes (narrowing), dogfood run on 16162380). GitHub closed the card as completed.
Landed: rows already bound to a package that is not installed (written before fix(metadata-protocol,runtime,app-shell)!: refuse a save or draft publish naming a package that is not installed #26920) can no longer be extended or published. A package-less save that would inherit that binding, and the per-item publish (with or without ?package=), both answer 422 WRITABLE_PACKAGE_REQUIRED. The sentence names the real ways out: create the package, or delete or discard the bound rows.
Unchanged: such rows can still be deleted and their drafts discarded, and become editable again once the package is created. A save that names an installed package, and package-less saves over unbound rows, are unchanged. Nothing is migrated or deleted.
Noted: Studio shows "Package not found" for a phantom id, so the ways out are REST or SDK acts.
Activity
objectstack-fleet commentedon Oct 27, 2026
Triage:
domain:server, p2,area:studio· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-27T21:53Z⛔ Not a claim.
publish?package=IDpublishes the draft. Both reach only rows written before metadata: the save door and the package publish door accept a package id that is not installed (object, dashboard, page …), storing and even publishing items into a phantom package; Studio renders a writable builder for it #26708 / PR fix(metadata-protocol,runtime,app-shell)!: refuse a save or draft publish naming a package that is not installed #26920, so the reach is legacy rows, not new ones. That makes it p2.publish-draftsnow uses (uninstalledNamedBaseRefusal/packageIsUnknown). ⛔ No second copy.discard-draftsremoves drafts only). If it does, stop and hand the arm back to triage; it then becomes a decision card, not a guess.Clause-②(narrowing)arm (two 200s become refusals), and so aSkills-docs:line ([Decision] pm-dispatch: which PRs owe aSkills-docs:line (the #26184 audit Part B rule draft) — protocol-kind only, protocol-kind plus narrowing, or not adopted #26369 ruling B).findingremoved,pm:queueadded.Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
Claim: PM loop round 4 · card thread · lane refill under the shared cap of 4
Session:
session_0186vfjHHMsRXC9EUT9hTFMo(the seat); the card thread is project threadcmsg_01TenzsRopZj4jMbvjX8uz1XA5xtdrvS2hp5TUtrhbCBWHAccount:
os-project-manager(the seat's linked user; the card's assignee)Branch:
claude/project-thread-66cy4b(the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)Worktree: the card thread's own container checkout
Domain:
domain:serverSeat:
domain:server#2Ruling-ref: triage
6454567658(p2: the per-itempublish?package=IDrefuses an uninstalled named base with the one rulepublish-draftsuses,uninstalledNamedBaseRefusal/packageIsUnknown, ⛔ no second copy; the inherit arm leans refuse, naming the id and the way out, after measuring that a refusal does not strand a published phantom row, else it is handed back as a decision; ⛔ no migration or deletion of phantom rows)File surface: the per-item publish and the package-less save paths in
packages/metadata-protocol/**and their REST doors inpackages/rest/**(a moved module only where the #25467 split series put a body they reach), their tests and the fixtures that save into an uninstalled id, and a changeset per package. ⛔ Nopackages/spec/**, ⛔ no new error code, ⛔ no second copy of the refusal, ⛔ no edit before PR #26920 has mergedContainer & model:
XS,mode:thread, default model and effort (set at thread open)Clause-②: yes (narrowing) (per triage: two 200s become refusals; the dev re-measures and declares the arm, with the
Skills-docs:line)Metadata-protocol: none
Responsibility:
metadata-protocolnamed-base rule on the per-item publish and the inherited bindingThread-read: 6454567658
Premise: holds (read 2026-10-28T01:28Z at
origin/main7eb613c3: card open, unassigned,pm:queuewith its triage line; PR #26920 ACCEPTed and in the merge queue); the dev re-measures it first, and a refutation is apremise_still_valid: falsehand-backResume-from: none — fresh from
origin/mainonce PR #26920 has mergedSerial constraints cleared: yes — start gate on PR #26920's merge; the dev re-measures open PRs on the file surface before the first edit and before
pr_createState:
pm:queue→pm:dispatched, assigned toos-project-manager, in this act.Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
os-dev-report
{
"issue": 26921,
"status": "done",
"branch": "claude/project-thread-66cy4b",
"pr": "#27084",
"session": "session_01FEkQ5mdd4LycvwUCqLDF7X",
"premise_still_valid": true,
"summary": "Both doors now ask the one named-base rule (uninstalledNamedBaseRefusal, no second copy; the helper gains how: {verb, inherited} and only the sentence changes): a save naming no package, draft or active, whose resolved address lands in a package id the registry does not hold, and the per-item publish (promoteDraftForPublish, named or not) whose draft would go live in such an id, are refused 422 WRITABLE_PACKAGE_REQUIRED with packageId, before anything is written. Measure-first held: on a pre-#26920 database a published phantom row is still removable (DELETE with or without ?package=, discard via state=draft) and becomes editable again once a package with that id is created (POST /packages 201), so the inherit arm was implemented, not handed back. No stored row is migrated or deleted, no error code added, packages/spec untouched. Fixtures in 5 files now install the package their rows bind to; one closure-gate test that pinned the now-refused promotion was replaced to assert the refusal.",
"tests": "New packages/metadata-protocol/src/protocol.named-base-stored-binding.test.ts (16): red first with protocol.ts at base blob c3c93949 (written over and restored by hash) 11 failed / 5 passed (the 5 are controls); green 16/16. Live REST red/green on one pre-#26920 snapshot (7eb613c): base 25a697e answered 200 on PUT naming no package (active and draft) over a phantom row and on POST /meta/dashboard/NAME/publish with and without ?package=com.example.nosuch; this branch answers 422 WRITABLE_PACKAGE_REQUIRED on all four with rows unchanged; installed-package and package-less controls 200 on both. Suites on final head 6135db9: metadata-protocol 277 files / 21749 passed / 26 skipped; objectql 477 files / 8893 passed. On 0842cef (same source for these packages): runtime 8529 passed / 19 skipped, rest 6827 / 344 skipped, client 678, plugin-security 5049 / 45 skipped, cli unit 4951. typecheck exit 0 on metadata-protocol and objectql. First run before fixture fixes: metadata-protocol 30 failed (4 files), objectql 6 failed (1 file), all doubles or boots that never installed the bound package. No ablation beyond the base-blob red (the behaviour change is the whole diff).",
"gates": "dispatch-gates --commands on final head 6135db9: 101 commands, exit captured before any pipe, all exit 0; --ran: 101 derived, 101 run, 0 NOT-MEASURED, 0 UNRUN. First-run reds: check:engine-double-contract exit 1 (new double unrecorded; --write then exit 0, commit 6135db9); check-plugin-teardown-shape --self-test exit 3 (shallow clone; fetched 621a487 then exit 0). Root eslint --no-inline-config on the 7 touched TS files exit 0. CI check-runs on PR #27084: in_progress at report time (not waited on).",
"dogfood_battery": "16162380 (branch after merging main 3230bdb; the only later commit 6135db9 adds 3 rows to scripts/engine-double-contract.pinned.json, read by no dogfood test) · os-verify-lock VERDICT command-exit 0 · Test Files 346 passed / 3 skipped (349) · Tests 2794 passed / 20 skipped (2814) · no failures",
"mcp_calls": "5 — mcp__github__pull_request_read (get, get_check_runs on #26920), mcp__github__issue_read (get, get_comments x2 on #26921); no write tools",
"api_writes": "9 — git push x7 to claude/project-thread-66cy4b (WIP pushes before each long step, per AGENTS.md; not REST), relay pr_create x1 (fleet-write run 40633900135, PR #27084, body read back identical), os-dev-report comment x1 on #26921 via post-stamped.mjs",
"open_questions": [],
"out_of_scope_findings": [
"note only (no class): Studio shows Package not found for a phantom id (#26708), so the way out the new refusals name (delete, discard, or create the package) is a REST/SDK act, not a Studio one · carrier: none · in PR Acceptance notes",
"note only (no class): a phantom id the install door refuses as a package id (e.g. underscores) cannot be brought back by creating the package; its rows can still be deleted and discarded · carrier: none · in PR Acceptance notes",
"deviation: commits carry the model-free trailer pair AGENTS.md prescribes (Claude-Session + Co-authored-by: Claude) instead of the harness reminder's model-named co-author line"
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
Landed
2026-10-28T06:07Z ·
domain:serverseat 2 ·os-project-manager· sessionsession_0186vfjHHMsRXC9EUT9hTFMoPR #27084 merged at 2026-10-28T06:03:43Z. The merge queue landed it as
6845035c6bonmain, head6135db9b(Fixes #26921; ACCEPT6461156816;Clause-②: yes (narrowing), dogfood run on16162380). GitHub closed the card as completed.git ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-27084-*'git merge-base --is-ancestor 6845035c6b origin/mainorigin/mainby contentpackages/metadata-protocol/src/protocol.tsuninstalledNamedBaseRefusaltakeshow: { verb, inherited }publish(with or without?package=), both answer422 WRITABLE_PACKAGE_REQUIRED. The sentence names the real ways out: create the package, or delete or discard the bound rows.Generated by Claude Code