Skip to content

finding(objectql): NamespaceConflictError (the ADR-0048 install-time namespace gate's refusal) carries no ADR-0112 envelope — no code, no status — and POST /packages would answer it as 500 by reading #14474

Description

@os-musk

Found while implementing #14367 (registerObject's cross-package ownership refusal gaining an ADR-0112 envelope). Out of that card's scope — its file surface is region-bounded to the registerObject refusal and one new class beside ArtifactObjectNameConflictError, and this class sits inside the region the in-flight #14309 dispatch is editing — so filed rather than fixed.

The site

NamespaceConflictError in packages/objectql/src/registry.ts (export class NamespaceConflictError extends Error, ~:1137 on origin/main @ a98b61b3e) carries namespace / existingPackageId / incomingPackageId as fields but no code and no status. It is the ADR-0048 install-time namespace gate's refusal, thrown by SchemaRegistry.installPackage when a package's namespace is already owned by an installed package that is not a co-owner of it (ADR-0130 D1).

Why it matters — reading, not runtime measurement

Unlike the #14367 refusal, installPackage IS on an HTTP path: POST /packages in packages/runtime/src/domains/packages.ts calls protocol.installPackage (or registry.installPackage as fallback), and the domain's terminal catch answers deps.errorFromThrown(e, 500). resolveThrownHttpError (packages/types/src/thrown-http-error.ts) reads .status / .statusCode off the throw and falls to the caller's fallback when neither is present — so, by reading, a namespace conflict on POST /packages answers 500 INTERNAL_ERROR with the refusal's prose as the message, where a refusal the platform DECIDES should answer 4xx with a registered code. Not measured against a running door in this filing; the reading is from the two call sites and the resolver, and a dev on this card should measure it first.

What the sibling refusals already do

DUPLICATE_ARTIFACT_PACKAGE / INVALID_ARTIFACT_PACKAGE_ENTRY (packages/objectql/src/artifact-packages.ts), DUPLICATE_ARTIFACT_OBJECT_NAME (ArtifactObjectNameConflictError, same file as this class) and, once #14367 lands, OBJECT_OWNERSHIP_CONFLICT all carry code + status: 422 and a classification row in packages/runtime/src/dispatcher-error-vocabulary.ts. This class is the one remaining install-time refusal in that family without an envelope.

Existing tests on this path

registry-namespace-install-gate.test.ts asserts toThrowError(NamespaceConflictError) / toBeInstanceOf(NamespaceConflictError) and registry-artifact-co-ownership.test.ts asserts toBeInstanceOf(NamespaceConflictError) — instance-typed, so not the bare-toThrow() false-green #14367 removes, but none can assert an envelope because there is none.

Suggested shape (not prescriptive)

Add code (SCREAMING_SNAKE, e.g. NAMESPACE_CONFLICT) + status: 422 to the class, message unchanged; measure the POST /packages door's answer before and after (this refusal, unlike #14367's, may need a pending-registration verdict and a ledger entry, since errorFromThrown would then put its status on the wire); add the vocabulary row with the measured verdict.

Blocked-by: none (the pattern exists; #14367 only shares the file).

Activity

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions