From 32d431b42b9bcf7c56015a373b5e55445e9ab985 Mon Sep 17 00:00:00 2001 From: Jarvis Date: Mon, 13 Jul 2026 14:15:17 -0500 Subject: [PATCH] docs(tess): remediate M5 qualification findings --- docs/SITEMAP.md | 32 ++++++++++ .../tess-m4-001-mos-coordination.md | 2 +- docs/tess/M5-MIGRATION-CUTOVER.md | 2 +- docs/tess/M5-MIGRATION-INVENTORY.md | 2 +- docs/tess/M5-MIGRATION-ROLLBACK.md | 2 +- docs/tess/MOS-COORDINATION.md | 2 +- docs/tess/THREAT-MODEL.md | 22 ++++--- .../gateway-api.interaction-errors.test.ts | 61 +++++++++++++++++++ 8 files changed, 111 insertions(+), 14 deletions(-) create mode 100644 docs/SITEMAP.md create mode 100644 packages/mosaic/src/tui/gateway-api.interaction-errors.test.ts diff --git a/docs/SITEMAP.md b/docs/SITEMAP.md new file mode 100644 index 0000000..cab3d2c --- /dev/null +++ b/docs/SITEMAP.md @@ -0,0 +1,32 @@ +# Documentation Sitemap + +## Tess interaction agent + +### Operator guides + +- [User guide](tess/USER-GUIDE.md) — authorized session, attach, send, stop, and handoff workflows. +- [Admin guide](tess/ADMIN-GUIDE.md) — deployment configuration, policy, and approval controls. +- [Developer guide](tess/DEVELOPER-GUIDE.md) — provider contracts, scope boundaries, and test workflow. +- [Plugin guide](tess/PLUGIN-GUIDE.md) — adapter, redaction, and identity-as-data requirements. +- [Operations guide](tess/OPERATIONS-GUIDE.md) — readiness, recovery, and incident-safe procedures. + +### Architecture and security + +- [Architecture](tess/ARCHITECTURE.md) +- [Threat model](tess/THREAT-MODEL.md) +- [Mos coordination boundary](tess/MOS-COORDINATION.md) +- [Hermes runtime adapter design](tess/hermes-runtime-adapter-design.md) +- [Operator plugin sketch](tess/M4-003-OPERATOR-PLUGIN-SKETCH.md) + +### API contract + +- [Tess OpenAPI contract](openapi-tess.yaml) + +### Migration and qualification + +- [Migration inventory](tess/M5-MIGRATION-INVENTORY.md) +- [Cutover procedure](tess/M5-MIGRATION-CUTOVER.md) +- [Rollback procedure](tess/M5-MIGRATION-ROLLBACK.md) +- [Retention and deprecation evidence](tess/M5-MIGRATION-RETENTION-DEPRECATION.md) +- [Verification matrix](tess/VERIFICATION-MATRIX.md) +- [Documentation checklist](tess/M5-003-DOCUMENTATION-CHECKLIST.md) diff --git a/docs/scratchpads/tess-m4-001-mos-coordination.md b/docs/scratchpads/tess-m4-001-mos-coordination.md index f224605..e5ad5b0 100644 --- a/docs/scratchpads/tess-m4-001-mos-coordination.md +++ b/docs/scratchpads/tess-m4-001-mos-coordination.md @@ -23,7 +23,7 @@ Created `docs/tess/MOS-COORDINATION.md`. Mos approved the design and selected th ## Progress checkpoint — 2026-07-13 - Implemented `MosCoordinationPort` with handoff/observe/result only, an authority-checking client, and deterministic native adapter in `@mosaicstack/coord`. -- Implemented the gateway `MosCoordinationService`, deriving requester identity from trusted configuration and actor/tenant/correlation from authenticated context. +- Implemented the gateway `InteractionCoordinationService`, deriving requester identity from trusted configuration and actor/tenant/correlation from authenticated context. - Added contract and gateway boundary tests for configurable identities, native round-trip, unconfigured requester, self-delegation, target drift, and cross-tenant observe/result denial before adapter invocation. - Did not modify `apps/gateway/src/commands/command-authorization.service.ts`. diff --git a/docs/tess/M5-MIGRATION-CUTOVER.md b/docs/tess/M5-MIGRATION-CUTOVER.md index cd5a715..973eb96 100644 --- a/docs/tess/M5-MIGRATION-CUTOVER.md +++ b/docs/tess/M5-MIGRATION-CUTOVER.md @@ -6,7 +6,7 @@ This procedure is evidence-bound. It does not authorize a production cutover unt 2. Query the normalized runtime capability surface, not a Hermes API directly. Confirm the session capabilities required for the operation are advertised. 3. Query the transitional matrix through `RuntimeProviderService.transitionalCapabilityMatrix` (`apps/gateway/src/agent/runtime-provider-registry.service.ts`). Kanban, skills, memory, tools, and cron must remain `unsupported`; stop rather than route those operations through Hermes. 4. Route new memory activity through the Mosaic operator-memory plugin path; there is no landed Hermes memory import. -5. Use `MosCoordinationService` for orchestration handoff. Tess does not take Mos authority. +5. Use `InteractionCoordinationService` (`apps/gateway/src/coord/interaction-coordination.service.ts`) for orchestration handoff. The interaction agent does not take configured orchestrator authority. 6. Record the qualification evidence and only then update an external deployment/channel binding through its separately authorized operational process. No claim here authorizes bulk transcript copying, data-schema migration, or enabling an unsupported transitional capability. diff --git a/docs/tess/M5-MIGRATION-INVENTORY.md b/docs/tess/M5-MIGRATION-INVENTORY.md index 05358aa..adaeebb 100644 --- a/docs/tess/M5-MIGRATION-INVENTORY.md +++ b/docs/tess/M5-MIGRATION-INVENTORY.md @@ -7,5 +7,5 @@ Hermes is a reference adapter, not a Mosaic core dependency. `packages/agent/src | sessions, hierarchy, streaming, send/attach/terminate | `HermesRuntimeProvider` plus `hermes-runtime-provider.test.ts` | adapted | | Kanban, skills, memory, tools, cron | normalized matrix in `HermesRuntimeProvider.transitionalCapabilityMatrix`; each is `unsupported` and `assertTransitionalCapability` denies before a transport call | deferred / fail-closed | | operator memory | `packages/memory/src/operator-memory-plugin.ts`, constructed by `apps/gateway/src/memory/memory.module.ts` and session-scoped by `apps/gateway/src/agent/agent.service.ts` | native Mosaic path | -| orchestration handoff | `apps/gateway/src/coord/mos-coordination.service.ts` retains authenticated handoff/observe/result ownership checks | native Mosaic path | +| orchestration handoff | `InteractionCoordinationService` in `apps/gateway/src/coord/interaction-coordination.service.ts` retains authenticated handoff/observe/result ownership checks | native Mosaic path | | transcripts, profiles, preferences | no Hermes importer/schema mapping landed | no automatic migration | diff --git a/docs/tess/M5-MIGRATION-ROLLBACK.md b/docs/tess/M5-MIGRATION-ROLLBACK.md index 34ec700..6f659d3 100644 --- a/docs/tess/M5-MIGRATION-ROLLBACK.md +++ b/docs/tess/M5-MIGRATION-ROLLBACK.md @@ -6,5 +6,5 @@ Rollback is configuration/binding reversal, not a database rollback: no Hermes s 2. Keep the gateway registration and core contracts unchanged unless a reviewed code rollback is required; `AgentRuntimeProviderRegistry` registration is explicit and non-replacing (`packages/agent/src/runtime-provider-registry.ts`). 3. Do not replay an unsupported Kanban, skills, memory, tools, or cron operation. The transitional matrix is intentionally fail-closed. 4. Preserve Mosaic audit, session, and operator-memory records under their normal scoped retention rules; do not copy them into Hermes as a rollback shortcut. -5. For an in-flight coordination request, use the owned handoff observation/result flow in `MosCoordinationService`; do not create a second orchestrator path. +5. For an in-flight coordination request, use the owned handoff observation/result flow in `InteractionCoordinationService` (`apps/gateway/src/coord/interaction-coordination.service.ts`); do not create a second orchestrator path. 6. Capture the binding reversal, affected scope, correlation IDs, and reason in the approved operational record before retrying a cutover. diff --git a/docs/tess/MOS-COORDINATION.md b/docs/tess/MOS-COORDINATION.md index 07092a8..cd47929 100644 --- a/docs/tess/MOS-COORDINATION.md +++ b/docs/tess/MOS-COORDINATION.md @@ -58,7 +58,7 @@ and the requester agent from trusted authentication/configuration only. ## Enforcement point -`apps/gateway` owns a `MosCoordinationService` boundary that compares the +`apps/gateway` owns an `InteractionCoordinationService` (`apps/gateway/src/coord/interaction-coordination.service.ts`) boundary that compares the trusted configured requester/target identities and rejects all of the following before calling a transport: unconfigured requester, self-delegation, target identity drift, cross-tenant observe/result lookup, and attempts to observe or diff --git a/docs/tess/THREAT-MODEL.md b/docs/tess/THREAT-MODEL.md index 8e6a79c..fef2a4c 100644 --- a/docs/tess/THREAT-MODEL.md +++ b/docs/tess/THREAT-MODEL.md @@ -33,14 +33,18 @@ Trust boundaries: Discord→plugin, CLI→gateway, plugin→gateway service iden 6. Every externally caused operation is replay-safe and correlated. 7. Provider capability absence is a denial, not an invitation to shell around it. -## Existing Findings That Block Tess +## Closed Prerequisite Findings -- Command executor lacks server-side enforcement for declared scopes. -- Session list/reuse/destroy surfaces are not owner-filtered consistently. -- MCP schemas accept caller-supplied user identity. -- Discord plugin lacks a complete authenticated service ingress and user/channel allowlists. -- Chat/tool persistence lacks mandatory redaction. -- Sessions/pending Discord output are in-memory and not restart-safe. -- Session GC currently performs globally scoped promotion. +The original M1 findings below are closed by landed controls and retained for audit traceability. -These are tracked as M1 security prerequisites and must pass independent security review before Tess ingress is enabled. +| Former finding | Closed evidence | +| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Command scope/role enforcement | `apps/gateway/src/commands/command-authorization.service.ts` and its authorization tests enforce the server-side approval boundary. | +| Cross-owner session access | Gateway session ownership tests cover server-derived owner and tenant scope. | +| Caller-controlled MCP identity | MCP tools derive actor and tenant from authenticated gateway context. | +| Missing Discord ingress allowlists | `apps/gateway/src/plugin/plugin.module.ts` requires the guild, channel, and user allowlist environment values; `apps/gateway/src/plugin/discord-ingress.security.spec.ts` exercises denial and configured ingress. | +| Missing redaction before persistence/egress | Gateway and log redaction coverage verifies sensitive content is classified before durable storage or channel delivery. | +| In-memory-only restart safety | `packages/agent/src/durable-session.test.ts` reconstructs durable identity, inbox/outbox, checkpoints, and handoffs after simulated restart. | +| Globally scoped session GC | `apps/gateway/src/gc/session-gc.service.spec.ts` verifies session-only collection and the absence of automatic global collection entry points. | + +These controls remain subject to the runtime's independent review and release qualification gates. diff --git a/packages/mosaic/src/tui/gateway-api.interaction-errors.test.ts b/packages/mosaic/src/tui/gateway-api.interaction-errors.test.ts new file mode 100644 index 0000000..f2dc36a --- /dev/null +++ b/packages/mosaic/src/tui/gateway-api.interaction-errors.test.ts @@ -0,0 +1,61 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { + attachInteractionSession, + sendInteractionMessage, + stopInteractionSession, +} from './gateway-api.js'; + +const gateway = 'https://gateway.example.test'; +const cookie = 'session=test'; +const base = { agentName: 'Nova', correlationId: 'corr-1', sessionId: 'session-1' }; + +afterEach(() => vi.unstubAllGlobals()); + +/** The TUI must preserve gateway denial type/status rather than treating it as success. */ +describe('interaction gateway error mapping', (): void => { + it.each([ + { + name: 'attach unauthorized', + response: { status: 401, message: 'Invalid or expired session' }, + invoke: (): Promise => attachInteractionSession(gateway, cookie, base), + expected: + 'Failed to attach interaction session (401): {"message":"Invalid or expired session"}', + }, + { + name: 'send invalid request', + response: { status: 400, message: 'Content and idempotency key are required' }, + invoke: (): Promise => + sendInteractionMessage(gateway, cookie, { + ...base, + content: 'hello', + idempotencyKey: 'message-1', + }), + expected: + 'Failed to send interaction message (400): {"message":"Content and idempotency key are required"}', + }, + { + name: 'stop forbidden', + response: { status: 403, message: 'Runtime termination approval denied' }, + invoke: (): Promise => + stopInteractionSession(gateway, cookie, { ...base, approvalRef: 'approval-1' }), + expected: + 'Failed to stop interaction session (403): {"message":"Runtime termination approval denied"}', + }, + ])( + '$name preserves the typed gateway denial in the CLI error', + async ({ response, invoke, expected }) => { + vi.stubGlobal( + 'fetch', + vi.fn( + async () => + new Response(JSON.stringify({ message: response.message }), { + status: response.status, + headers: { 'Content-Type': 'application/json' }, + }), + ), + ); + + await expect(invoke()).rejects.toThrow(expected); + }, + ); +});