Run 018 journal — architect

Run: 018 · Date: 2026-05-06 · Phase 1 author

S1 — Finding

The Phase 0 foundation is structurally sound for intern Phase 1/2 work, with one critical gap and several moderate ones. The critical gap: existing API routes for deal editing (PATCH /api/deals/[slug]) and stage changes (POST /api/deals/[slug]/stage) do NOT use canUseKey() or recordMetric() — they check identity but skip the feature permission system entirely. This means any intern with a cookie can change deal stages or edit financials, bypassing the admin-tier gates defined in deals.change-stage and deals.edit-financials. The meetings classification dropdown writes directly to Supabase from the browser client (not through an API route), which violates the "all data writes go through API routes" intern rule in CLAUDE.md. There is no PATCH /api/meetings/[id]/classify route at all — Charlie has no API-gated path to classify meetings. Bear needs a "Days in Stage" badge but the deal_stage_history table has no query helper or API route to compute time-in-stage. The outreach_campaigns rename is clean — only one reference in circuit-breaker.ts, and no stale from('campaigns') calls exist anywhere in src/. No circular dependencies exist in the features directory. The handoff contract wires correctly through to the promote route with no type mismatches.

S2 — Blind spot

I could not verify the Supabase migration SQL directly (no migration files were read), so I am trusting that the columns referenced in TypeScript (spent_usd, total_budget_usd, daily_budget_usd on outreach_campaigns) actually exist in the database schema. I also did not verify whether the tasks table (referenced in auto-promote/route.ts line 98) has a matching migration or whether its schema matches the insert shape. The meetings/sync-to-deal/route.ts inserts into deals with a company_name field that does not appear in the DealRow type — this may be a legacy column or a schema drift issue I could not confirm without the migration.

S3 — Pattern

Like run #008 where the architect validated the deal pipeline schema, this run again revealed that the "last mile" between feature definitions and route-level enforcement is the weakest link. In run #008, the finding was that deal stage labels and pipeline views needed alignment. Here the same structural pattern repeats: the feature registry defines permissions correctly, but the API routes that existed before the registry was built were never retrofitted with canUseKey() gates. This is a consistent pattern across the codebase — new infrastructure gets built alongside old routes that predate it.

S6 — What changed about me

When validating a new permission system, I will now check every pre-existing API route that touches the same tables — not just the routes built alongside the permission system — because the gap between "permission defined" and "permission enforced" is where the real risk lives.

Generated from 018__architect.md — do not edit this HTML directly.