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
runtime: POST /api/v1/packages/:id/publish-drafts?dryRun=true ignores the unknown query parameter and performs a real publish #26713
Filing gate: ① product defect, reproduced, landing site named.
Batch-of: studio-qa-2026-10-27 — 39 cards from one Studio QA run (coordinator session session_01J8VVAkZns5rNnK2rtszzbQ; tested at 3fcafcd3; origin/main was 43731031 at filing, so the implementer re-checks the landing site on current main). The run record is the qa-run issue filed with this batch.
Symptom
A caller asking for a dry run gets a real, irreversible publish; the door has no closed query set.
Reproduction
Create a package with one pending draft.
POST /api/v1/packages/<id>/publish-drafts?dryRun=true with {} → 200 outcome:"published"; the draft is live.
Expected
An unknown query parameter is refused (closed query set, AGENTS rule), or dryRun is implemented as a real dry run.
p1:publish-drafts?dryRun=true ignores the unknown parameter and performs a real, irreversible publish. Close the query set: refuse an unknown parameter with 400. If a dry run is wanted, it is a declared parameter, and that is a protocol addition that comes back to triage. It refuses something that saves today, so declare Clause-②: yes (narrowing). Handled directly in 18.x. From the studio-qa-2026-10-27 batch. Re-check the landing site on current main.
Claim: PM loop round 4 · card thread · 18.2.0 wave-2 split with server seat 1
Session: session_0186vfjHHMsRXC9EUT9hTFMo (the seat); the card thread is session_01FEkQ5nX7RrMithjQTtAtVb
Account: os-project-manager (the seat's linked user; the card's assignee)
Branch: claude/project-thread-g4y5hj (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 6446824931 (p1: close the query set, refuse an unknown parameter with 400; a dry run is a protocol addition that returns to triage); seat ruling: the #23072 refusal shape, one helper, ⛔ no dryRun implementation
File surface: packages/runtime/src/domains/packages.ts (the publish-drafts route's closed query set, and discard-drafts if it shares the block), the shared query-refusal helper if it is lifted out of domains/meta.ts, the RestServer route if it serves the same door, their tests, and a changeset per package. ⛔ No dryRun, ⛔ no new error code, ⛔ no other package door (proposed as cards)
Container & model: S, mode:thread, default model and effort (set at thread open)
Clause-②: yes (narrowing) per triage, with the Skills-docs: line; the dev re-measures
Metadata-protocol: none
Responsibility: runtime packages domain publish-drafts door (closed query set, #23072 precedent)
Thread-read: 6446824931
Premise: holds (read 2026-10-27T17:05Z at origin/mainc8583f80ac: card open, unassigned, pm:queue with its triage line; the split with server seat 1 gives this card to seat 2); the dev re-measures it first, and a refutation is a premise_still_valid: false hand-back
Resume-from: none — fresh from origin/main
Serial constraints cleared: yes — 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": 26713,
"status": "done",
"branch": "claude/project-thread-g4y5hj",
"pr": "#26853",
"session": "session_01FEkQ5nX7RrMithjQTtAtVb",
"premise_still_valid": true,
"summary": "Premise held on main c8583f8: POST /packages/:id/publish-drafts?dryRun=true answered 200 outcome published and the draft went live (real protocol probe over SQLite). The two package draft doors (publish-drafts and its twin discard-drafts, where the same parameter dropped every pending draft) now declare their closed query set, measured EMPTY, and refuse any query name 400 VALIDATION_ERROR with the shared recognition sentence unknownQueryParamRefusalMessage from @objectstack/rest, written through deps.error with the code as the /meta doors write it (#23072): one helper refuseDraftDoorQuery in packages.ts, called after requireManageMetadata and the wall gate and before the protocol (the #23214 placement); no new sentence, no new code, no dryRun. Only the runtime dispatcher serves these doors (RestServer mounts neither), and no caller in ui, packages/client, the CLI or skills sends a query parameter to them. Reading taken: discard-drafts is a separate if-block beside publish-drafts, not literally the same block; I closed it here as the twin draft door the order names, with the same one-line call, because the same parameter there is the same irreversible act. If the seat reads the order strictly, that call and its three test cases come out cleanly. #26708 had no open PR at start or before pr_create; my hunk is one line at each route entry after the wall gate.",
"tests": "Head e542b23. New packages/runtime/src/domains/packages-draft-doors-closed-query.test.ts, 10 cases via dispatch() over a real SchemaRegistry: per door, dryRun / repeated dryRun / two unknown names answer 400 VALIDATION_ERROR with exactly unknownQueryParamRefusalMessage(query, []) and the protocol never called; no query answers 200 with the protocol called once; a caller without manage_metadata gets 403 first. Red on base c8583f8: 6 failed | 4 passed (expected 400, got 200 outcome published; discard twin discardedCount 1). Real-door probe (one-off, not committed): base status 200 outcome published, draftsAfter empty, served label Board v1; head status 400 VALIDATION_ERROR naming dryRun, draftsAfter still view/repairs_board, nothing served. Ablation via scripts/ablation-replace.mjs (guard forced to return null, anchor 1 to 0, blob 8dcfe74db65c to f592f4389363): 6 failed | 4 passed, exactly the refusal cases; restored blob == HEAD, git diff HEAD empty (test imports src, no build between). pnpm --filter @objectstack/runtime test: 460/460 files, 7458 passed | 19 skipped. runtime typecheck exit 0. eslint packages/runtime --no-inline-config exit 0. test:repo not run locally (no repo-project test touched, not derived); CI Test Core runs it. Evidence: pm-orders/server/evidence/26713/.",
"gates": "Local: dispatch-gates --commands on this diff derived 60 families; all 60 run, all exit 0; --ran: 60 derived famil(ies) accounted for, 60 run, 0 NOT-MEASURED (ran-final-e542b231.txt). First pass had two container prerequisites (exit 3), re-run after fixing: check-plugin-teardown-shape --self-test (positive control 621a487 fetched, 48 cases pass) and check:dual-build-cjs-loads (7 packages without dist built; 112 entries / 69 packages load). Hand: check-commit-card-trailers --range origin/main..HEAD exit 0, check-nul-bytes exit 0. CI check-runs: in_progress at report time, not waited on.",
"dogfood_battery": "e542b231 · os-verify-lock VERDICT command-exit 0 (held 21m04s) · Test Files 314 passed | 3 skipped (317) · Tests 2609 passed | 20 skipped (2629) · no failures",
"mcp_calls": "7 — all reads: issue_read x4 (26713 get, 26713 get_comments x2, 26708 get), search_pull_requests x3 (26708 PR lookups). No MCP write.",
"api_writes": "2 relay strokes — pr_create (POST /repos/objectstack-ai/objectstack/pulls draft + POST issues/26853/assignees os-project-manager; read-back identical, 9495 bytes) and this os-dev-report comment (POST issues/26713/comments). Plus one git push (not REST).",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: dispatcher door DELETE /api/v1/packages/:id?keepdata=true (also ?keep_data=true), measured on c8583f8 with the packages-write-flag-arity harness: 200, deletePackage called with packageId only and no keepData, so the protocol drops the object tables the caller asked to keep (evidence pm-orders/server/evidence/26713/probe-siblings.log) · family: package write doors with no closed query set, measured on c8583f8: POST /packages (install, reads overwrite), DELETE /packages/:id (reads keepData), PATCH /packages/:id, PATCH enable, PATCH disable, POST publish, revert, rollback, commits/:c/revert, adopt-orphans, duplicate (none reads the query), and RestServer POST /packages/publish (reads none); already closed: POST /meta/:type/:name/publish (META_ITEM_PUBLISH_PARAMS, #22877) · proposed card: close the query sets of the remaining package write doors, uninstall first · dedupe words: packages keepData misspelled query parameter ignored, uninstall drops tables, package write door closed query set"
]
}
PR #26853 merged at 2026-10-27T19:24:02Z by the merge queue as 96e7a4335c on main (Fixes #26713; ACCEPT 6450984821; Clause-②: yes (narrowing) with Skills-docs: none). GitHub closed the card as completed.
Landed:POST /packages/:id/publish-drafts and POST /packages/:id/discard-drafts now declare an empty closed query set. Any query parameter, dryRun included, gets 400 VALIDATION_ERROR with the shared unknown-query sentence. The check runs after the capability and wall gates and before the protocol, so a refused call publishes or discards nothing. Before this change, ?dryRun=true answered 200 and published every pending draft.
Activity
objectstack-fleet commentedon Oct 27, 2026
Triage: first grade —
priority:p1·domain:server·area:studio·pm:queue(findingremoved) · triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-27T15:58Z⛔ Not a claim, ⛔ not a dispatch.
publish-drafts?dryRun=trueignores the unknown parameter and performs a real, irreversible publish. Close the query set: refuse an unknown parameter with 400. If a dry run is wanted, it is a declared parameter, and that is a protocol addition that comes back to triage. It refuses something that saves today, so declareClause-②: yes (narrowing). Handled directly in 18.x. From the studio-qa-2026-10-27 batch. Re-check the landing site on currentmain.Generated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
Claim: PM loop round 4 · card thread · 18.2.0 wave-2 split with server seat 1
Session:
session_0186vfjHHMsRXC9EUT9hTFMo(the seat); the card thread issession_01FEkQ5nX7RrMithjQTtAtVbAccount:
os-project-manager(the seat's linked user; the card's assignee)Branch:
claude/project-thread-g4y5hj(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
6446824931(p1: close the query set, refuse an unknown parameter with 400; a dry run is a protocol addition that returns to triage); seat ruling: the #23072 refusal shape, one helper, ⛔ nodryRunimplementationFile surface:
packages/runtime/src/domains/packages.ts(thepublish-draftsroute's closed query set, anddiscard-draftsif it shares the block), the shared query-refusal helper if it is lifted out ofdomains/meta.ts, theRestServerroute if it serves the same door, their tests, and a changeset per package. ⛔ NodryRun, ⛔ no new error code, ⛔ no other package door (proposed as cards)Container & model:
S,mode:thread, default model and effort (set at thread open)Clause-②: yes (narrowing) per triage, with the
Skills-docs:line; the dev re-measuresMetadata-protocol: none
Responsibility:
runtimepackages domainpublish-draftsdoor (closed query set, #23072 precedent)Thread-read: 6446824931
Premise: holds (read 2026-10-27T17:05Z at
origin/mainc8583f80ac: card open, unassigned,pm:queuewith its triage line; the split with server seat 1 gives this card to seat 2); the dev re-measures it first, and a refutation is apremise_still_valid: falsehand-backResume-from: none — fresh from
origin/mainSerial constraints cleared: yes — 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 27, 2026
os-dev-report
{
"issue": 26713,
"status": "done",
"branch": "claude/project-thread-g4y5hj",
"pr": "#26853",
"session": "session_01FEkQ5nX7RrMithjQTtAtVb",
"premise_still_valid": true,
"summary": "Premise held on main c8583f8: POST /packages/:id/publish-drafts?dryRun=true answered 200 outcome published and the draft went live (real protocol probe over SQLite). The two package draft doors (publish-drafts and its twin discard-drafts, where the same parameter dropped every pending draft) now declare their closed query set, measured EMPTY, and refuse any query name 400 VALIDATION_ERROR with the shared recognition sentence unknownQueryParamRefusalMessage from @objectstack/rest, written through deps.error with the code as the /meta doors write it (#23072): one helper refuseDraftDoorQuery in packages.ts, called after requireManageMetadata and the wall gate and before the protocol (the #23214 placement); no new sentence, no new code, no dryRun. Only the runtime dispatcher serves these doors (RestServer mounts neither), and no caller in ui, packages/client, the CLI or skills sends a query parameter to them. Reading taken: discard-drafts is a separate if-block beside publish-drafts, not literally the same block; I closed it here as the twin draft door the order names, with the same one-line call, because the same parameter there is the same irreversible act. If the seat reads the order strictly, that call and its three test cases come out cleanly. #26708 had no open PR at start or before pr_create; my hunk is one line at each route entry after the wall gate.",
"tests": "Head e542b23. New packages/runtime/src/domains/packages-draft-doors-closed-query.test.ts, 10 cases via dispatch() over a real SchemaRegistry: per door, dryRun / repeated dryRun / two unknown names answer 400 VALIDATION_ERROR with exactly unknownQueryParamRefusalMessage(query, []) and the protocol never called; no query answers 200 with the protocol called once; a caller without manage_metadata gets 403 first. Red on base c8583f8: 6 failed | 4 passed (expected 400, got 200 outcome published; discard twin discardedCount 1). Real-door probe (one-off, not committed): base status 200 outcome published, draftsAfter empty, served label Board v1; head status 400 VALIDATION_ERROR naming dryRun, draftsAfter still view/repairs_board, nothing served. Ablation via scripts/ablation-replace.mjs (guard forced to return null, anchor 1 to 0, blob 8dcfe74db65c to f592f4389363): 6 failed | 4 passed, exactly the refusal cases; restored blob == HEAD, git diff HEAD empty (test imports src, no build between). pnpm --filter @objectstack/runtime test: 460/460 files, 7458 passed | 19 skipped. runtime typecheck exit 0. eslint packages/runtime --no-inline-config exit 0. test:repo not run locally (no repo-project test touched, not derived); CI Test Core runs it. Evidence: pm-orders/server/evidence/26713/.",
"gates": "Local: dispatch-gates --commands on this diff derived 60 families; all 60 run, all exit 0; --ran: 60 derived famil(ies) accounted for, 60 run, 0 NOT-MEASURED (ran-final-e542b231.txt). First pass had two container prerequisites (exit 3), re-run after fixing: check-plugin-teardown-shape --self-test (positive control 621a487 fetched, 48 cases pass) and check:dual-build-cjs-loads (7 packages without dist built; 112 entries / 69 packages load). Hand: check-commit-card-trailers --range origin/main..HEAD exit 0, check-nul-bytes exit 0. CI check-runs: in_progress at report time, not waited on.",
"dogfood_battery": "e542b231 · os-verify-lock VERDICT command-exit 0 (held 21m04s) · Test Files 314 passed | 3 skipped (317) · Tests 2609 passed | 20 skipped (2629) · no failures",
"mcp_calls": "7 — all reads: issue_read x4 (26713 get, 26713 get_comments x2, 26708 get), search_pull_requests x3 (26708 PR lookups). No MCP write.",
"api_writes": "2 relay strokes — pr_create (POST /repos/objectstack-ai/objectstack/pulls draft + POST issues/26853/assignees os-project-manager; read-back identical, 9495 bytes) and this os-dev-report comment (POST issues/26713/comments). Plus one git push (not REST).",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: dispatcher door DELETE /api/v1/packages/:id?keepdata=true (also ?keep_data=true), measured on c8583f8 with the packages-write-flag-arity harness: 200, deletePackage called with packageId only and no keepData, so the protocol drops the object tables the caller asked to keep (evidence pm-orders/server/evidence/26713/probe-siblings.log) · family: package write doors with no closed query set, measured on c8583f8: POST /packages (install, reads overwrite), DELETE /packages/:id (reads keepData), PATCH /packages/:id, PATCH enable, PATCH disable, POST publish, revert, rollback, commits/:c/revert, adopt-orphans, duplicate (none reads the query), and RestServer POST /packages/publish (reads none); already closed: POST /meta/:type/:name/publish (META_ITEM_PUBLISH_PARAMS, #22877) · proposed card: close the query sets of the remaining package write doors, uninstall first · dedupe words: packages keepData misspelled query parameter ignored, uninstall drops tables, package write door closed query set"
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
Landed
2026-10-27T19:24Z ·
domain:serverseat 2 ·os-project-manager· sessionsession_0186vfjHHMsRXC9EUT9hTFMoPR #26853 merged at 2026-10-27T19:24:02Z by the merge queue as
96e7a4335conmain(Fixes #26713; ACCEPT6450984821;Clause-②: yes (narrowing)withSkills-docs: none). GitHub closed the card as completed.git ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-26853-*'git merge-base --is-ancestor 96e7a4335c origin/mainorigin/mainby contentpackages/runtime/src/domains/packages.tsrefuseDraftDoorQueryPOST /packages/:id/publish-draftsandPOST /packages/:id/discard-draftsnow declare an empty closed query set. Any query parameter,dryRunincluded, gets400 VALIDATION_ERRORwith the shared unknown-query sentence. The check runs after the capability and wall gates and before the protocol, so a refused call publishes or discards nothing. Before this change,?dryRun=trueanswered200and published every pending draft.DELETE /packages/:id?keepdata=truedrops the tables the caller asked to keep, and the other package write doors have no closed query set. That is packages:DELETE /packages/:id?keepdata=trueanswers 200 and drops the tables the caller asked to keep; the package write doors have no closed query set #26857,domain:server.mainbefore its ACCEPT.Generated by Claude Code