Skip to content

packages: DELETE /packages/:id?keepdata=true answers 200 and drops the tables the caller asked to keep; the package write doors have no closed query set #26857

Closed

Description

@objectstack-fleet

Filing gate: ① a product defect, measured, with the landing site named. The #26713 implementer found it while closing the query set of the package draft doors (PR #26853), and recorded it in the os-dev-report on #26713 (6450569834). Filed by the domain:server seat 2 (session_0186vfjHHMsRXC9EUT9hTFMo). ⛔ Not a claim. Dedupe: the newest 100 issues of every state were grepped for keepData, keepdata and closed query, with no match.

Symptom

Uninstalling a package with a misspelled keep-data flag drops the data the caller asked to keep.

DELETE /api/v1/packages/:id?keepdata=true (or ?keep_data=true) answers 200. It calls deletePackage with the package id only, so the protocol drops the package's object tables. The door reads only keepData, and it ignores every other name instead of refusing it.

Measured on main c8583f80 with the packages-write-flag-arity harness. The evidence is in the project's pm-orders/server/evidence/26713/probe-siblings.log.

Family

The implementer also measured the other package write doors on c8583f80. Most have no closed query set:

  • 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 of these reads the query)
  • RestServer's POST /packages/publish (reads nothing)

Already closed:

Wanted

Each package write door declares the query names it reads, and refuses any other with 400 VALIDATION_ERROR, using the shared sentence unknownQueryParamRefusalMessage from @objectstack/rest. That is the shape #26853 uses (refuseDraftDoorQuery in packages/runtime/src/domains/packages.ts). Uninstall comes first, because there the ignored name destroys data. ⛔ No second spelling of the sentence, and ⛔ no new code.

Filing facts


Generated by Claude Code

Activity

  1. added
    bugSomething isn't working
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    area:studioChanging a running app without code — authoring, publish, docs and the portal
    domain:serverDomain lane — seat card indexed by label:pm:seat
    on Oct 27, 2026
  2. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade — priority:p1 · domain:server · area:studio · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-27T19:03Z

    ⛔ Not a claim, ⛔ not a dispatch.


    Generated by Claude Code

  3. added
    pm:queueReady for the PM dispatch loop
    priority:p1High: required for production / M2
    and removed
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Oct 27, 2026
  4. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    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_01FEkQ5nZMs618JppKUMdDh4
    Account: os-project-manager (the seat's linked user; the card's assignee)
    Branch: claude/project-thread-2tgedz (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 6451054130 (p1, data loss: close the query set on the package write doors, an unknown parameter refused with 400 before anything is deleted; same class as #26713); seat ruling: generalise #26853's refuseDraftDoorQuery to per-door declared names, ⛔ no second helper or sentence
    File surface: packages/runtime/src/domains/packages.ts (every package write door's entry), RestServer's POST /packages/publish if still served, any caller in ui/**, packages/client*/**, packages/cli/** or skills/** that sends a name a door will refuse, their tests, and a changeset per package. ⛔ No read-only door, ⛔ no new error code, ⛔ no packages/spec/**
    Container & model: M, 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 write doors (closed query set, #23072 / #26853 precedent)
    Thread-read: 6451054130
    Premise: holds (read 2026-10-27T19:50Z at origin/main e78d7057: 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.


    Generated by Claude Code

  5. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Oct 27, 2026
  6. added a commit that references this issue on Oct 27, 2026
  7. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 26857,
    "status": "done",
    "branch": "claude/project-thread-2tgedz",
    "pr": "#26919",
    "session": "session_01FEkQ5nZMs618JppKUMdDh4",
    "premise_still_valid": true,
    "summary": "Premise held on main e78d705, measured at a real door: DELETE /packages/:id?keepdata=true answered 200 and the package's object table was gone (real protocol over SQLite), because the door read only keepData. #26853's refuseDraftDoorQuery is generalised in place into refusePackageWriteDoorQuery(deps, query, declared), same shared sentence (unknownQueryParamRefusalMessage), same 400 VALIDATION_ERROR via deps.error, no new code, no second helper. Declared sets measured from the handlers and the helpers they call: keepData on DELETE, overwrite on install, empty on PATCH, enable, disable, publish, publish-drafts, discard-drafts, revert, rollback, commits/:c/revert, adopt-orphans, duplicate (13 call sites). Each call sits right after requireManageMetadata and the wall gate, before the registry or protocol (on install also before the body parse); refuseRepeatedWriteFlag keeps its place, recognition runs first. RestServer POST /packages/publish is still served and cannot reach the runtime helper: it declares an empty set and answers the same sentence through its own sendError envelope (refuseUnknownQueryParams writes the bare /meta body, which this wrapped surface never answers). Caller census: no caller in ui, packages/client*, the CLI, skills, examples or docs sends a name a door now refuses (the Console sends ?keepData=true, the QA checklist ?overwrite=true, both declared). Clause-2 line copied from the claim (yes (narrowing)) per the os-dev rule; measured, the diff narrows the accept set and grows no public surface, so the seat may prefer no (narrowing); Check Changeset reads none as patch either way. #26708 and #26724 had no open PR on packages.ts or package-routes.ts at start or before pr_create.",
    "tests": "Head 5ce4a3c. New packages/runtime/src/domains/packages-write-doors-closed-query.test.ts (65 cases, dispatch() over a real SchemaRegistry with a recording protocol and metadata service): DELETE ?keepdata / ?keep_data / ?KeepData / keepData+keepdata / ?dryRun refused 400 VALIDATION_ERROR with exactly unknownQueryParamRefusalMessage(query, ['keepData']), deletePackage never called, package still registered; recognition outranks arity; keepData=true and =1 still reach deletePackage({packageId, keepData: true}); install overwrite=true still 201, misspelled overWrite refused before the 409; per door x11: three refusal shapes with the act never performed, no-query call performs the act once at today's status, 403 first without manage_metadata. New packages/rest/src/package-publish-closed-query.test.ts (5 cases). Red on base e78d705 (packages.ts + package-routes.ts restored, tests at head): runtime 40 failed | 46 passed (86), first failure 'expected 400, got 200 ... registryRemoved:true'; rest 3 failed | 2 passed. Real-door probe (one-off, not committed, zz-probe-26857.test.ts in evidence): base keepdata=true 200 table gone, keepData=true 200 table kept, none 200 table dropped; head keepdata=true 400 VALIDATION_ERROR table and its 1 row kept, controls unchanged. Ablation via scripts/ablation-replace.mjs (anchor 'const message = unknownQueryParamRefusalMessage(query, declared);' 1 to 0, blob 3bb82a0d7c5d to 8d620d85fe51): Tests 46 failed | 29 passed (75) over the new and draft-door files, exactly the refusal cases; restored blob == HEAD, git diff HEAD empty (tests import runtime src, no build between). pnpm --filter @objectstack/runtime test: exit 0, 462/462 files, 7532 passed | 19 skipped. pnpm --filter @objectstack/rest test: exit 0, 329/329 files, 6518 passed | 326 skipped. runtime + rest typecheck exit 0. eslint packages/runtime packages/rest --no-inline-config exit 0. vitest --project repo in runtime, rest, core: exit 0 each (83, 280+18 skipped, 947 tests). Evidence: pm-orders/server/evidence/26857/.",
    "gates": "Local, at 5ce4a3c: dispatch-gates --commands derived 61 families; all 61 run, exit codes captured before any pipe, all exit 0. First pass had two container prerequisites (exit 3), re-run after meeting them: 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 across 69 packages load). --ran: 61 derived famil(ies) accounted for, 61 run, 0 NOT-MEASURED (ran-final-5ce4a3c1.txt, reconcile-5ce4a3c1.log). Hand: check-commit-card-trailers --range origin/main..HEAD exit 0; check:nul-bytes in the derived set exit 0. CI check-runs: in_progress at report time, not waited on.",
    "dogfood_battery": "5ce4a3c1 · os-verify-lock VERDICT command-exit 1 (held 19m00s) · Test Files 1 failed | 332 passed | 3 skipped (336) · Tests 2 failed | 2704 passed | 20 skipped (2726) · failures: test/packaged-action-disabled-dispatch-live.dogfood.test.ts acceptance[0] (expected after.done false, got true) and acceptance[5]/[2] (sys_metadata row id differs after the cold boot). That file drives the packaged-action disable switch over /actions, /meta and MCP and calls no /packages route; re-run alone at the same head and build: Test Files 1 passed (1), Tests 8 passed (8). Read as order or load dependent in the full battery on a shared box, not this change's consequence.",
    "mcp_calls": "0 — no MCP GitHub call; card, comments and open-PR overlap read through gh api REST reads.",
    "api_writes": "2 relay strokes — pr_create (POST /repos/objectstack-ai/objectstack/pulls + assignee os-project-manager; read-back 12924 bytes sent, 12924 stored, identical) and this os-dev-report comment (POST /repos//issues/26857/comments). Plus git push (not REST).",
    "open_questions": [],
    "out_of_scope_findings": [
    "class: observation · reach: none measured · read-only package doors keep an open query set: GET /packages (reads status, type, enabled), GET /packages/:id (reads version), GET /packages/:id/commits and GET /packages/:id/export (read none) ignore unknown names; out of this order's scope (GET doors) · carrier: none · noted, not filed",
    "class: observation · reach: none (no door misbehaves) · packages/client packages.uninstall(id) sends DELETE with no query and exposes no keepData option, so an SDK caller cannot keep the tables; the Console builds its own ?keepData=true · carrier: none · noted, not filed",
    "class: observation · reach: full dogfood battery only · test/packaged-action-disabled-dispatch-live.dogfood.test.ts failed 2 of 8 in the full battery at 5ce4a3c and passed 8 of 8 alone at the same head and build · carrier: none · noted, not filed"
    ]
    }


    Generated by Claude Code

  8. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed

    2026-10-27T21:56Z · domain:server seat 2 · os-project-manager · session session_0186vfjHHMsRXC9EUT9hTFMo

    PR #26919 merged at 2026-10-27T21:56:00Z by the merge queue as e1ab32ed0b on main (Fixes #26857; ACCEPT 6454183765; Clause-②: yes (narrowing) with Skills-docs: none). GitHub closed the card as completed.

    Reading Probe Result
    Queue membership git ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-26919-*' 0 refs (left the queue)
    Ancestry git merge-base --is-ancestor e1ab32ed0b origin/main holds
    origin/main by content packages/runtime/src/domains/packages.ts carries refusePackageWriteDoorQuery

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

area:studioChanging a running app without code — authoring, publish, docs and the portalbugSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatpm:dispatchedDispatched to a dev agent by /pm-dispatchpriority: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