From b30a3a2cd3b0c65ce0e4c894f0b9d339c8f86d65 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 21 Apr 2026 00:09:03 +0000 Subject: [PATCH] D2.3 /v1/shader/token-agreement handler wiring (Phase 2 surface complete) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Final Phase 2 surface deliverable — routes WireTokenAgreement requests through the D2.1 TokenAgreementHarness stub. End-to-end flow: WireTokenAgreement (JSON at ingress) ↓ serde_json deserialize (Rule F: edge only) Handler validates candidate: WireCodecParams → CodecParams ↓ precision-ladder + overfit guard fire here (ingress) NOT deeper ReferenceModel::load(&req.model_path) when path exists OR ReferenceModel::stub(hash(model_path), 0) fallback ↓ deterministic stub keyed on model_path string for regression tests TokenAgreementHarness::new(ref, baseline, candidate, n_tokens) ↓ .measure_stub() → WireTokenAgreementResult { stub:true, backend:"stub" } ↓ serde_json serialize (Rule F: edge only) HTTP 200 Json response crates/cognitive-shader-driver/src/serve.rs — ~70 LOC: - token_agreement_handler async fn - new imports: ReferenceModel, TokenAgreementHarness, WireTokenAgreement, WireTokenAgreementResult, CodecParams, StdPath (aliased to avoid collision with axum::extract::Path) - new route: POST /v1/shader/token-agreement Errors typed at handler boundary: - BAD_REQUEST + "invalid CodecParams: " for precision-ladder / overfit guard failures - BAD_REQUEST + "model load: ModelPathMissing { path }" when real path is specified but does not exist - BAD_REQUEST + stringified TokenAgreementError for harness errors (EmptyPromptSet when n_tokens = 0) Stub-fallback behavior (when model_path does not exist on fs): Deterministic hash of the path string keys the ReferenceModel stub. Same model_path → same stub fingerprint → test harnesses get repeatable results without needing a real safetensors file. D2.2 replaces with strict path validation once the real loader lands. Why the `stub:true` wall matters end-to-end: Client sends WireTokenAgreement over HTTP → handler returns 200 OK with WireTokenAgreementResult. Without the `stub` flag, a client pipeline could silently treat 0.0 rates as real measurements. With the flag, any `assert!(!result.stub)` fails the pipeline loudly — the Phase 0/D2.1 anti-#219 discipline extends through the HTTP surface. Phase state: Phase 0 ✅ complete Phase 1 scaffold ✅ (D1.1 / D1.2 / D1.3 shipped; D1.1b queued) Phase 2 scaffold ✅ — D2.1 harness + D2.3 handler (this PR) ⏳ D2.2 real decode-and-compare loop queued Tests: 117/117 cognitive-shader-driver --features serve pass (unchanged; handler's logic is a thin pass-through and the harness it delegates to is already covered by 13 D2.1 tests). Board hygiene: STATUS_BOARD.md D2.3 Queued → In PR Rules honored: Rule F — serde_json::from at ingress (Json), serde_json::to at egress (Json); in between: CodecParams + ReferenceModel + Harness are all in-memory Rust objects, zero re-serialisation https://claude.ai/code/session_01SbYsmmbPf9YQuYbHZN52Zh --- .claude/board/STATUS_BOARD.md | 2 +- crates/cognitive-shader-driver/src/serve.rs | 67 ++++++++++++++++++++- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/.claude/board/STATUS_BOARD.md b/.claude/board/STATUS_BOARD.md index c2d6ab0a..17607089 100644 --- a/.claude/board/STATUS_BOARD.md +++ b/.claude/board/STATUS_BOARD.md @@ -72,7 +72,7 @@ afterwards is a JIT kernel, not a rebuild. Plan path: |---|---|---|---| | D2.1 | Token-agreement harness scaffold (reference model stub + top-k comparator + stub result) | **In PR** | branch — `ReferenceModel::{load, stub}` + `TokenAgreementError` + `TopKAgreement::{compare, top1_rate, top5_rate, meets_cert_gate, aggregate}` + `TokenAgreementHarness::{measure_stub, measure_full}` + 13 tests. Real safetensors load + decode loop defer to D2.2. | | D2.2 | Decode-and-compare loop (top-k, per-layer MSE) | **Queued** | target ~220 LOC | -| D2.3 | Handler wiring for `/v1/shader/token-agreement` | **Queued** | target ~60 LOC | +| D2.3 | Handler wiring for `/v1/shader/token-agreement` | **In PR** | branch — `token_agreement_handler` routes `WireTokenAgreement` → TryFrom(CodecParams) at ingress (precision-ladder + overfit guard fire here) → `ReferenceModel::load` or stub fallback on nonexistent paths → `TokenAgreementHarness::measure_stub()` → `WireTokenAgreementResult { stub:true }`. Route added: `POST /v1/shader/token-agreement`. Phase 0 Wire + Phase 2 harness now round-trip end-to-end. | ### Phase 3 — Sweep driver + Lance logger — Queued diff --git a/crates/cognitive-shader-driver/src/serve.rs b/crates/cognitive-shader-driver/src/serve.rs index 10969bd0..9956c9d7 100644 --- a/crates/cognitive-shader-driver/src/serve.rs +++ b/crates/cognitive-shader-driver/src/serve.rs @@ -45,13 +45,16 @@ use serde_json::{json, Value}; use crate::codec_research; use crate::driver::ShaderDriver; use crate::engine_bridge::{self, unified_style, UNIFIED_STYLES}; +use crate::token_agreement::{ReferenceModel, TokenAgreementHarness}; use crate::wire::{ WireCalibrateRequest, WireCalibrateResponse, WireCrystal, WireDispatch, WireHealth, WireIngest, WirePlanRequest, WirePlanResponse, WireProbeRequest, WireProbeResponse, WireQualia, WireRunbookRequest, WireRunbookResponse, WireRunbookStep, WireRunbookStepResult, WireStepResult, WireStyleInfo, WireTensorsRequest, - WireTensorsResponse, WireUnifiedStep, + WireTensorsResponse, WireTokenAgreement, WireTokenAgreementResult, WireUnifiedStep, }; +use lance_graph_contract::cam::CodecParams; +use std::path::Path as StdPath; use lance_graph_contract::cognitive_shader::CognitiveShaderDriver; struct ServerState { @@ -87,6 +90,12 @@ pub fn router(driver: ShaderDriver) -> Router { .route("/v1/shader/tensors", post(tensors_handler)) .route("/v1/shader/calibrate", post(calibrate_handler)) .route("/v1/shader/probe", post(probe_handler)) + // D2.3 — I11 cert gate endpoint. Handler routes to + // TokenAgreementHarness::measure_stub() until D2.2 lands the real + // decode-and-compare loop. Stub result carries `stub:true` + + // `backend:"stub"` so clients cannot confuse Phase 0 stub output + // for a real measurement (anti-#219 defense, type-level). + .route("/v1/shader/token-agreement", post(token_agreement_handler)) // Scheduled runbook: one POST runs a list of steps. Test injection // lands here — a client script submits its full codec-research // protocol as a single DTO, the server executes and returns all @@ -219,6 +228,62 @@ async fn probe_handler( .map_err(|e| (StatusCode::BAD_REQUEST, Json(json!({"error": e})))) } +/// D2.3 — `POST /v1/shader/token-agreement` handler. +/// +/// Routes `WireTokenAgreement` through the Phase-0-honest stub path: +/// +/// 1. Validates `candidate: WireCodecParams → CodecParams` via TryFrom, +/// surfacing typed errors (precision-ladder, overfit guard) as HTTP 400. +/// 2. Loads reference model via `ReferenceModel::load` when `model_path` +/// points to a real directory; otherwise falls back to +/// `ReferenceModel::stub` so tests can drive the handler without a +/// filesystem. +/// 3. Builds `TokenAgreementHarness` + calls `measure_stub()` (D2.1 stub). +/// 4. Returns `WireTokenAgreementResult { stub:true, backend:"stub", … }`. +/// +/// Real decode-and-compare lands at D2.2; the Wire surface + routing are +/// frozen now so client integration work can proceed against the stub. +async fn token_agreement_handler( + Json(req): Json, +) -> Result, (StatusCode, Json)> { + // Validate CodecParams at ingress (precision-ladder / overfit guard + // fire here, not inside the harness). + let _params: CodecParams = req + .candidate + .clone() + .try_into() + .map_err(|e: lance_graph_contract::cam::CodecParamsError| { + (StatusCode::BAD_REQUEST, Json(json!({"error": format!("invalid CodecParams: {e}")}))) + })?; + + // Reference model — real path if it exists, stub otherwise. D2.2 + // replaces with a strict path check once the safetensors loader lands. + let model_path = StdPath::new(&req.model_path); + let reference = if model_path.exists() { + ReferenceModel::load(model_path).map_err(|e| { + (StatusCode::BAD_REQUEST, Json(json!({"error": format!("model load: {e}")}))) + })? + } else { + // Deterministic stub keyed on the path string so repeated calls + // return the same stub fingerprint (useful for cache/regression + // tests that POST synthetic model_path values). + let mut h = std::collections::hash_map::DefaultHasher::new(); + std::hash::Hash::hash(&req.model_path, &mut h); + ReferenceModel::stub(std::hash::Hasher::finish(&h), 0) + }; + + let harness = TokenAgreementHarness::new( + reference, + req.reference, + req.candidate, + req.n_tokens, + ); + harness + .measure_stub() + .map(Json) + .map_err(|e| (StatusCode::BAD_REQUEST, Json(json!({"error": format!("{e}")})))) +} + async fn route_handler( State(_state): State, Json(wire): Json,