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.
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`.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
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
docs/plans/2026-08-11-p3-pi-first-harness-adapter-design.md(approval18e0488d0).docs/plans/2026-08-11-p3-pi-first-harness-adapter-implementation.md, § Task Two (approval575ad743a; base-pin5f3ababd8).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)
adapter_unavailable.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.interaction_unsupported), never a silent no-op.HarnessActorContextis derived solely from trusted Gateway scope; a caller DTO cannot supplyseatId, native session path, executable, home, or working directory — such fields are rejected withHarnessScopeViolationError, and context derivation never reads the callerrequest. Fails closed.IProviderAdapter,AgentRuntimeProvider, orpackages/types/src/agent/**— the harness layer stays generic.Independent gates (author = pi ≠ integrator-verifier ≠ code/security reviewer)
@mosaicstack/gatewaysuite 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.@mosaicstack/typesunion (satisfies); no prodany; no secrets.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 initialFailed 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.