Skip to content

refactor(metadata-protocol,rest,runtime): one declared shape for the protocol.deletePackage seam - #10060

Merged
os-elon merged 7 commits into
mainfrom
claude/issue-9960-deletepackage-shared-type
Aug 20, 2026
Merged

refactor(metadata-protocol,rest,runtime): one declared shape for the protocol.deletePackage seam#10060
os-elon merged 7 commits into
mainfrom
claude/issue-9960-deletepackage-shared-type

Conversation

@os-elon

@os-elonos-elon commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

Fixes#9960 — route B as adjudicated in the triage comment 5344247751: converge the three hand-rolled deletePackage shapes on one exported request/response type from @objectstack/metadata-protocol (the producer), imported by both consumers, and delete the as any seam. No packages/spec declaration, and the rollback clause was not reached.

The three statements that disagreed

sitewhat it said about the same verb
packages/metadata-protocol/src/protocol.ts (producer)an inline structural type on the method: packageId, organizationId?, allTenants?, actor?, keepData?
packages/rest/src/package-routes.ts (direct-mount option){ packageId; actor?; allTenants? } — named neitherorganizationIdnorkeepData, and its response omitted deleted
packages/runtime/src/domains/packages.ts (dispatcher twin)nothing — it reached the verb through (protocol as any) and routinely sent exactly the two keys the REST option's type could not express

All three line numbers in the card were re-verified against origin/main before any edit (:15379, :256, :895 — all three still accurate at 09b880b0c).

organizationId is what makes this load-bearing rather than cosmetic: the protocol refuses a call naming neither it nor allTenants (TENANT_SCOPE_REQUIRED, 400), so it is precisely the key whose presence decides an uninstall's blast radius — and it was the key one of the two doors had no word for.

The measurement the grading asked for first

Question: does the runtime's typeof (protocol as any).deletePackage === 'function' guard exist because some protocol implementations genuinely lack the verb?

Answer: optional-per-protocol. The guard is load-bearing and stays; the shared type expresses the optionality at both seams. Four anchored probes, not one grep:

  1. Classes implementing any *Protocol interface, multiline so a two-line implements clause cannot hide: grep -rn --include=*.ts -Pzo '(?s)class\s+\w+\s+implements[^{]{0,200}?Protocol[^{]{0,200}?\{' packages examples apps → exactly one hit, ObjectStackProtocolImplementation implements DataProtocol, MetadataProtocol, PackageProtocol. It carries the verb.
  2. But the seam is not typed by that class.packages/runtime/src/domains/packages.ts resolves the service by string name, and protocol is a deliberately unmapped slot in ServiceSlotContracts (packages/spec/src/contracts/core-service-contracts.ts): "protocol and mcp have no written contract, so they stay unmapped and visible rather than being given a shape nothing checks." The string overload of resolveService returns any — which is why a wrong key was unsayable-but-sendable here.
  3. The spec's own PackageProtocol does not declare deletePackage at all (listPackages / getPackage / installPackage / uninstallPackage / enablePackage / disablePackage, every member optional). A host implementing the declared contract conformantly therefore has nodeletePackage.
  4. Registrants without the verb are real in-tree.registerService('protocol', …) (multiline-tolerant probe) hits packages/metadata-protocol/src/plugin.ts:251 plus five service-automation test harnesses; a per-file count of deletePackage in those five returns 0, 0, 0, 0, 0.

Anti-vacuity: probe 1 returns a non-zero hit list, and probe 4's positive control is the same probe's own producer hit (plugin.ts:251) — both pre-exist this diff and survive it. No probe used a symbol this diff introduces.

