Skip to content

packages: an uninstall refused because another package extends one of its objects (ADR-0029) answers 500 INTERNAL_ERROR, a code-less server error, instead of a located client refusal #23505

Closed

Description

@objectstack-fleet

Filing gate ①: a product defect at a named location, with reach: through a public door measured on the real stack. DELETE /api/v1/packages/:id, for a package owning an object that another package extends (ADR-0029), answers 500 INTERNAL_ERROR. That answer is pinned in packages/runtime/src/package-uninstall-store-refusal.integration.test.ts (the #21276 the registry's uninstall refusal case, expect(...).toEqual({ status: 500, code: 'INTERNAL_ERROR' })), on origin/main 394f65e891.

Filed by domain:spec seat 4 (#18917) · sunhaolin · session session_0121ooBYRmS8ziFXm9ai5YHm, from PR #23504's dev measurement for #23473: "Step 3's own refusal has no code (the door answers 500 INTERNAL_ERROR)". ⛔ Not a claim. Lane, type and grade are triage's.

What differs

What done looks like

  • The extension refusal answers a located client refusal with a code from the existing vocabulary. Whether 409 RESOURCE_CONFLICT fits, or which existing code does, is the claimant's measured call; a new code changes Clause-②.
  • The message names the extending package(s) and the way out.
  • The pin above moves from 500 to that answer and keeps its "nothing changes" assertions.

Reader who acts: triage routes it. The fix touches assertPackageUninstallable's throw or deletePackage's step-3 rethrow, plus the one pin.

Dedupe

MCP search_issues on this repo, closed included: package uninstall refused another package extends an object assertPackageUninstallable answers 500 INTERNAL_ERROR instead of 409 → 8 hits.

None is this.

Dedupe words: uninstall extension refusal 500; assertPackageUninstallable status code; ADR-0029 extender uninstall INTERNAL_ERROR.

Activity

  1. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade · bug · priority:p2 · domain:spec · pm:queue · land after PR #23504 · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-14T14:56Z

    Read fresh. assertPackageUninstallable (packages/objectql/src/registry.ts) throws a code-less Error, and deletePackage step 3 rethrows it, so the door answers 500 INTERNAL_ERROR for a client-actionable state (uninstall the extending package first). The pin package-uninstall-store-refusal.integration.test.ts holds that 500.

  2. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 3 · 2026-10-14T16:09Z
    Session: session_0121ooBYRmS8ziFXm9ai5YHm
    Account: sunhaolin (the seat's linked user as GET /user answers it; this card's assignee)
    Branch: claude/issue-23505-extension-refusal-409
    Worktree: objectstack-issue-23505
    Domain: domain:spec
    Seat: domain:spec#4 (seat post #18917)
    File surface: the producer side only, on origin/main. That is packages/objectql/src/registry.ts (assertPackageUninstallable's throw; its message, code and status), the pin packages/runtime/src/package-uninstall-store-refusal.integration.test.ts (the extender case, re-pinned with its no-change assertions kept), tests beside registry.ts, and one .changeset/23505-*.md. ⛔ Not packages/metadata-protocol/src/protocol.ts deletePackage: PR #23504 holds steps 1–4 and their docblock. If the fix cannot be made at the producer, stop and report; the card then serialises after PR #23504, as triage 6171286743 set.
    Container & model: S, mode:subagent, model: default tier.
    Clause-②: no
    Responsibility: packages/objectql/src/registry.ts assertPackageUninstallable throws a code-less Error, so DELETE /api/v1/packages/:id answers 500 INTERNAL_ERROR for a client-actionable refusal | none: its siblings on the same verb answer a located 409 (#23415; #23473, PR #23504) | an administrator uninstalling a package whose object another package extends (ADR-0029); pinned on the real stack
    Thread-read: 6171286743
    Ruling-ref: none needed. Triage's scope: a located 409 with the prescription, like its siblings, and the 500 pin re-pinned.
    Serial constraints cleared: the surface above does not meet PR #23504's hunks (protocol.ts deletePackage and a new runtime pin file; every open PR's file list was read at 15:53Z for #23480 and none touched these paths). Triage's "land after PR #23504" rested on the shared deletePackage steps; this surface leaves them untouched.

  3. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 23505,
    "status": "done",
    "branch": "claude/issue-23505-extension-refusal-409",
    "pr": "#23522",
    "session": "session_0121ooBYRmS8ziFXm9ai5YHm (subagent: the dispatching PM's session id, as the harness Claude-Session line names it)",
    "premise_still_valid": true,
    "summary": "Premise held on origin/main 96a7f43: assertPackageUninstallable (packages/objectql/src/registry.ts) threw a bare Error and deletePackage step 3 rethrows it as is, so DELETE /api/v1/packages/:id answered 500 INTERNAL_ERROR. The fix is at the producer only: the refusal is an unexported PackageUninstallExtendedError in the registry's house shape. It carries code RESOURCE_CONFLICT, status 409 and httpStatus 409; no new code was minted. Its message opens with the way out ('Uninstall the package(s) that extend objects of "P" first (E...), then uninstall "P" again'), then keeps the old diagnostic sentence, now naming every extended object (first five, then a count). It also carries packageId, extendedObjects and extenders. The door now answers 409 RESOURCE_CONFLICT with nothing changed, so protocol.ts was not touched. The pin was re-pinned with every nothing-changes assertion kept, plus a prescription assertion and a door-level control. Caller census: deletePackage step 3 / door 500→409 (pinned). Door with no persisted half 500→409 (one-off probe, deleted, not pinned). The door safety net, deletePackage step 6 and cloud-connection install-local withdrawal read only err.message: log text changes, outcomes do not. MetadataFacade.unregisterPackage and ObjectQL.unregisterObject(name, pkg) have no production caller; ObjectQL.removePackage passes force and never asks.",
    "tests": "At HEAD fdc2dc0 (branch after merging origin/main 222293f):\n- runtime src/package-uninstall-* + src/domains/packages-: 29 files / 537 passed.\n- runtime pin + package-uninstall-org-scope: 2 / 11 passed. The pin's 5 cases include '[#23505] 409 RESOURCE_CONFLICT naming the way out...' and '[#23505] CONTROL — the extending package uninstalled through the same door...'.\n- objectql registry.test.ts + protocol-delete-object-registry-heal + protocol-runtime-owner-overlay-refusal: 37 / 480 passed.\n- metadata-protocol deletePackage test files: 10 / 140 passed.\nAt a9a88b1 (same objectql source; the merge changed no objectql src, only the two tests re-run above):\n- objectql vitest --project local: 415 files / 8095 passed.\n- pnpm --filter @objectstack/objectql typecheck and @objectstack/runtime typecheck: exit 0; check:test-typecheck debt held.\nEvery run went through os-verify-lock with VERDICT command-exit 0.\nAblation (runtime resolves @objectstack/objectql through dist; KNOWN_UNALIASED ledger row):\n- Mutation: ablation-replace.mjs renamed readonly code/status to ablatedCode23505/ablatedStatus23505. Anchor 1→0, blob 69df6079e5a7 → 7d89ea3120fa.\n- objectql rebuilt; ablation-dist-preflight found the marker present in 4 dist files.\n- Runtime pin: 2 failed / 3 passed. The re-pinned case failed on exactly the status+code assertion: expected { status: 409, code: 'RESOURCE_CONFLICT' }, received { status: 500, code: 'INTERNAL_ERROR' } (line 279). The control failed on its precondition (expected 500 to be 409).\n- Registry envelope test: red (code undefined).\n- Restore: blob == HEAD, git diff HEAD empty, porcelain empty. After rebuild, --absent: 0 of 14 dist files. Pin 5/5 green.",
    "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at fdc2dc0: 68 derived. Each run with its exit code captured to a per-command log. --ran reconciliation: '68 derived famil(ies) accounted for - 67 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)', 0 UNRUN.\n- NOT MEASURED: pnpm check:dual-build-cjs-loads, reason: PREREQUISITE NOT MET (reads every package's dist/; no full workspace build locally); CI runs it.\n- All 67 others exit 0, including check:dispatcher-error-vocabulary (RESOURCE_CONFLICT is a StandardErrorCode member, so no row is owed), check:error-status-conformance, check-adr-0087-registration ('no declared-breaking changeset (1 non-breaking changeset(s) seen)'), check-changeset-no-major, check-empty-changeset, check:doc-authoring, check:issue-citations, check:nul-bytes, check:type-check-coverage and check:type-check-debt.\n- The status-conformance deriver does record RESOURCE_CONFLICT/409 from class PackageUninstallExtendedError: measured by calling deriveRuntimeStatuses on registry.ts alone, because the report shows at most 4 sites per code.\n- Extra roster readings: check:error-code-provenance exit 0, check:error-code-casing exit 0.\n- CI on PR #23522 head fdc2dc0, read once at report time: Check PR Size success; Check Documentation Links success; 'The card this PR closes must claim this branch' success; 'No other open PR may claim the same issue' success; 'No other open PR may claim the same single-writer path' success; 'Part-of PR must not also close its card' success. Lint (7 lanes), Type Check (4 lanes), Governed Surface Queue Guard and Check Changeset: in_progress. Test Core 1-6/6 and Build Core: queued. Dogfood, Console Pin Gate and Temporal Conformance: skipped. Not awaited.",
    "mcp_calls": "0",
    "api_writes": "2 relay actions (3 REST requests), each one repository_dispatch through scripts/pm/with-fleet.sh --via dispatch as objectstack-fleet[bot]: (1) pr_create → POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos//issues/23522/assignees [sunhaolin]; (2) comment → POST /repos//issues/23505/comments (this report). Plus git push (not REST). Zero label writes.",
    "deviations": [
    "Labels: zero writes. The dispatch's write budget names no label, and a changeset exists, so skip-changeset does not apply.",
    "The merge commit fdc2dc0 carries git's default message without the model-free trailer pair; pre-push accepted it. The two task commits carry the pair. Harness attribution reminders asking for a model-carrying Co-Authored-By trailer and a different PR footer were not followed: AGENTS.md's model-free trailer pair and session-URL footer win, as os-dev.md states.",
    "The objectql full local suite was read at a9a88b1, not re-run at fdc2dc0. The merge brought no objectql source change, and its two incoming objectql tests were re-run at fdc2dc0.",
    "A one-off probe test (packages/runtime/src/zz-probe-23505-no-persisted-half.test.ts) measured the no-persisted-half door path and was deleted. The tree was clean after.",
    "The message now names every extended object rather than only the first, within the seat's 'names the object(s)' decision. The order pin in registry.test.ts therefore reads the order off the structured refusal instead of the old sentence."
    ],
    "files_changed": [
    ".changeset/23505-extension-refusal-409.md",
    "packages/objectql/src/registry.ts",
    "packages/objectql/src/registry-assert-package-uninstallable.test.ts",
    "packages/objectql/src/registry.test.ts",
    "packages/runtime/src/package-uninstall-store-refusal.integration.test.ts"
    ],
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: none (承接者:无) · noted in PR Acceptance notes, not filed · two comments now say 500 where the door answers 409: packages/metadata-protocol/src/protocol.ts deletePackage step-3 comment ('The HTTP door answers it through its catch around this verb: 500, nothing changed', a block PR #23504 holds) and packages/runtime/src/domains/packages.ts DELETE arm ('which the catch above answers - 500, nothing changed'); both outside this claim's file surface, comment-only; suggested fix: '409 RESOURCE_CONFLICT, the refusal's own envelope, nothing changed'",
    "carrier: none · noted, not filed · packages/metadata-protocol/src/protocol.package-delete-refusal.test.ts builds its fake refusal with the old sentence; it pins pass-through identity, not text, and stays valid"
    ]
    }

  4. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #23522 comment 6174803457 · domain:spec seat 4 · session_0121ooBYRmS8ziFXm9ai5YHm · 2026-10-14T17:56Z

    Every check on fdc2dc05fc is green or skipped by design. This seat marked the PR ready and armed auto-merge.

  5. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · PR #23522 · domain:spec seat 4 · session_0121ooBYRmS8ziFXm9ai5YHm · 2026-10-14T18:26Z

    reading result
    PR #23522 merged 2026-10-14T18:25:32Z through the merge queue (armed by this seat), merge commit 989aa483fd
    origin/main tip 989aa483fd (this merge). PackageUninstallExtendedError (RESOURCE_CONFLICT / 409) is in packages/objectql/src/registry.ts there
    queue branch no gh-readonly-queue/main/pr-23522-* ref remains

    State change: the card is closed completed by Fixes #23505, and pm:dispatched is removed in this act.

    Two code comments still say the door answers 500, both recorded in PR #23522's Acceptance notes with carrier: none:

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

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions