Warden plan

15 - v1.2 promotion payload (better-auth 1.7 migration + #136 + agent hardening)

← eval suite index


tier: destructive requires: [needs-postgres]


15 - v1.2 promotion payload (better-auth 1.7 migration + #136 + agent hardening)

What it proves

The canary→main promotion ships more than the bug-sweep: the better-auth 1.7 upgrade (auth-schema migration + new OAuth resource registry) and the #136 citation work, plus two agent-hardening fixes that ride along. Plans 13/14 cover the sprint issues; this plan is the promotion-payload floor for everything else that lands on main in the same push, so a regression in any of it blocks the promotion:

Prerequisites

Step 1: better-auth 1.7 migration acceptance on a populated pre-1.7 DB

set -uo pipefail
source "$WARDEN_LIB/assert.sh"
cd "${PROJECT_ROOT:-$(git rev-parse --show-toplevel)}"
# shellcheck disable=SC1091
source "${WARDEN_ENV_FILE:?WARDEN_ENV_FILE not set — run this plan via .warden/run.sh}"

MIG=server/drizzle/migrations
MIG_URL="${DATABASE_URL%/*}/mig_test"
LOG=/tmp/warden-mig.log
: >"$LOG"

# Scratch DB (mirrors the upgrade PR's mig_test harness): a throwaway DB stepped
# from the pre-1.7 migrations (0000–0011), seeded with a legacy credential
# account, then advanced across the 1.7 migrations (0012+0013).
psql "$ADMIN_DATABASE_URL" -q -X -c "DROP DATABASE IF EXISTS mig_test;" >>"$LOG" 2>&1
psql "$ADMIN_DATABASE_URL" -q -X -c "CREATE DATABASE mig_test;"        >>"$LOG" 2>&1
psql "$MIG_URL" -q -X -c "CREATE EXTENSION IF NOT EXISTS vector;"       >>"$LOG" 2>&1

# Apply the pre-1.7 migrations 0000–0011 (the first 12 sorted files). --> statement-breakpoint
# markers are comments to psql; ON_ERROR_STOP surfaces a bad step.
apply_ok=1
for f in $(ls "$MIG"/*.sql | sort | head -12); do
  psql "$MIG_URL" -q -X -v ON_ERROR_STOP=1 -f "$f" >>"$LOG" 2>&1 || { apply_ok=0; echo "FAILED at $f" >>"$LOG"; break; }
done
if [ "$apply_ok" -ne 1 ]; then tail -20 "$LOG"; warden_fail "could not apply pre-1.7 migrations 0000–0011 to mig_test"; fi

# Seed a legacy credential account with NO issuer (the pre-1.7 shape). Only the
# NOT NULL columns are supplied; the rest carry defaults.
psql "$MIG_URL" -q -X -v ON_ERROR_STOP=1 >>"$LOG" 2>&1 <<'SQL'
INSERT INTO users (id, email) VALUES ('mig-user-1', 'mig-legacy@robin.test');
INSERT INTO accounts (id, user_id, account_id, provider_id, password)
  VALUES ('mig-acct-1', 'mig-user-1', 'mig-legacy@robin.test', 'credential', 'scrypt$legacy$hash');
SQL

# Advance across the 1.7 migrations.
psql "$MIG_URL" -q -X -v ON_ERROR_STOP=1 -f "$MIG"/0012_*.sql >>"$LOG" 2>&1 \
  && psql "$MIG_URL" -q -X -v ON_ERROR_STOP=1 -f "$MIG"/0013_*.sql >>"$LOG" 2>&1 \
  || { tail -20 "$LOG"; warden_fail "1.7 migrations 0012/0013 failed to apply over the populated DB"; }

# MIG-1: issuer backfilled to the synthetic credential issuer.
ISS=$(psql "$MIG_URL" -tA -c "SELECT issuer FROM accounts WHERE id='mig-acct-1';")
if [ "$ISS" = "local:credential" ]; then
  warden_pass "MIG-1: legacy credential account backfilled to issuer='local:credential'"
else
  warden_fail "MIG-1: issuer backfill wrong — expected 'local:credential', got '$ISS'"
fi

# MIG-2: issuer NOT NULL enforced (an insert with no issuer must be rejected).
if psql "$MIG_URL" -q -X -v ON_ERROR_STOP=1 \
     -c "INSERT INTO accounts (id,user_id,account_id,provider_id) VALUES ('mig-null','mig-user-1','nn-acct','credential');" >/dev/null 2>&1; then
  warden_fail "MIG-2: issuer NOT NULL not enforced — an issuer-less insert succeeded"
else
  warden_pass "MIG-2: accounts.issuer is NOT NULL (issuer-less insert rejected)"
fi

# MIG-3: (issuer, account_id) unique enforced (duplicate of the backfilled row).
if psql "$MIG_URL" -q -X -v ON_ERROR_STOP=1 \
     -c "INSERT INTO accounts (id,user_id,account_id,provider_id,issuer) VALUES ('mig-dup','mig-user-1','mig-legacy@robin.test','credential','local:credential');" >/dev/null 2>&1; then
  warden_fail "MIG-3: accounts_issuer_account_id_uq not enforced — duplicate (issuer,account_id) inserted"
else
  warden_pass "MIG-3: (issuer, account_id) unique index enforced"
fi

# MIG-4: jwks gained alg + crv.
JCOLS=$(psql "$MIG_URL" -tA -c "SELECT count(*) FROM information_schema.columns WHERE table_name='jwks' AND column_name IN ('alg','crv');")
if [ "$JCOLS" = "2" ]; then
  warden_pass "MIG-4: jwks has both alg and crv columns"
else
  warden_fail "MIG-4: jwks alg/crv missing — found $JCOLS of 2"
fi

# MIG-5: the three 1.7 OAuth resource tables exist.
OT=$(psql "$MIG_URL" -tA -c "SELECT count(*) FROM (SELECT to_regclass('public.oauth_resource') a, to_regclass('public.oauth_client_resource') b, to_regclass('public.oauth_client_assertion') c) t WHERE a IS NOT NULL AND b IS NOT NULL AND c IS NOT NULL;")
if [ "$OT" = "1" ]; then
  warden_pass "MIG-5: oauth_resource + oauth_client_resource + oauth_client_assertion tables exist"
else
  warden_fail "MIG-5: a 1.7 OAuth resource table is missing (oauth_resource/oauth_client_resource/oauth_client_assertion)"
fi

psql "$ADMIN_DATABASE_URL" -q -X -c "DROP DATABASE IF EXISTS mig_test;" >>"$LOG" 2>&1 || true

Step 2: 1.7 runtime auth flows on the migrated schema

set -uo pipefail
source "$WARDEN_LIB/assert.sh"
cd "${PROJECT_ROOT:-$(git rev-parse --show-toplevel)}"
# shellcheck disable=SC1091
source "${WARDEN_ENV_FILE:?WARDEN_ENV_FILE not set — run this plan via .warden/run.sh}"

# The committed suites exercise 1.7 runtime against the current (post-migration)
# schema: scrypt password verify post-migration (auth-session-passthrough); DCR
# 201 + PKCE asymmetric JWT + unregistered-resource invalid_target (oauth-mcp);
# the full PKCE capture flow (capture-oauth-pkce).
if pnpm --filter @robin/server exec vitest run \
     src/__tests__/auth-session-passthrough.test.ts \
     src/__tests__/oauth-mcp.test.ts \
     src/__tests__/capture-oauth-pkce.integration.test.ts \
     >/tmp/warden-authflows.log 2>&1; then
  warden_pass "1.7 runtime auth flows pass on the migrated schema (login, DCR, PKCE JWT, invalid_target)"
else
  tail -30 /tmp/warden-authflows.log
  warden_fail "1.7 runtime auth-flow suite FAILED — see /tmp/warden-authflows.log"
fi

Step 3: #136 citation floor

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

# CI-1 (static): the org content-style setting reaches the RENDER path, not just
# the admin form — the wiki detail page and the Socrates editor surface both
# thread the style/citation setting (via useContentStyleSettings / citationTemplate /
# citationFormat), which is the substance of #136.
WP="app/src/app/(shell)/wiki/[id]/page.tsx"
ES="app/src/components/socrates/editor/EditorSurface.tsx"
# Require BOTH the hook AND the render-anchored prop pass-through per surface —
# `citationStyle=` is the setting actually handed to the child render. An OR that
# accepts a bare `useContentStyleSettings` import is vacuous: it stays green when
# the prop pass-through (the substance of #136) is removed but the hook import
# is orphaned. Formatting-tolerant (matches `citationStyle={...}` any spacing).
# Plan-drift guard: fail loudly (not a bash error) if a surface gets moved/renamed
# again, so history tooling records it as an assertion, not a script crash.
for f in "$WP" "$ES"; do
  [ -f "$f" ] || { warden_fail "#136 CI-1: render surface missing — plan drift: $f"; exit 0; }
done
if grep -q "useContentStyleSettings" "$WP" && grep -q "citationStyle=" "$WP" \
   && grep -q "useContentStyleSettings" "$ES" && grep -q "citationStyle=" "$ES"; then
  warden_pass "#136 CI-1: content-style setting threads to the render (wiki detail + editor surface)"
else
  warden_fail "#136 CI-1: a render surface no longer threads the content-style/citation setting"
fi

# CI-2: the citation formatter itself is green (jsdom/unit, no DB).
if pnpm --filter @robin/app exec vitest run src/lib/citationFormat.test.ts >/tmp/warden-citation.log 2>&1; then
  warden_pass "#136: citationFormat unit suite passes"
else
  tail -20 /tmp/warden-citation.log
  warden_fail "#136: citationFormat suite FAILED — see /tmp/warden-citation.log"
fi

Step 4: Socrates domain hard-filter (D34)

set -uo pipefail
source "$WARDEN_LIB/assert.sh"
cd "${PROJECT_ROOT:-$(git rev-parse --show-toplevel)}"
# shellcheck disable=SC1091
source "${WARDEN_ENV_FILE:?WARDEN_ENV_FILE not set — run this plan via .warden/run.sh}"

# D34: the agent's tool layer hard-filters domains to the caller's scope.
if pnpm --filter @robin/server exec vitest run src/agent/tools.test.ts >/tmp/warden-agent-tools.log 2>&1; then
  warden_pass "D34: Socrates domain hard-filter (agent tools) suite passes"
else
  tail -20 /tmp/warden-agent-tools.log
  warden_fail "D34: agent tools suite FAILED — domain hard-filter may have regressed; see /tmp/warden-agent-tools.log"
fi

Step 5: propose_edit provider-compatibility

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

# PE-1 (static): ProposeEditOutput.confidence stays a plain z.number() with NO
# .min(/.max( — Zod min/max emit minimum/maximum in the JSON schema, which
# OpenRouter's Anthropic/Azure/Bedrock structured-output providers reject
# (400s every call). The 0..1 range lives in the prompt + a server-side clamp.
PE=server/src/agent/propose-edit.ts
if grep -q "confidence: z.number()," "$PE" \
   && ! grep -nE "confidence:\s*z\.number\(\)\s*\.(min|max)\(" "$PE" >/dev/null; then
  warden_pass "propose_edit: confidence is a plain z.number() (no minimum/maximum — provider-compatible)"
else
  warden_fail "propose_edit: confidence carries .min(/.max( again — will 400 Anthropic/Azure/Bedrock structured output"
fi

# PE-2: the propose_edit output/gate suite is green.
if pnpm --filter @robin/server exec vitest run src/agent/propose-edit.test.ts >/tmp/warden-propose-edit.log 2>&1; then
  warden_pass "propose_edit output/gate suite passes"
else
  tail -20 /tmp/warden-propose-edit.log
  warden_fail "propose_edit suite FAILED — see /tmp/warden-propose-edit.log"
fi

Shape (note for the next author)

This is the promotion-payload floor: everything that lands on main in the canary→main push but is NOT a bug-sweep issue (plans 13/14 own those). Step 1 is the only novel harness — a throwaway mig_test DB stepped through the real migration files, mirroring the upgrade PR's scratch-DB pattern, because the migration's data behavior (issuer backfill over pre-existing rows, then the NOT NULL + unique tightening) cannot be proven by pushing the final schema; it has to be replayed over a populated pre-1.7 DB. Steps 2–5 re-run committed suites as the regression floor, gated by two fast static guards (#136 render-path threading, propose_edit provider-compat).

Stays manual / not asserted here: