Warden plan

03 - Connect interaction-surface invariants

← eval suite index


tier: hermetic requires: []


03 - Connect interaction-surface invariants

What it proves

The connect sprint's interaction surface holds structurally: signal-first search verbs, the declarative MCP tool registry as the one verb layer (the old monkey-patch gone), the OAuth 2.1 Resource Server surface (tables + plugin + discovery + the Principal seam), the websocket stream channel with pure ticket crypto, the end-to-end retirement of stored-HyDE, and the C-13 "no new event bus" ruling. These are file/grep-level assertions — fast, deterministic, no DB — that later sprints re-run as a regression floor; the DB-backed boot + full test suite is covered by plan 01's green bar.

Prerequisites

None beyond a clean checkout of the branch under test. (Plan 01 covers the container-backed gates.)

Step 1: signal-first search verbs (C-20, D1)

set -uo pipefail
source "$WARDEN_LIB/assert.sh"
cd "${PROJECT_ROOT:-$(git rev-parse --show-toplevel)}"

SEARCH=server/src/modules/search/search.ts
{ grep -qE "export (async )?function searchSignals" "$SEARCH" \
  && grep -qE "export (async )?function searchWikis" "$SEARCH"; } \
  && warden_pass "scoped engine verbs searchSignals/searchWikis exported (C-20)" \
  || warden_fail "searchSignals/searchWikis not both exported from search.ts"

# The signal verb + the net-new wiki verb reach MCP; legacy alias is gone (D1).
{ grep -qE "name: 'search_wikis'" server/src/mcp/server.ts \
  && ! grep -rq "searchQuerySchema" server/src; } \
  && warden_pass "MCP search_wikis verb present; legacy searchQuerySchema alias gone (D1)" \
  || warden_fail "search_wikis missing or legacy searchQuerySchema alias survives"

Step 2: MCP registry is the one verb layer (C-21)

set -uo pipefail
source "$WARDEN_LIB/assert.sh"
cd "${PROJECT_ROOT:-$(git rev-parse --show-toplevel)}"

{ grep -qE "export function defineTool" server/src/mcp/registry.ts \
  && grep -qE "export function createToolRegistry" server/src/mcp/registry.ts; } \
  && warden_pass "declarative registry (defineTool/createToolRegistry) present (C-21)" \
  || warden_fail "mcp/registry.ts is missing defineTool/createToolRegistry"

# The incumbent monkey-patch is absorbed — server.ts registers via the registry,
# it never calls server.registerTool directly (only registry.ts::applyTo does).
grep -q "registerTool" server/src/mcp/server.ts \
  && warden_fail "server.ts still calls registerTool (monkey-patch not absorbed)" \
  || warden_pass "no registerTool monkey-patch in server.ts — registry is the sole path (C-21)"

Step 3: OAuth 2.1 Resource Server surface (C-14, D20)

set -uo pipefail
source "$WARDEN_LIB/assert.sh"
cd "${PROJECT_ROOT:-$(git rev-parse --show-toplevel)}"

# The five oauth-provider tables exist in the schema (post AS full-lift, 2026-07-08).
MISS=0
for t in oauthClient oauthRefreshToken oauthAccessToken oauthConsent jwks; do
  grep -q "$t" server/src/db/schema.ts || { echo "schema missing oauth table: $t"; MISS=1; }
done
[ "$MISS" = "0" ] \
  && warden_pass "oauth-provider tables (client/refresh/access/consent + jwks) declared in schema.ts (C-14, AS full-lift)" \
  || warden_fail "an oauth-provider table is missing from schema.ts"

# AS = @better-auth/oauth-provider + jwt() (the mcp plugin is RETIRED); RS = @robin/oauth
# guard over an injected token port; getMcpSession fully retired; the 0004 migration shipped.
{ grep -q "oauthProvider" server/src/modules/iam/auth.ts \
  && grep -q "jwt()" server/src/modules/iam/auth.ts \
  && grep -q "oauthGuard" server/src/core/oauth/index.ts \
  && ! grep -qs "getMcpSession" server/src/routes/mcp.ts server/src/routes/ws.ts \
  && ls server/drizzle/migrations/0004_*.sql >/dev/null 2>&1; } \
  && warden_pass "oauth-provider + jwt() AS, @robin/oauth RS guard, getMcpSession retired, 0004 migration present (C-14, D20 AS full-lift)" \
  || warden_fail "the OAuth AS full-lift surface is incomplete (oauth-provider/jwt/guard/getMcpSession-retired/0004)"

# The agent-principal seam (D20/C-16).
{ [ -f server/src/lib/principal.ts ] && grep -q "type Principal" server/src/lib/principal.ts \
  && grep -q "Principal" server/src/routes/mcp.ts; } \
  && warden_pass "Principal seam present + resolved on the MCP route (C-16, D20)" \
  || warden_fail "the Principal seam is missing from lib/principal.ts or routes/mcp.ts"

Step 4: websocket stream channel (C-17/C-18/C-19)

set -uo pipefail
source "$WARDEN_LIB/assert.sh"
cd "${PROJECT_ROOT:-$(git rev-parse --show-toplevel)}"

# Hub with the single publish seam + frame contract + upgrade route.
{ [ -f server/src/stream/hub.ts ] && grep -q "publishToChannel" server/src/stream/hub.ts \
  && [ -f server/src/stream/frames.ts ] && [ -f server/src/routes/ws.ts ]; } \
  && warden_pass "stream hub (publishToChannel) + frames + ws route present (C-19)" \
  || warden_fail "a stream-channel file (hub/frames/ws route) is missing"

# Pure ticket crypto lives in @robin/auth (state-is-the-seam: nonce cache is server-side).
{ [ -f packages/auth/src/ticket.ts ] \
  && grep -q "createStreamTicket" packages/auth/src/ticket.ts \
  && grep -q "verifyStreamTicket" packages/auth/src/ticket.ts; } \
  && warden_pass "pure ws ticket helpers in @robin/auth (C-18)" \
  || warden_fail "createStreamTicket/verifyStreamTicket missing from packages/auth"

# node-server v2 native path (C-17).
grep -qE '"@hono/node-server":[[:space:]]*"\^2' server/package.json \
  && warden_pass "@hono/node-server on ^2 (native upgradeWebSocket, C-17)" \
  || warden_fail "@hono/node-server is not on ^2 (C-17 bump reverted?)"

Step 5: stored-HyDE retired end-to-end (C-01, C-03)

set -uo pipefail
source "$WARDEN_LIB/assert.sh"
cd "${PROJECT_ROOT:-$(git rev-parse --show-toplevel)}"

SCOPE="server/src server/scripts packages/agent/src"

# Zero live references to the retired generator/table/factory anywhere.
REF=$(grep -rniE "wiki_agent_schema|wikiagentschema|ensureagentschema|createhydeagent" $SCOPE --include='*.ts' 2>/dev/null | wc -l | tr -d ' ')
[ "$REF" = "0" ] \
  && warden_pass "zero wiki_agent_schema/ensureAgentSchema/createHydeAgent references (C-01)" \
  || { grep -rniE "wiki_agent_schema|wikiagentschema|ensureagentschema|createhydeagent" $SCOPE --include='*.ts' | head; warden_fail "$REF stored-HyDE references remain"; }

# The 'hyde' concept is gone from code (identifiers, not an English word in prose).
HYDE=$(grep -rniE '\bhyde\b' $SCOPE --include='*.ts' 2>/dev/null | grep -vE ':[0-9]+:[[:space:]]*(\*|//|/\*)' | wc -l | tr -d ' ')
[ "$HYDE" = "0" ] \
  && warden_pass "no hyde vocabulary in code (C-03)" \
  || { grep -rniE '\bhyde\b' $SCOPE --include='*.ts' | grep -vE ':[0-9]+:[[:space:]]*(\*|//|/\*)' | head; warden_fail "$HYDE hyde references remain in code"; }

# The generator lib + backfill runner are deleted, and 0002 drops the table.
{ [ ! -f server/src/lib/wiki-agent-schema.ts ] && [ ! -f server/src/lib/backfill-runner.ts ] \
  && grep -q "DROP TABLE IF EXISTS wiki_agent_schema" server/drizzle/migrations/0002_*.sql; } \
  && warden_pass "generator + backfill-runner deleted; 0002 drops wiki_agent_schema (C-01)" \
  || warden_fail "a stored-HyDE file survives or the 0002 DROP is missing"

Step 6: event-bus spike resolved — no new bus (C-13)

set -uo pipefail
source "$WARDEN_LIB/assert.sh"
cd "${PROJECT_ROOT:-$(git rev-parse --show-toplevel)}"

# No new bus infrastructure: no pg-boss dep, no QueueEvents-as-bus, no raw
# LISTEN/NOTIFY. (Case-sensitive SQL keywords + unambiguous tokens; comment
# lines excluded so a docblock mentioning the rejected candidates can't red it.)
BUS=$(grep -rnE '\bpg-boss\b|\bQueueEvents\b|\bLISTEN\b|\bNOTIFY\b' server/src packages --include='*.ts' 2>/dev/null \
  | grep -vE ':[0-9]+:[[:space:]]*(\*|//|/\*)' | wc -l | tr -d ' ')
[ "$BUS" = "0" ] \
  && warden_pass "no new event-bus infra (pg-boss/QueueEvents/LISTEN/NOTIFY) — C-13 held" \
  || { grep -rnE '\bpg-boss\b|\bQueueEvents\b|\bLISTEN\b|\bNOTIFY\b' server/src packages --include='*.ts' | grep -vE ':[0-9]+:[[:space:]]*(\*|//|/\*)' | head; warden_fail "$BUS candidate event-bus references present (C-13)"; }

# The retrieval eval harness (C-10) shipped as a runnable artifact.
{ [ -f server/scripts/search-eval.ts ] && [ -f server/scripts/fixtures/search-eval.yaml ] \
  && grep -q '"search:eval"' server/package.json; } \
  && warden_pass "golden-set eval harness + fixture + search:eval script present (C-10)" \
  || warden_fail "the C-10 eval harness artifact is missing"