What changed

  • packages/metadata-protocol/src/protocol.tsDeletePackageRequest / DeletePackageResponse declared once, next to UninstallCleanupOutcome (which the response already anchors); the method's signature is now deletePackage(request: DeletePackageRequest): Promise(DeletePackageResponse). Same members, no reordering, no widening.
  • packages/metadata-protocol/src/index.ts — both types exported. This is the only user-visible half of the change: two additive type exports.
  • packages/rest/src/package-routes.ts — the option imports them (type-only) and stays deletePackage?, plus three compile-time pins in the same idiom [finding] package-routes.ts re-declares getMetaItems/deletePackage as a narrower local structural type instead of reading the spec's #9846 established one member above: exact request equality, exact response equality, and "the member stays optional". Exact equality rather than assignability, because the shape removed here is assignable to the producer's in one direction — an assignability check would have passed on the very divergence that made organizationId unsayable.
  • packages/runtime/src/domains/packages.ts — the resolved service is narrowed to the producer's declared verb, the as any casts are gone (call site plus three response reads), and the capability probe stays.
  • packages/rest/tsconfig.json — the paths rule the type import requires. See "Round 2" below.
  • packages/rest/src/package-routes-query-multiplicity.test.ts — the bounded in-place fix, declared here rather than left silent: that file's protocol double returned an uninstall response without deleted, which compiled only while the option's own type omitted it too. With the option now carrying DeletePackageResponse, the double stops type-checking and @objectstack/rest's TEST_DEBT entry drifted 155 → 156. Repaired at the author's end (deleted: []) and re-measured back to 155. The ratchet was not raised — that is maintainer-only, and this card is not authorised for it. The cases in that file count protocol calls, not deleted rows.

Types only. Accept set unchanged, no public route or behaviour change, and the emitted JavaScript of both consumers is identical (a removed cast and a narrowed const annotation both erase).

Reverse verification — two ablations, both legs run

Producer dist first, since both consumers type-check through it, never through its sources: packages/metadata-protocol/dist/index.d.ts carries interface DeletePackageRequest (:596), interface DeletePackageResponse (:619) and both names in the terminal export list — the mutation reached the resolution path. Both ablations below then mutate a file inside the package under test, which tsc --noEmit compiles directly.

A. The runtime seam now rejects a key it used to swallow. Pasted organisationId: 'ABLATION' (the misspelling the any used to accept) into the request literal:

src/domains/packages.ts(923,25): error TS2561: Object literal may only specify known
properties, but 'organisationId' does not exist in type 'DeletePackageRequest'.
Did you mean to write 'organizationId'?

Restore leg (git checkout HEAD -- …, from the committed state): tsc --noEmit exit 0, working tree byte-clean.

B. The pins fail on the shape this card removed. Put the pre-#9960 hand-rolled option back with the pins untouched:

src/package-routes.ts(383,3): error TS2344: Type 'false' does not satisfy the constraint 'true'.
src/package-routes.ts(388,3): error TS2344: Type 'false' does not satisfy the constraint 'true'.

Two of three pins, which is the correct direction: the request and response pins fire, the optionality pin stays green because the mutated member was still optional. Restore leg: exit 0.

Round 2 — the check:type-source-resolution failure, and why the first round's own reassurance was wrong

CI went red on Lint & Repo Gates:

✗ @objectstack/rest: NEW dist-resolved type import(s) since this entry was measured:
@objectstack/metadata-protocol.

Correct, and caused by this card. Round 1 reported that type correctness rested "on both consumers resolving through its rebuilt dist/*.d.ts" and treated that as sufficient. It is not: a verdict about a build artifact is not a verdict about the producer's source in the checkout, which is precisely the failure whose symptom is a typecheck that passes. The separate invariant round 1 did establish — that the type never reaches rest's publishedd.ts — remains true and re-verified below, but it is a different gate.

