Skip to content

test(rest): drive the two authorization-input absorb seams in computeExecCtx — phase-1 measurement (#13906) - #14272

Merged
os-justin merged 2 commits into
mainfrom
claude/issue-13906-execctx-authz-seams
Sep 1, 2026
Merged

test(rest): drive the two authorization-input absorb seams in computeExecCtx — phase-1 measurement (#13906)#14272
os-justin merged 2 commits into
mainfrom
claude/issue-13906-execctx-authz-seams

Conversation

@os-justin

@os-justin os-justin commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Part of #13906

What this PR is

Phase-1 MEASUREMENT delivery: one new test file (packages/rest/src/execctx-authz-input-seam-reachability.test.ts), zero production changesrest-server.ts is not touched, per the dispatch's read-only order. It turns the card's code reading into driven readings, with a positive control beside every fault leg. Pins on permissive answers are measurements, not endorsements: whether any seam should change is the phase-2 ruling, deliberately not taken here. #13906 stays open on merge: the measured defect is unrepaired and phase 2 awaits the maintainer's ruling — which is why the first line above says Part of rather than a closing keyword.

Measured answers to the four dispatch questions (driven at b93d4fd49f)

Q1 — the permissive path IS reachable, driven. With a real ObjectKernel on the kernelManager path: a healthy isolated tenancy service makes the Layer 0 organization_membership_ended refusal fire for an ex-member's org-stamped API key (401 on the wire; the resolver names the reason — that is the positive control that the refusal APPLIES to this fixture). Change ONE condition — register the tenancy service as a factory that throws, the registry's own "registered and failed to construct" rejection (#13905 class) — and the same key is served 200 with full grants. The refusal was skipped, not inapplicable: the resolver-level leg shows accessible_org_ids in hand and lacking the stamped org while the principal is admitted. "Registered and failed" and "never registered" answer byte-identically at the door. The sibling posture-conditional refusal (organization_required, org-less key under isolated) is skipped the same way — measured in the same section. Note: api-key.ts documents absent-posture-admit as a deliberate decision for the org-less refusal; what that comment does not distinguish is "no tenancy service" from "the tenancy service failed" — both arrive as the same absent posture.

Q2 — the card's sharper claim HOLDS, and it is the normal state of the shipped single-kernel wiring. The local kernel in computeExecCtx is assigned only on kernelManager branches (section 0 pins this mechanically from source). Driven: ONE real kernel with a healthy, wall-enforcing tenancy service behind a RECORDING factory, wired both ways. Via kernelManager: 401 refusal, tenancy factory invoked. Via the shipped provider wiring (byte-faithful to the post-#14250 rest-api-plugin.ts providers): 200 for the same ex-member key, and the tenancy factory was never invoked — invocation count 0. On that path the posture is undefined before any failure can occur; no failure is even required.

Q3 — a failed probe and an inactive gate are indistinguishable, at the context field and at the consumer. Positive control: an ACTIVE gate with a healthy session re-read blocks the gated user — enforceAuth answers 403 with the gate's code and message. An inactive gate, a THROWING isAuthGateActive probe, and a FAILED session re-read under an active gate (transient class: identity read succeeds, the gate's second read rejects — the fixture counts the reads, so the gate is proven to have probed) all produce authGate: undefined on the assembled ExecutionContext and the identical not-blocked observable, asserted deep-equal against the inactive leg. The gated user whose session policy says must be blocked is not blocked, and the wire carries no trace.

Q4 — the criterion controls. In-file: the enumeration criterion flags the two named seams (known positives) and never flags wiredEngineOrLoud (the known kept-apart site). Historical control, run outside vitest with the same criterion over git show trees: at the pre-#13910 tree (836a29c27a~1) it reports 10 absorb site lines in computeExecCtx and flags the provider engine seam — the known positive that #13476 was filed to repair; at today's HEAD it reports 9, the provider engine line is gone and wiredEngineOrLoud is present — exactly reproducing the card's own census (10 before / 9 after / 1 loud after).

Premise re-derivation (ZONE 3)

Both seams are still live and still collapse on today's tree after the three 09-01 rest-server.ts merges (#13811, #13910, #14120) and after #13904's provider repair (#14250). #13910 repaired the ENGINE seam beside them; #14250 narrowed the provider absorb to the branded not-registered rejection. Neither reaches the tenancy or auth-gate seams. One repair-relevant fact for phase 2: the tenancy seam's blanket catch now absorbs the #13905 branded not-registered rejection and unbranded construction failures indistinguishably, although the discriminator existsisServiceNotRegisteredError is exported and the shipped objectql provider already uses it one layer down.

Out of scope, not entered

The auth-service seams and the getSession swallow (the CONTEXT-LOST degrade recorded on #13255, which 404s in this repo as the routing comment warned), and the settings/localization seam.

Verification

  • pnpm --filter @objectstack/rest exec vitest run --maxWorkers=2 src/execctx-authz-input-seam-reachability.test.ts — "Test Files 1 passed (1) · Tests 17 passed (17)".
  • Full affected-package suite at the same tree: pnpm --filter @objectstack/rest exec vitest run --maxWorkers=2 — "Test Files 167 passed (167) · Tests 2802 passed (2802)".
  • pnpm --filter @objectstack/rest typecheck — tsc clean plus "check:test-typecheck: OK — test layer compiles under tsconfig.test.json" (the build tsconfig excludes tests, so the test-layer half is the one that measures this file).
  • Gate union derived with node scripts/pm/dispatch-gates.mjs, re-derived after the last commit at b93d4fd49f (stderr names this repo and commit): 26 families both times, comm reconciliation empty in both directions. 25 of 26 green with their own verdict lines — including check:objectql-double-limit ("301 doubles graded, bound applied" — it caught the first fixture being limit-blind; repaired by holding the bound by presence) and check:where-matcher ("325 matchers, refusing conformer"), check:engine-double-contract, check:type-check-debt ("27 ledger entries re-measured, none above recorded"), check:nul-bytes (standard any-edit family, additionally run). scripts/check-test-completeness.mjs exits 3 locally and its own text says to record it as NOT MEASURED outside CI (it parses a saved turbo test log CI supplies).
  • Dogfood suite: not owed — this diff does not touch rest-server.ts (the dispatch names that suite for diffs that do).
  • Changeset: none — tests-only diff, skip-changeset applied per the closed list.

Session: https://claude.ai/code/session_015YPiiDdw96RGS25WLctCQP

Generated by Claude Code

…teExecCtx

Phase-1 MEASUREMENT — no production code changed. Drives, with positive
controls beside every fault leg:

- tenancy posture (kernelManager path): a healthy isolated posture refuses an
  ex-member org-stamped API key (organization_membership_ended, 401 on the
  wire); a tenancy service that is REGISTERED AND FAILS to construct skips the
  refusal and the same key is served 200 — the permissive path is reachable.
  "Registered and failed" and "never registered" answer byte-identically.
- tenancy posture (single-kernel provider path): the local `kernel` is only
  assigned on kernelManager branches, so the posture probe is never issued —
  a healthy, wall-enforcing tenancy service is NEVER INVOKED (recorded), and
  the same deployment facts answer 401 via kernelManager and 200 via the
  shipped provider wiring. The always-undefined claim holds as the normal
  state of that wiring.
- ADR-0069 auth gate: an active gate blocks the gated user (403 + gate code);
  an inactive gate, a throwing probe, and a failed session re-read are
  byte-indistinguishable at the ExecutionContext field and at enforceAuth.
- Section 0 pins the enumeration criterion in-file (flags the two seams,
  never wiredEngineOrLoud); the historical control (same criterion flagging
  the provider engine seam pre-13910, 10 sites before / 9 after) is recorded
  on the card.

Pins are measurements, not endorsements.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015YPiiDdw96RGS25WLctCQP
…atcher gates

The fixture find double now holds the caller's limit bound by presence and
filters through one hand-written matcher that supports equality and the $in
shape the shared resolver actually issues, refusing every other shape loudly.
check:objectql-double-limit graded the new double (301 graded, bound applied)
and check:where-matcher counts the matcher among the refusing conformers.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015YPiiDdw96RGS25WLctCQP
@github-actions github-actions Bot added the size/l label Sep 1, 2026
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 0 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 6eb8e3cc5022c4b1d0007962220444f4f947036dpackageMentionDocs.

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

Labels

skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants