@@ -146,10 +146,14 @@ fn all_callable_methods(semantic_registry: &SemanticMethodRegistry) -> Vec<&str>
146146 methods
147147}
148148
149- /// Wire Standard L2 `capabilities.list` response.
149+ /// Wire Standard L3 `capabilities.list` response.
150150///
151- /// Returns `{primal, version, methods, provided_capabilities}` per
151+ /// Returns `{primal, version, methods, provided_capabilities, cost_estimates,
152+ /// operation_dependencies, consumed_capabilities}` per
152153/// `CAPABILITY_WIRE_STANDARD.md` v1.0.
154+ ///
155+ /// Cost model: energy/time/compute, not monetary. Dollar value is an
156+ /// end-user concern built on top of these primitives.
153157#[ expect(
154158 clippy:: unused_async,
155159 reason = "handler signature requires async for uniform dispatch"
@@ -223,11 +227,261 @@ pub(crate) async fn capabilities_list(
223227 "coordination.register" ,
224228 "coordination.discover"
225229 ] ,
230+ "cost_estimates" : cost_estimates( ) ,
231+ "operation_dependencies" : operation_dependencies( ) ,
226232 "protocol" : "jsonrpc-2.0" ,
227233 "transport" : [ "uds" , "tcp" ]
228234 } ) )
229235}
230236
237+ /// Wire Standard L3: per-method compute cost estimates.
238+ ///
239+ /// Models cost as energy, time, and compute intensity — not monetary.
240+ /// Dollar value is an end-user concern built on these primitives.
241+ ///
242+ /// Fields per method:
243+ /// - `cpu`: negligible | low | medium | high | variable
244+ /// - `gpu_eligible`: whether this method can trigger GPU work
245+ /// - `latency_ms`: expected p50 latency in milliseconds
246+ /// - `energy`: negligible | low | medium | high | variable
247+ /// - `memory_pressure`: none | low | medium | high | variable
248+ fn cost_estimates ( ) -> serde_json:: Value {
249+ let mut map = serde_json:: Map :: with_capacity ( 60 ) ;
250+
251+ let cost = |cpu : & str , gpu : bool , latency : u32 , energy : & str , mem : & str | -> serde_json:: Value {
252+ serde_json:: json!( {
253+ "cpu" : cpu, "gpu_eligible" : gpu, "latency_ms" : latency,
254+ "energy" : energy, "memory_pressure" : mem
255+ } )
256+ } ;
257+
258+ // Meta / discovery — pure in-memory, no I/O
259+ for m in [
260+ "health.liveness" ,
261+ "health.check" ,
262+ "health.readiness" ,
263+ "capabilities.list" ,
264+ "identity.get" ,
265+ "toadstool.version" ,
266+ "provenance.query" ,
267+ ] {
268+ map. insert ( m. into ( ) , cost ( "negligible" , false , 0 , "negligible" , "none" ) ) ;
269+ }
270+
271+ // Workload lifecycle
272+ map. insert (
273+ "toadstool.submit_workload" . into ( ) ,
274+ cost ( "variable" , true , 50 , "variable" , "variable" ) ,
275+ ) ;
276+ map. insert (
277+ "toadstool.query_status" . into ( ) ,
278+ cost ( "negligible" , false , 1 , "negligible" , "none" ) ,
279+ ) ;
280+ map. insert (
281+ "toadstool.cancel_workload" . into ( ) ,
282+ cost ( "low" , false , 5 , "low" , "none" ) ,
283+ ) ;
284+ map. insert (
285+ "toadstool.list_workloads" . into ( ) ,
286+ cost ( "low" , false , 2 , "low" , "low" ) ,
287+ ) ;
288+ map. insert (
289+ "toadstool.query_capabilities" . into ( ) ,
290+ cost ( "low" , false , 5 , "low" , "low" ) ,
291+ ) ;
292+
293+ // Resource estimation
294+ map. insert (
295+ "resources.estimate" . into ( ) ,
296+ cost ( "medium" , false , 10 , "low" , "low" ) ,
297+ ) ;
298+ map. insert (
299+ "resources.validate_availability" . into ( ) ,
300+ cost ( "medium" , false , 10 , "low" , "low" ) ,
301+ ) ;
302+ map. insert (
303+ "resources.suggest_optimizations" . into ( ) ,
304+ cost ( "medium" , false , 20 , "low" , "low" ) ,
305+ ) ;
306+
307+ // GPU job queue
308+ map. insert (
309+ "compute.submit" . into ( ) ,
310+ cost ( "variable" , true , 50 , "variable" , "variable" ) ,
311+ ) ;
312+ map. insert (
313+ "compute.status" . into ( ) ,
314+ cost ( "negligible" , false , 1 , "negligible" , "none" ) ,
315+ ) ;
316+ map. insert (
317+ "compute.result" . into ( ) ,
318+ cost ( "low" , false , 5 , "low" , "medium" ) ,
319+ ) ;
320+ map. insert (
321+ "compute.cancel" . into ( ) ,
322+ cost ( "low" , false , 2 , "low" , "none" ) ,
323+ ) ;
324+ map. insert ( "compute.list" . into ( ) , cost ( "low" , false , 2 , "low" , "low" ) ) ;
325+
326+ // Shader dispatch — high GPU, high energy
327+ map. insert (
328+ "shader.dispatch" . into ( ) ,
329+ cost ( "high" , true , 100 , "high" , "high" ) ,
330+ ) ;
331+ map. insert (
332+ "compute.dispatch.submit" . into ( ) ,
333+ cost ( "high" , true , 100 , "high" , "high" ) ,
334+ ) ;
335+ map. insert (
336+ "compute.dispatch.status" . into ( ) ,
337+ cost ( "negligible" , false , 1 , "negligible" , "none" ) ,
338+ ) ;
339+ map. insert (
340+ "compute.dispatch.result" . into ( ) ,
341+ cost ( "low" , false , 10 , "low" , "medium" ) ,
342+ ) ;
343+ map. insert (
344+ "compute.dispatch.forward" . into ( ) ,
345+ cost ( "medium" , false , 50 , "medium" , "low" ) ,
346+ ) ;
347+
348+ // Hardware learning — BAR0/MMIO reads
349+ map. insert (
350+ "compute.hardware.observe" . into ( ) ,
351+ cost ( "medium" , false , 20 , "medium" , "low" ) ,
352+ ) ;
353+ map. insert (
354+ "compute.hardware.distill" . into ( ) ,
355+ cost ( "high" , false , 100 , "medium" , "medium" ) ,
356+ ) ;
357+ map. insert (
358+ "compute.hardware.apply" . into ( ) ,
359+ cost ( "medium" , false , 50 , "medium" , "low" ) ,
360+ ) ;
361+ map. insert (
362+ "compute.hardware.share_recipe" . into ( ) ,
363+ cost ( "low" , false , 10 , "low" , "low" ) ,
364+ ) ;
365+ map. insert (
366+ "compute.hardware.auto_init" . into ( ) ,
367+ cost ( "high" , false , 200 , "high" , "medium" ) ,
368+ ) ;
369+ map. insert (
370+ "compute.hardware.auto_init_all" . into ( ) ,
371+ cost ( "high" , false , 500 , "high" , "medium" ) ,
372+ ) ;
373+ map. insert (
374+ "compute.hardware.status" . into ( ) ,
375+ cost ( "low" , false , 5 , "low" , "none" ) ,
376+ ) ;
377+ map. insert (
378+ "compute.hardware.vfio_devices" . into ( ) ,
379+ cost ( "low" , false , 10 , "low" , "none" ) ,
380+ ) ;
381+
382+ // Performance surface
383+ map. insert (
384+ "compute.performance_surface.report" . into ( ) ,
385+ cost ( "medium" , false , 20 , "low" , "medium" ) ,
386+ ) ;
387+ map. insert (
388+ "compute.performance_surface.query" . into ( ) ,
389+ cost ( "medium" , false , 10 , "low" , "low" ) ,
390+ ) ;
391+ map. insert (
392+ "compute.performance_surface.list" . into ( ) ,
393+ cost ( "low" , false , 2 , "low" , "low" ) ,
394+ ) ;
395+ map. insert (
396+ "compute.route.multi_unit" . into ( ) ,
397+ cost ( "medium" , false , 15 , "low" , "low" ) ,
398+ ) ;
399+
400+ // GPU telemetry — sysfs/BAR0 reads
401+ map. insert (
402+ "gpu.query_info" . into ( ) ,
403+ cost ( "low" , false , 10 , "low" , "low" ) ,
404+ ) ;
405+ map. insert (
406+ "gpu.query_memory" . into ( ) ,
407+ cost ( "low" , false , 5 , "low" , "none" ) ,
408+ ) ;
409+ map. insert (
410+ "gpu.query_telemetry" . into ( ) ,
411+ cost ( "low" , false , 10 , "low" , "none" ) ,
412+ ) ;
413+
414+ // Gate routing — network I/O
415+ map. insert ( "gate.update" . into ( ) , cost ( "low" , false , 5 , "low" , "none" ) ) ;
416+ map. insert ( "gate.remove" . into ( ) , cost ( "low" , false , 2 , "low" , "none" ) ) ;
417+ map. insert ( "gate.list" . into ( ) , cost ( "low" , false , 1 , "low" , "low" ) ) ;
418+ map. insert (
419+ "gate.route" . into ( ) ,
420+ cost ( "medium" , false , 30 , "medium" , "low" ) ,
421+ ) ;
422+
423+ // Transport — DMA/device I/O
424+ map. insert (
425+ "transport.discover" . into ( ) ,
426+ cost ( "medium" , false , 50 , "medium" , "low" ) ,
427+ ) ;
428+ map. insert (
429+ "transport.list" . into ( ) ,
430+ cost ( "low" , false , 2 , "low" , "none" ) ,
431+ ) ;
432+ map. insert (
433+ "transport.route" . into ( ) ,
434+ cost ( "medium" , false , 20 , "medium" , "low" ) ,
435+ ) ;
436+ map. insert (
437+ "transport.open" . into ( ) ,
438+ cost ( "medium" , false , 50 , "medium" , "medium" ) ,
439+ ) ;
440+ map. insert (
441+ "transport.stream" . into ( ) ,
442+ cost ( "high" , false , 100 , "high" , "high" ) ,
443+ ) ;
444+ map. insert (
445+ "transport.status" . into ( ) ,
446+ cost ( "low" , false , 2 , "low" , "none" ) ,
447+ ) ;
448+
449+ // Ember — device lifecycle
450+ map. insert ( "ember.list" . into ( ) , cost ( "low" , false , 5 , "low" , "none" ) ) ;
451+ map. insert ( "ember.status" . into ( ) , cost ( "low" , false , 5 , "low" , "none" ) ) ;
452+
453+ serde_json:: Value :: Object ( map)
454+ }
455+
456+ /// Wire Standard L3: method prerequisite DAG.
457+ ///
458+ /// Maps methods to their prerequisites — what must be called (or
459+ /// have completed) before a given method is meaningful.
460+ fn operation_dependencies ( ) -> serde_json:: Value {
461+ serde_json:: json!( {
462+ "compute.status" : [ "compute.submit" ] ,
463+ "compute.result" : [ "compute.submit" ] ,
464+ "compute.cancel" : [ "compute.submit" ] ,
465+ "toadstool.query_status" : [ "toadstool.submit_workload" ] ,
466+ "toadstool.cancel_workload" : [ "toadstool.submit_workload" ] ,
467+ "compute.dispatch.status" : [ "compute.dispatch.submit" ] ,
468+ "compute.dispatch.result" : [ "compute.dispatch.submit" ] ,
469+ "compute.hardware.distill" : [ "compute.hardware.observe" ] ,
470+ "compute.hardware.apply" : [ "compute.hardware.distill" ] ,
471+ "compute.hardware.share_recipe" : [ "compute.hardware.distill" ] ,
472+ "compute.hardware.auto_init" : [ "compute.hardware.observe" ] ,
473+ "compute.hardware.auto_init_all" : [ "compute.hardware.observe" ] ,
474+ "compute.performance_surface.query" : [ "compute.performance_surface.report" ] ,
475+ "compute.route.multi_unit" : [ "compute.performance_surface.report" ] ,
476+ "gate.route" : [ "gate.update" ] ,
477+ "gate.remove" : [ "gate.update" ] ,
478+ "transport.route" : [ "transport.discover" ] ,
479+ "transport.open" : [ "transport.discover" ] ,
480+ "transport.stream" : [ "transport.open" ] ,
481+ "transport.status" : [ "transport.open" ]
482+ } )
483+ }
484+
231485/// Returns discovered capabilities including semantic methods.
232486///
233487/// Legacy `compute.discover_capabilities` — returns node capabilities
@@ -440,6 +694,34 @@ mod tests {
440694
441695 assert ! ( cap[ "consumed_capabilities" ] . as_array( ) . is_some( ) ) ;
442696 assert_eq ! ( cap[ "protocol" ] , "jsonrpc-2.0" ) ;
697+
698+ // Wire Standard L3: cost_estimates
699+ let costs = cap[ "cost_estimates" ] . as_object ( ) . expect ( "cost_estimates" ) ;
700+ assert ! (
701+ costs. len( ) >= 40 ,
702+ "should have cost estimates for most methods"
703+ ) ;
704+ let health_cost = & costs[ "health.liveness" ] ;
705+ assert_eq ! ( health_cost[ "cpu" ] , "negligible" ) ;
706+ assert_eq ! ( health_cost[ "energy" ] , "negligible" ) ;
707+ assert_eq ! ( health_cost[ "gpu_eligible" ] , false ) ;
708+ let dispatch_cost = & costs[ "shader.dispatch" ] ;
709+ assert_eq ! ( dispatch_cost[ "cpu" ] , "high" ) ;
710+ assert_eq ! ( dispatch_cost[ "energy" ] , "high" ) ;
711+ assert_eq ! ( dispatch_cost[ "gpu_eligible" ] , true ) ;
712+ let submit_cost = & costs[ "compute.submit" ] ;
713+ assert_eq ! ( submit_cost[ "cpu" ] , "variable" ) ;
714+ assert_eq ! ( submit_cost[ "energy" ] , "variable" ) ;
715+
716+ // Wire Standard L3: operation_dependencies
717+ let deps = cap[ "operation_dependencies" ]
718+ . as_object ( )
719+ . expect ( "operation_dependencies" ) ;
720+ assert ! ( deps. len( ) >= 15 , "should have dependency entries" ) ;
721+ let status_deps = deps[ "compute.status" ] . as_array ( ) . expect ( "array" ) ;
722+ assert ! ( status_deps. iter( ) . any( |d| d == "compute.submit" ) ) ;
723+ let stream_deps = deps[ "transport.stream" ] . as_array ( ) . expect ( "array" ) ;
724+ assert ! ( stream_deps. iter( ) . any( |d| d == "transport.open" ) ) ;
443725 }
444726
445727 #[ tokio:: test]
0 commit comments