Fixed where the gate prescribes, in packages/rest/tsconfig.json:

  • ONE paths rule, bare key, targeting ../metadata-protocol/src/index.ts. No /* sibling: that package's exports map has only ".", so there is no subpath to redirect, and a paths target that is not on disk makes tsc fall back to node resolution — i.e. back to dist — silently. The gate judges rules individually against the specifiers actually imported and does not ask for a block covering imports that do not exist.
  • rootDir widened from ./src to .., as a consequence rather than a preference. The producer's source is now in the program and rootDir is enforced even under --noEmit: measured at 20 x TS6059, and deleting the key does not help either — tsc infers one from this package's own inputs and reports the identical 20. .. is the directory that genuinely contains every file in the program. Nothing that ships reads it: the package builds with tsup, and typecheck passes --noEmit.

KNOWN_DIST_RESOLVED_TYPE_IMPORTS was not widened — it is shrink-only and maintainer territory. @objectstack/rest's entry keeps its eight other dist-resolved deps unchanged, and the gate audits that set for equality in both directions.

Red-to-green, reproduced in that order before anything was pushed:

node scripts/check-type-source-resolution.mjs -> exit 1, the exact CI message
... add the paths rule + widen rootDir ...
node scripts/check-type-source-resolution.mjs -> exit 0
"76 packages with a tsconfig.json scanned; 51 registered as still resolving
a workspace dep's types through dist/"

And the re-run the gate change makes necessary:pnpm --filter @objectstack/rest typecheck is exit 0 with zero errors now that it reads the producer's source — nothing was hiding behind the stale artifact, and no ledger was touched to get there. One measurement worth recording: on the first attempt that same command reported 135 x TS2307 plus a 46 x TS7006 cascade, which was not a finding — it was an unbuilt closure in a freshly re-created worktree. With the closure built, 20 x TS6059 remained and nothing else, and those were the rootDir diagnostic above.

@objectstack/rest's TEST_DEBT re-measure also passes with the paths block active (the re-measure project inherits it): still exactly 155, "surplus: none".

Checks — all run locally at ad2a23d3e, the pushed head

checkresult
check:type-source-resolutionexit 0 (reproduced red first, then green)
check:test-source-aliasexit 0 (the sibling runtime axis, untouched by this change)
pnpm --filter @objectstack/rest typecheckexit 0, zero errors — now resolving the producer's source
pnpm --filter @objectstack/runtime typecheckexit 0 (tsc --noEmit)
pnpm --filter @objectstack/rest build (tsup)exit 0; published dist/index.d.ts still has zeroDeletePackageRequest references and no metadata-protocol import line — the only mention is a pre-existing prose comment
pnpm --filter @objectstack/metadata-protocol test123 files / 1702 passed, 2 files / 10 skipped
pnpm --filter @objectstack/rest test131 files / 2134 passed (re-run after the tsconfig change)
pnpm --filter @objectstack/runtime test177 files / 2649 passed
check:type-check-coverageOK — 64/77 covered, 13 DEBT, 1 exempt
check:type-check-debt (re-measure, closure built)OK — 33 entries, "none above its recorded number", surplus: none
gate set derived by node scripts/pm/dispatch-gates.mjs (no paths)check:cross-package-test-inputs, check:dispatcher-error-vocabulary, check:durability-log-level, check:filter-alias-parity, check:route-envelope, check:slot-lookup, check:changeset-gate-self-tests, check:objectui-changeset, check-empty-changeset, check-changeset-no-major, check-adr-0087-registration, docs-audit/check-affected-docs — all exit 0
also run: check:nul-bytes, check:adr-anchors, check:query-options-erasure, check:published-files, eslint --no-inline-config on the five changed source filesall exit 0

@objectstack/metadata-protocol carries no typecheck script (it is a DEBT-ledger package); its type correctness here is now carried by @objectstack/rest's typecheck reading its source, plus its own tsup DTS build.

Concurrency

origin/main was merged at fe94d2e42 (7 commits, no conflicts, no deferred regeneration recorded). It has since moved a further 18 commits, none of which touch packages/rest, packages/runtime, packages/metadata-protocol, or either gate script — left unmerged deliberately, since the merge queue rebuilds the PR onto current main anyway. The in-flight check before the first edit found no claim on any of the three declared files; the region was declared by symbol on the issue because #9612 shares protocol.ts — this diff does not touch the publish-gate seam.

Draft on purpose: the PM lands it through the merge queue after review.


Generated by Claude Code

…`deletePackage` seam
`protocol.deletePackage` had three independent statements of its own contract
and none of them agreed:
1. the producer's own inline structural type on the method,
2. `PackageRoutesOptions.protocol.deletePackage` in `@objectstack/rest`, which
named neither `organizationId` nor `keepData` and omitted `deleted` from
the response,
3. the dispatcher twin in `@objectstack/runtime`, which typed the seam not at
all and reached it through `(protocol as any)` — while routinely sending
exactly the two keys (2) could not express.
`organizationId` is the key that decides an uninstall's blast radius (the
protocol refuses a call naming neither it nor `allTenants` —
`TENANT_SCOPE_REQUIRED`, 400), so the member the REST seam had no word for is
the one that matters most.
`DeletePackageRequest` / `DeletePackageResponse` are now declared once at the
producer and exported from `@objectstack/metadata-protocol`; both consumers
import them and the `as any` is gone. Types only — identical members, identical
call, no accept-set or behaviour change. No `packages/spec` declaration: minting
protocol surface for a verb with zero external consumers is a spec-seat decision
nobody has asked for.
The member stays OPTIONAL and the runtime's `typeof … === 'function'` capability
probe stays: the `protocol` service slot is deliberately uncontracted, the
spec's `PackageProtocol` does not declare this verb, and registrants that carry
no `deletePackage` are real in-tree.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019yDEhPBC3tcGkW9bkce1HM
…9960)
Bounded in-place fix, same defect class as the card: `package-routes-query-multiplicity.test.ts`
built a protocol double whose uninstall response omitted `deleted`. That
compiled only while the option's type omitted it too — with the option now
carrying the producer's `DeletePackageResponse`, the double stops type-checking,
and `@objectstack/rest`'s TEST_DEBT ledger entry drifted 155 → 156.
Fixed at the author's end (`deleted: []`), never by raising the ratchet: the
ledger is shrink-only and raising it is maintainer-only. Re-measured back to
155, and `check:type-check-debt` reports "none above its recorded number".
The cases in this file count protocol CALLS, not deleted rows, so an empty
`deleted` changes nothing they assert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019yDEhPBC3tcGkW9bkce1HM
@github-actions

github-actionsBot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/metadata-protocol, @objectstack/rest, @objectstack/runtime, touching 28 documentable anchor(s).

13 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/api/client-sdk.mdx(via getAudit (sdk), getBookTree (sdk), meta.getAudit (sdk), meta.getBookTree (sdk), meta.publishItem (sdk), meta.rollbackItem (sdk), publishItem (sdk), rollbackItem (sdk))
  • content/docs/automation/hooks.mdx(via organizationId (symbol))
  • content/docs/data-modeling/seed-data.mdx(via organizationId (symbol))
  • content/docs/kernel/contracts/metadata-service.mdx(via packageId (symbol), /:type/:name/publish (route), /:type/:name/rollback (route))
  • content/docs/kernel/events.mdx(via organizationId (symbol))
  • content/docs/kernel/runtime-services/audit-service.mdx(via organizationId (symbol))
  • content/docs/kernel/runtime-services/sharing-service.mdx(via organizationId (symbol))
  • content/docs/permissions/authorization.mdx(via packageId (symbol))
  • content/docs/permissions/permission-sets.mdx(via packageId (symbol))
  • content/docs/protocol/kernel/config-resolution.mdx(via organizationId (symbol))
  • content/docs/protocol/kernel/plugin-spec.mdx(via packageId (symbol))
  • content/docs/ui/doc-pages.mdx(via packageId (symbol))
  • content/docs/ui/setup-app.mdx(via packageId (symbol))

4 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/index.mdx(via organizationId (symbol))
  • content/docs/releases/v14.mdx(via /book/:name/tree (route))
  • content/docs/releases/v16.mdx(via organizationId (symbol))
  • content/docs/releases/v17.mdx(via allTenants (symbol), deletePackage (symbol), deletePackage (literal), /:type/:name/publish (route))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 2 changed file(s) yielded no anchor (packages/metadata-protocol/src/index.ts, packages/rest/tsconfig.json) — pages documenting those are invisible to this run
  • 6 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 45 of 221 client-bound route-ledger rows — the other 176 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run: node scripts/docs-audit/affected-docs.mjs --bridge-coverage

Coarse fallback — 32 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json eb2bebe532aad5fdc99007923520df6d9d1dd9c9packageMentionDocs.

Which tree this was computed on

This run read content/docs from 8c61a388ed0b9056364d6a3e479972b61e197218 — the merge of head 379ae9788ccf773e91c0eb8a4613f4cde5374eb0 into base eb2bebe532aad5fdc99007923520df6d9d1dd9c9, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 8c61a388ed0b9056364d6a3e479972b61e197218 && git checkout 8c61a388ed0b9056364d6a3e479972b61e197218
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin eb2bebe532aad5fdc99007923520df6d9d1dd9c9 379ae9788ccf773e91c0eb8a4613f4cde5374eb0 && git checkout -B drift-repro eb2bebe532aad5fdc99007923520df6d9d1dd9c9 && git merge --no-ff 379ae9788ccf773e91c0eb8a4613f4cde5374eb0
node scripts/docs-audit/affected-docs.mjs --json eb2bebe532aad5fdc99007923520df6d9d1dd9c9

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs eb2bebe532aad5fdc99007923520df6d9d1dd9c9 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

…o dist (#9960)
`check:type-source-resolution` went red on the branch:
✗ @objectstack/rest: NEW dist-resolved type import(s) since this entry was
measured: @objectstack/metadata-protocol.
Correct, and caused by this card: `src/package-routes.ts` type-imports the
declared `deletePackage` shapes, and with no `paths` rule tsc resolved that
specifier through the dependency's `exports` map — `dist/index.d.ts`, a build
artifact. This package's `typecheck` was therefore a verdict about the last
`pnpm build` rather than about the producer's source in the checkout, which is
the failure mode whose symptom is a typecheck that PASSES.
Fixed where the gate prescribes — the package's own `tsconfig.json`:
* ONE `paths` rule, bare key, targeting `../metadata-protocol/src/index.ts`.
No `/*` sibling: that package's `exports` map has only `"."`, so there is no
subpath to redirect, and a rule pointing at files that are not on disk makes
tsc fall back to node resolution silently.
* `rootDir` widened from `./src` to `..`, as a consequence rather than a
preference: the producer's source is now in the program, `rootDir` is
enforced even under `--noEmit` (measured: 20 x TS6059, and deleting the key
makes tsc infer one and report the identical 20), and `..` is the directory
that genuinely contains every file in the program. Nothing that ships reads
it — the package builds with tsup.
⛔ The `KNOWN_DIST_RESOLVED_TYPE_IMPORTS` registry was NOT widened. It is
shrink-only and maintainer territory; `@objectstack/rest`'s entry keeps its
eight other dist-resolved deps unchanged, and the gate audits that set for
equality in both directions.
Verified red-to-green, in that order: `node scripts/check-type-source-resolution.mjs`
exit 1 with the exact CI message, then exit 0 ("76 packages scanned; 51
registered"). `pnpm --filter @objectstack/rest typecheck` is exit 0 with ZERO
errors now that it reads the producer's source — no error was hiding behind the
stale artifact, and no ledger was touched to get there.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019yDEhPBC3tcGkW9bkce1HM
@os-elon
os-elon marked this pull request as ready for review August 20, 2026 07:27
@os-elon
os-elon added this pull request to the merge queueAug 20, 2026
@os-elonClaude

Copy link
Copy Markdown
CollaboratorAuthor

PM review — ACCEPT, enqueued

domain:engine seat, PM session session_019yDEhPBC3tcGkW9bkce1HM. Head 379ae9788, on the recovered base.

All 25 checks green, each read on its own conclusion rather than the aggregate — Test Core 1/3 · 2/3 · 3/3 + aggregate, Lint & Repo Gates, TypeScript Type Check, Build Core, Temporal Conformance, Dogfood ×3 + aggregate, Dogfood Verify CLI, Console Pin Freshness, Check Changeset, and all three claim guards.

Neither of the two reds this PR carried was its own

Recording this because the diff was correct throughout and two rounds were spent proving it:

redcauseresolution
Test Core (3/3)the repo-wide merge-queue problem — #10120 (e61ee6832, "pay the plugin-security transform at module load, out of every clocked window")cleared on the fixed base
Lint & Repo Gateseslint blowing its parse stack on migrations/registry.ts#10121, a file this diff does not touchcleared by #10124 (eb2bebe53, 07:00Z)

⭐ The Test Core red was diagnosed across PRs, not within this one: three unrelated PRs each had a different shard red, and this PR — deliberately left 18 commits behind — was red anyway, which is what falsified "something landed in main". Filed and closed as #10125. Alone, this PR's red was indistinguishable from a flake of its own.

The one red that WAS yours, and how it was fixed

check:type-source-resolution@objectstack/rest acquiring a dist-resolved type import of @objectstack/metadata-protocol. Fixed at the author's end with one paths rule pointing at the producer's source; ⛔ the registry was not widened (it is shrink-only, and widening it would have been a ratchet raise). red→green reproduced in order: exit 1 with the exact CI text first, exit 0 after.

⭐ The control on that fix is the right one: the gate reports 51 registered packages before and after — this package neither joined the registry nor left it. A count that did not move is what proves the fix removed the dep from the measured set rather than adding it to the recorded one.

⭐ And the reasoning in the tsconfig comment earns its length. One rule and not two, because that package's exports map has only "." — and a paths target that does not exist makes tsc fall back to node resolution, i.e. back to dist, silently. So the instinct "add a /* sibling to be safe" is inverted here: the over-broad fix would defeat itself invisibly. The @objectstack/metadata-protocol* spelling (star not preceded by a slash) is called out with a pointer to the repo's other paths block where that trap was measured.

rootDir widening was verified as a forced consequence, not a preference: 20 × TS6059 with the producer's source in the program, and deleting the key gives the identical 20. Independently checked here that nothing in the repo reads rest's tsc output layout — rest/dist/src returns 0 hits; the only packages/rest/dist references are turbo cache manifests for the tsup build, whose outdir comes from the shared config.

Verified independently

Head matches. Per-file divergence 0 on every file (positive control: commits on other paths ≠ 0). Merge-tree conflicts 0. ⛔ Debt ledger not in the diff; @objectstack/rest's TEST_DEBT still exactly 155 — the one drift this round (155 → 156, from a protocol double whose response omitted deleted) was repaired at the author's end with deleted: [], ⛔ not absorbed by raising the ledger.

The guard enumeration, which answered a better question than was asked

The order asked whether any protocol implementation lacks deletePackage. The answer given is why the seam is untyped at all: protocol is a deliberately unmapped slot in ServiceSlotContracts — the spec's own comment says such slots "stay unmapped and visible rather than being given a shape nothing checks" — so the call lands on the name: string overload returning any.

the wrong key was unsayable-but-sendable, and the as any was decoration on an any that was already there.

Plus: the spec's PackageProtocol declares six verbs and not this one, so a host implementing the declared contract conformantly carries no such verb; five real in-tree registrants lack it; and a host can point REST at a different slot entirely. ⇒ optional-per-protocol, guard kept, optionality expressed in the shared type, and a pin that fails to compile if a later edit makes the member required.

PROBE 1's note is worth keeping: the implements clause wraps across two lines here, and a single-line grep returns zero — a false "no implementations anywhere". Every probe in the enumeration is controlled, and none references a symbol this diff introduces.

Ablation (A) — pasting organisationId and getting TS2561 … Did you mean to write 'organizationId'? — demonstrates the exact bug class the change prevents. (B) predicted which two of three pins fire and why the third stays green.

Enqueued.


Generated by Claude Code

Merged via the queue into main with commit c766ec3Aug 20, 2026
26 checks passed
@os-elon
os-elon deleted the claude/issue-9960-deletepackage-shared-type branch August 20, 2026 07:45
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] protocol.deletePackage has no declared spec shape — three hand-rolled types that disagree, and the runtime twin reaches it through as any

2 participants

@os-elon@claude