feat(gateway): harness registry, fake adapter, no-substitution suite (P3 Slice Zero, Task 2) #1168

Merged
jason.woltje merged 1 commits from feat/p3-slice0-task2-harness-registry into next 2026-08-12 00:40:04 +00:00
Owner

P3 Slice Zero, Task 2 — harness registry, fake adapter, and no-substitution suite

Adds the generic Gateway harness registry, HarnessService, and a fake adapter + reusable contract suite for @mosaicstack/gateway. Builds on the Task-1 generic harness contract types (@mosaicstack/types, merged via #1166). No real adapter, no Pi wiring, no module wiring, no Web/HTTP surfaces — those are later Slice Zero tasks.

Traceability

  • Approved design: jarvis-brain docs/plans/2026-08-11-p3-pi-first-harness-adapter-design.md (approval 18e0488d0).
  • Approved implementation plan: jarvis-brain docs/plans/2026-08-11-p3-pi-first-harness-adapter-implementation.md, § Task Two (approval 575ad743a; base-pin 5f3ababd8).
  • Base: branched off next @ fb9f9cda (SLICE_ZERO_TASK2_BASE_SHA), unchanged at PR time.

Files (8, path-fenced — all under apps/gateway/src/harness/)

  • harness.tokens.ts (new) — DI tokens.
  • harness.registry.ts (+ .spec.ts) (new) — register/lookup by harness id.
  • harness.service.ts (+ .spec.ts) (new) — selection validation, lifecycle, trusted actor context.
  • testing/harness-adapter.contract.ts (new) — reusable adapter conformance suite.
  • testing/fake-harness.adapter.ts (+ .spec.ts) (new) — in-memory fake for the contract.

Design conformance (independently reviewed)

  • Registry: blank and duplicate adapter ids rejected; unknown harness id → adapter_unavailable.
  • Selection — no substitution (type-enforced + behavioral): exact-tuple match, no first-catalog-row / any fallback; unknown tuple → selection_invalid, known-but-unavailable → model_unavailable; the error DTO echoes the requested tuple unchanged — there is no "effective selection" field through which a divergent selection could be reported. A contract test asserts no first-row substitution.
  • Lifecycle: create/resume, detach, evict, and end have distinct observable effects (not aliases/no-ops).
  • Unsupported capability → typed error (interaction_unsupported), never a silent no-op.
  • Trusted actor context (security boundary): HarnessActorContext is derived solely from trusted Gateway scope; a caller DTO cannot supply seatId, native session path, executable, home, or working directory — such fields are rejected with HarnessScopeViolationError, and context derivation never reads the caller request. Fails closed.
  • No import of / dependency on IProviderAdapter, AgentRuntimeProvider, or packages/types/src/agent/** — the harness layer stays generic.
  • No new Nest module wiring in this task, so no #1145-class unresolved-provider defect is introduced.

Independent gates (author = pi ≠ integrator-verifier ≠ code/security reviewer)

  • Integrator-verify: PASS — lineage + 8-file path-fence + scope-fence; focused specs 17/17; full @mosaicstack/gateway suite 714 passed / 17 skipped / 0 failed; typecheck rc 0; build rc 0. Anti-masking (both invariants genuine): forcing a first-row substitution turns a no-substitution test RED; making the trusted-context check a no-op turns the hostile-DTO test RED; both restored to green.
  • Code + security review: PASS — no blocker/major/minor findings; boundary fails closed with no leaky error surfaces; error codes reuse the shared @mosaicstack/types union (satisfies); no prod any; no secrets.
  • Red-first was a module-load failure (harness modules absent at base → 3 suites failed to resolve), the honest red for from-scratch modules.

Reviewer/verifier env note (for the merge-gate)

A fresh worktree needs the gateway's gitignored workspace-dep dist/ built first:
corepack pnpm --filter "@mosaicstack/gateway^..." build. An initial Failed to resolve entry for package "@mosaicstack/auth" is that missing dist, not harness behavior.

Pre-push hook (typecheck/lint/format) passed on push. Merge-gate re-derivation to follow before merge. Disjoint from the merged #1166 (types) and #1167 (coord). Refs P3 Slice Zero Task 2.

## P3 Slice Zero, Task 2 — harness registry, fake adapter, and no-substitution suite Adds the generic Gateway **harness registry**, **`HarnessService`**, and a **fake adapter + reusable contract suite** for `@mosaicstack/gateway`. Builds on the Task-1 generic harness contract types (`@mosaicstack/types`, merged via #1166). No real adapter, no Pi wiring, no module wiring, no Web/HTTP surfaces — those are later Slice Zero tasks. ### Traceability - **Approved design:** jarvis-brain `docs/plans/2026-08-11-p3-pi-first-harness-adapter-design.md` (approval `18e0488d0`). - **Approved implementation plan:** jarvis-brain `docs/plans/2026-08-11-p3-pi-first-harness-adapter-implementation.md`, **§ Task Two** (approval `575ad743a`; base-pin `5f3ababd8`). - **Base:** branched off `next` @ `fb9f9cda` (`SLICE_ZERO_TASK2_BASE_SHA`), unchanged at PR time. ### Files (8, path-fenced — all under `apps/gateway/src/harness/`) - `harness.tokens.ts` (new) — DI tokens. - `harness.registry.ts` (+ `.spec.ts`) (new) — register/lookup by harness id. - `harness.service.ts` (+ `.spec.ts`) (new) — selection validation, lifecycle, trusted actor context. - `testing/harness-adapter.contract.ts` (new) — reusable adapter conformance suite. - `testing/fake-harness.adapter.ts` (+ `.spec.ts`) (new) — in-memory fake for the contract. ### Design conformance (independently reviewed) - **Registry:** blank and duplicate adapter ids rejected; unknown harness id → `adapter_unavailable`. - **Selection — no substitution (type-enforced + behavioral):** exact-tuple match, **no first-catalog-row / any fallback**; unknown tuple → `selection_invalid`, known-but-unavailable → `model_unavailable`; the error DTO echoes the **requested tuple unchanged** — there is no "effective selection" field through which a divergent selection could be reported. A contract test asserts no first-row substitution. - **Lifecycle:** create/resume, detach, evict, and end have distinct observable effects (not aliases/no-ops). - **Unsupported capability** → typed error (`interaction_unsupported`), never a silent no-op. - **Trusted actor context (security boundary):** `HarnessActorContext` is derived **solely** from trusted Gateway scope; a caller DTO cannot supply `seatId`, native session path, executable, home, or working directory — such fields are rejected with `HarnessScopeViolationError`, and context derivation never reads the caller `request`. Fails **closed**. - No import of / dependency on `IProviderAdapter`, `AgentRuntimeProvider`, or `packages/types/src/agent/**` — the harness layer stays generic. - No new Nest module wiring in this task, so **no #1145-class unresolved-provider defect** is introduced. ### Independent gates (author = pi ≠ integrator-verifier ≠ code/security reviewer) - **Integrator-verify:** PASS — lineage + 8-file path-fence + scope-fence; focused specs 17/17; **full `@mosaicstack/gateway` suite 714 passed / 17 skipped / 0 failed**; typecheck rc 0; build rc 0. **Anti-masking (both invariants genuine):** forcing a first-row substitution turns a no-substitution test RED; making the trusted-context check a no-op turns the hostile-DTO test RED; both restored to green. - **Code + security review:** PASS — no blocker/major/minor findings; boundary fails closed with no leaky error surfaces; error codes reuse the shared `@mosaicstack/types` union (`satisfies`); no prod `any`; no secrets. - Red-first was a module-load failure (harness modules absent at base → 3 suites failed to resolve), the honest red for from-scratch modules. ### Reviewer/verifier env note (for the merge-gate) A fresh worktree needs the gateway's gitignored workspace-dep `dist/` built first: `corepack pnpm --filter "@mosaicstack/gateway^..." build`. An initial `Failed to resolve entry for package "@mosaicstack/auth"` is that missing dist, **not** harness behavior. Pre-push hook (typecheck/lint/format) passed on push. Merge-gate re-derivation to follow before merge. Disjoint from the merged #1166 (types) and #1167 (coord). `Refs P3 Slice Zero Task 2`.
jason.woltje added 1 commit 2026-08-12 00:34:54 +00:00
jason.woltje merged commit a186922e3a into next 2026-08-12 00:40:04 +00:00
Sign in to join this conversation.