Skip to content

Commit b30a3a2

Browse files
committed
D2.3 /v1/shader/token-agreement handler wiring (Phase 2 surface complete)
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: <CodecParamsError display>" 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<WireTokenAgreement>), serde_json::to at egress (Json<WireTokenAgreementResult>); in between: CodecParams + ReferenceModel + Harness are all in-memory Rust objects, zero re-serialisation https://claude.ai/code/session_01SbYsmmbPf9YQuYbHZN52Zh
1 parent 3ee739a commit b30a3a2

2 files changed

Lines changed: 67 additions & 2 deletions

File tree

.claude/board/STATUS_BOARD.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ afterwards is a JIT kernel, not a rebuild. Plan path:
7272
|---|---|---|---|
7373
| 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. |
7474
| D2.2 | Decode-and-compare loop (top-k, per-layer MSE) | **Queued** | target ~220 LOC |
75-
| D2.3 | Handler wiring for `/v1/shader/token-agreement` | **Queued** | target ~60 LOC |
75+
| 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. |
7676

7777
### Phase 3 — Sweep driver + Lance logger — Queued
7878

crates/cognitive-shader-driver/src/serve.rs

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,16 @@ use serde_json::{json, Value};
4545
use crate::codec_research;
4646
use crate::driver::ShaderDriver;
4747
use crate::engine_bridge::{self, unified_style, UNIFIED_STYLES};
48+
use crate::token_agreement::{ReferenceModel, TokenAgreementHarness};
4849
use crate::wire::{
4950
WireCalibrateRequest, WireCalibrateResponse, WireCrystal, WireDispatch, WireHealth,
5051
WireIngest, WirePlanRequest, WirePlanResponse, WireProbeRequest, WireProbeResponse,
5152
WireQualia, WireRunbookRequest, WireRunbookResponse, WireRunbookStep,
5253
WireRunbookStepResult, WireStepResult, WireStyleInfo, WireTensorsRequest,
53-
WireTensorsResponse, WireUnifiedStep,
54+
WireTensorsResponse, WireTokenAgreement, WireTokenAgreementResult, WireUnifiedStep,
5455
};
56+
use lance_graph_contract::cam::CodecParams;
57+
use std::path::Path as StdPath;
5558
use lance_graph_contract::cognitive_shader::CognitiveShaderDriver;
5659

5760
struct ServerState {
@@ -87,6 +90,12 @@ pub fn router(driver: ShaderDriver) -> Router {
8790
.route("/v1/shader/tensors", post(tensors_handler))
8891
.route("/v1/shader/calibrate", post(calibrate_handler))
8992
.route("/v1/shader/probe", post(probe_handler))
93+
// D2.3 — I11 cert gate endpoint. Handler routes to
94+
// TokenAgreementHarness::measure_stub() until D2.2 lands the real
95+
// decode-and-compare loop. Stub result carries `stub:true` +
96+
// `backend:"stub"` so clients cannot confuse Phase 0 stub output
97+
// for a real measurement (anti-#219 defense, type-level).
98+
.route("/v1/shader/token-agreement", post(token_agreement_handler))
9099
// Scheduled runbook: one POST runs a list of steps. Test injection
91100
// lands here — a client script submits its full codec-research
92101
// protocol as a single DTO, the server executes and returns all
@@ -219,6 +228,62 @@ async fn probe_handler(
219228
.map_err(|e| (StatusCode::BAD_REQUEST, Json(json!({"error": e}))))
220229
}
221230

231+
/// D2.3 — `POST /v1/shader/token-agreement` handler.
232+
///
233+
/// Routes `WireTokenAgreement` through the Phase-0-honest stub path:
234+
///
235+
/// 1. Validates `candidate: WireCodecParams → CodecParams` via TryFrom,
236+
/// surfacing typed errors (precision-ladder, overfit guard) as HTTP 400.
237+
/// 2. Loads reference model via `ReferenceModel::load` when `model_path`
238+
/// points to a real directory; otherwise falls back to
239+
/// `ReferenceModel::stub` so tests can drive the handler without a
240+
/// filesystem.
241+
/// 3. Builds `TokenAgreementHarness` + calls `measure_stub()` (D2.1 stub).
242+
/// 4. Returns `WireTokenAgreementResult { stub:true, backend:"stub", … }`.
243+
///
244+
/// Real decode-and-compare lands at D2.2; the Wire surface + routing are
245+
/// frozen now so client integration work can proceed against the stub.
246+
async fn token_agreement_handler(
247+
Json(req): Json<WireTokenAgreement>,
248+
) -> Result<Json<WireTokenAgreementResult>, (StatusCode, Json<Value>)> {
249+
// Validate CodecParams at ingress (precision-ladder / overfit guard
250+
// fire here, not inside the harness).
251+
let _params: CodecParams = req
252+
.candidate
253+
.clone()
254+
.try_into()
255+
.map_err(|e: lance_graph_contract::cam::CodecParamsError| {
256+
(StatusCode::BAD_REQUEST, Json(json!({"error": format!("invalid CodecParams: {e}")})))
257+
})?;
258+
259+
// Reference model — real path if it exists, stub otherwise. D2.2
260+
// replaces with a strict path check once the safetensors loader lands.
261+
let model_path = StdPath::new(&req.model_path);
262+
let reference = if model_path.exists() {
263+
ReferenceModel::load(model_path).map_err(|e| {
264+
(StatusCode::BAD_REQUEST, Json(json!({"error": format!("model load: {e}")})))
265+
})?
266+
} else {
267+
// Deterministic stub keyed on the path string so repeated calls
268+
// return the same stub fingerprint (useful for cache/regression
269+
// tests that POST synthetic model_path values).
270+
let mut h = std::collections::hash_map::DefaultHasher::new();
271+
std::hash::Hash::hash(&req.model_path, &mut h);
272+
ReferenceModel::stub(std::hash::Hasher::finish(&h), 0)
273+
};
274+
275+
let harness = TokenAgreementHarness::new(
276+
reference,
277+
req.reference,
278+
req.candidate,
279+
req.n_tokens,
280+
);
281+
harness
282+
.measure_stub()
283+
.map(Json)
284+
.map_err(|e| (StatusCode::BAD_REQUEST, Json(json!({"error": format!("{e}")}))))
285+
}
286+
222287
async fn route_handler(
223288
State(_state): State<AppState>,
224289
Json(wire): Json<WireUnifiedStep>,

0 commit comments

Comments
 (0)