@@ -45,13 +45,16 @@ use serde_json::{json, Value};
4545use crate :: codec_research;
4646use crate :: driver:: ShaderDriver ;
4747use crate :: engine_bridge:: { self , unified_style, UNIFIED_STYLES } ;
48+ use crate :: token_agreement:: { ReferenceModel , TokenAgreementHarness } ;
4849use 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 ;
5558use lance_graph_contract:: cognitive_shader:: CognitiveShaderDriver ;
5659
5760struct 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+
222287async fn route_handler (
223288 State ( _state) : State < AppState > ,
224289 Json ( wire) : Json < WireUnifiedStep > ,
0 commit comments