Skip to content

OPS-T1 decision gate — R-WP21 (kh-ast-S9 → RATIFIED-S10)

OPS-T1 decision gate — R-WP21 (kh-ast-S9 → RATIFIED-S10)

Section titled “OPS-T1 decision gate — R-WP21 (kh-ast-S9 → RATIFIED-S10)”

Status: RATIFIED-S10 — Liam selected option (c) only at S10 Wave 0 close. Source: ROADMAP Wave 5 R-WP21 + type-safety-pipeline/PRODUCT.md D-19 + R-WP17 output. Inputs: docs/generated/type-drift-report.md (full report) and docs/generated/type-drift-baseline.json (37 fetcher-only interfaces). S10 binding output: investigations/S10-wave-0-synthesis.md §3.1.

Empirical picture (from R-WP17, baseline 187df260)

Section titled “Empirical picture (from R-WP17, baseline 187df260)”
  • 37 fetcher-only interfaces — declared in types/*.ts or lib/query/fetchers.ts, consumed by ≥1 fetcher generic, with no matching route handler return-type annotation.
  • 2 route-only, 0 enforced, 21 unused — total 60 candidate interfaces.
  • Subsystem density (by /api/<subsystem>/ prefix of the fetcher URL):
Subsystemfetcher-only count
intelligence20
admin8
review5
tags3
items2
coverage2
bids2
source-documents1
pipeline-runs1
notifications1
entities1
digest1
total47 call sites / 37 interfaces

The intelligence subsystem alone accounts for 54% of the drift surface (20/37). Review + admin contribute another 35% (13/37). The remaining 11% is scattered across 9 subsystems with 1–3 interfaces each.

Both canonical false-negative fixtures from PRODUCT.md D-18 surface as fetcher-only: TaxonomySyncStatus (declared in lib/query/fetchers.ts) and ReviewStatsResponse (cast manually at app/api/review/stats/route.ts:76).

(a) OPS-T1 defineRoute() wrapper rollout now

Section titled “(a) OPS-T1 defineRoute() wrapper rollout now”

Author a typed defineRoute(ResponseSchema, handler) wrapper that enforces structural symmetry between the schema and the handler return. Migrate all ~193 KH routes to consume it.

  • Effort: ~16–24h (2–3 days per the brief).
  • Coverage: Closes Gap 1 at the root for the entire codebase, including the 156 routes that are currently enforced=0 / route-only=2 / fetcher-only=37 / unused=21 (out of ~60 named-interface routes; the other ~133 routes use anonymous payloads or have no client-side type).
  • Risk: large diff surface; merge contention with main-track route work; requires retraining authors on the wrapper.
  • Leverage: the structural fix prevents future drift across all subsystems uniformly.

For each fetcher-only interface, add an explicit return-type annotation to the handler: export async function GET(): Promise<NextResponse<X>>. Or migrate the cast site (return NextResponse.json(payload)) to return NextResponse.json<X>(payload) where the SDK supports the generic.

  • Effort: ~30 min per interface × 37 ≈ 18.5h worst case. In practice ~4–6h for a single executor on the intelligence cluster (20 routes share the same hook pattern); another ~2–3h for admin + review; another ~2h for the long tail. Total realistic: 8–11h.
  • Coverage: closes the 37 known gaps. New routes are NOT prevented from regressing.
  • Risk: small per-route diffs; minimal merge contention.
  • Leverage: lower than OPS-T1 in the long run, but parallelisable and incremental.

(c) Accept gap + use R-WP17 detector as CI gate

Section titled “(c) Accept gap + use R-WP17 detector as CI gate”

Lock the current baseline (docs/generated/type-drift-baseline.json — already populated with the 37 rows at S9 close). PR CI runs bun run ast-dataflow type-drift-detect --ci. Build fails if any NEW fetcher-only interface is introduced without baseline update. The 37 existing gaps are tolerated as documented debt.

  • Effort: ~1h to add the CI step to .github/workflows/ci.yml (mirrors the schema-parity.yml pattern).
  • Coverage: prevents regression. Does NOT close the existing 37 gaps.
  • Risk: the baseline becomes a moving target if not actively shrunk.
  • Leverage: highest ROI per hour for the prevention axis; zero for the remediation axis.

Hybrid (b)+(c) over a 2-wave sequence, defer (a) until evidence demands it.

Wave A — incremental annotations on the intelligence cluster (proposed S10 work)

Section titled “Wave A — incremental annotations on the intelligence cluster (proposed S10 work)”
  • Scope: 20 fetcher-only interfaces under app/api/intelligence/**/route.ts.
  • Effort: ~4–6h single-executor (hook pattern is uniform across the cluster).
  • Outcome: drift surface drops from 37 → 17 (54% reduction).

Wave B — CI gate ratchet (proposed S10 same session OR S11)

Section titled “Wave B — CI gate ratchet (proposed S10 same session OR S11)”
  • Add type-drift-parity job to ci.yml. Style reference: .github/workflows/schema-parity.yml.
  • Baseline at session merge enforces 17 (or 37 if Wave A defers). PR adding a new fetcher-only interface fails CI unless the baseline is explicitly updated in the same PR.
  • Effort: ~1h.
  • Outcome: regression prevention; debt ledger.
  1. The 37-interface surface is not large enough to justify ~3 days of refactor risk. Per-route annotation cost is ~30 min × 37 ≈ 18.5h worst-case, comparable to OPS-T1 effort, but with far smaller diff blast radius and zero retraining cost.
  2. Main-track MCP cleanup is the higher-leverage parallel work. OPS-T1 would compete for main-track attention; deferring keeps the bandwidth available.
  3. The CI gate alone (option c) is the dominant lever for preventing future drift. Pairing it with per-route annotations on the worst cluster (intelligence) addresses both the existing debt and the regression axis without taking on a 3-day structural refactor.
  4. R-WP17 detector becomes a permanent invariant. Whether or not OPS-T1 lands, the detector is the source of truth — the structural fix is one way to enforce the invariant, the detector + CI is another. Either is acceptable; (a) is not uniquely necessary.

If, after Wave A + Wave B land, the drift surface grows beyond the post-Wave-A baseline by ≥10 interfaces (e.g. 17 → 27) within two sessions, re-open the OPS-T1 decision. The structural fix becomes load-bearing only once the per-route annotation cost compounds.

  • Liam ratifies the recommendation (hybrid b+c). — superseded
  • OR Liam selects (a) and the S10 work package shifts to defineRoute() rollout. — deferred
  • Liam selects (c) only — Wave-A (b) dropped; Wave-A’ (a) deferred to S11+ codemod-led rollout. — RATIFIED-S10

Decision rationale (S10): Wave 0-A confirmed 96.4% route survival (no OPS-T1 scope reduction available). Wave 0-C confirmed OPS-T1 itself is not mechanisable under the current ast-dataflow + gitnexus + ccc toolchain — needs a sibling scripts/codemods/wrap-define-route.ts ts-morph utility. Liam’s binding direction: instead of investing 4-6h in tactical Wave-A annotations that get partially overwritten by future OPS-T1, reserve S11 to author PRODUCT.md + TECH.md for the codemod utility so the eventual OPS-T1 rollout aligns with main-track Phase 1 canonical-pipeline timing.

S10 close-out tracker:

  • R-WP17 CI gate (option c): shipped S10 WP1 (c99f5953).
  • R-WP21 row: marked RATIFIED-(c)-S10 in ROADMAP.
  • OQ-R8 row: DECIDED-S10 (c-only).
  • OQ-R9: still deferred — S10-wave-0-synthesis.md §3.2.
  • S11 codemod authoring WP: see S10-wave-0-synthesis.md §4.1.