1919from .ast import (
2020 AgentBlock ,
2121 AssertStatement ,
22+ AttemptBlock ,
23+ ClaimBoundaryStatement ,
24+ EvidenceRequirementStatement ,
25+ FalsifyIfStatement ,
26+ HandoffContractStatement ,
2227 IfStatement ,
2328 LetStatement ,
29+ NonGoalStatement ,
30+ ObligationBlock ,
2431 OnBlock ,
2532 PolicyStatement ,
2633 PrintStatement ,
2734 Program ,
35+ PurposeStatement ,
2836 RecallStatement ,
2937 RememberStatement ,
3038 ReplyStatement ,
@@ -85,6 +93,7 @@ class ExecutionResult:
8593 handoffs : list [dict [str , Any ]] = field (default_factory = list )
8694 branches : list [dict [str , Any ]] = field (default_factory = list )
8795 trace : list [TraceEvent ] = field (default_factory = list )
96+ contract_metadata : dict [str , Any ] = field (default_factory = dict )
8897
8998 def trace_as_dicts (self ) -> list [dict [str , Any ]]:
9099 """Return the trace ledger in JSON-serializable form."""
@@ -105,6 +114,7 @@ def to_dict(self) -> dict[str, Any]:
105114 "tool_results" : list (self .tool_results ),
106115 "handoffs" : list (self .handoffs ),
107116 "branches" : list (self .branches ),
117+ "contract_metadata" : dict (self .contract_metadata ),
108118 "trace" : self .trace_as_dicts (),
109119 }
110120
@@ -210,10 +220,27 @@ def run(
210220 joined = "\n " .join (diagnostic .format () for diagnostic in diagnostics )
211221 raise IXRuntimeError (f"IX validation failed:\n { joined } " )
212222
213- result = ExecutionResult (status = "running" )
223+ result = ExecutionResult (
224+ status = "running" ,
225+ contract_metadata = self ._contract_metadata (program ),
226+ )
214227 values : dict [str , Any ] = dict (inputs or {})
215228 statements = self ._select_statements (program , agent = agent , event = event )
216229 self ._emit (result , "run.start" , "IX execution started" , program .span )
230+
231+ if result .contract_metadata ["counts" ]["attempts" ] > 0 :
232+ self ._emit (
233+ result ,
234+ "contract.metadata" ,
235+ "Cognition contract metadata extracted; no cognition executed" ,
236+ program .span ,
237+ counts = result .contract_metadata ["counts" ],
238+ attempts = [
239+ attempt ["name" ]
240+ for attempt in result .contract_metadata ["attempts" ]
241+ ],
242+ )
243+
217244 self ._run_statements (program , statements , values , result , depth = 0 , inherited_policies = ())
218245 result .variables = dict (values )
219246 result .status = "completed"
@@ -245,7 +272,7 @@ def _select_statements(
245272 if agent is None and event is None :
246273 selected : list [Statement ] = []
247274 for statement in program .statements :
248- if not isinstance (statement , AgentBlock ):
275+ if not isinstance (statement , AgentBlock | AttemptBlock ):
249276 selected .append (statement )
250277 return tuple (selected )
251278
@@ -267,6 +294,110 @@ def _select_agent_event(self, agent: AgentBlock, *, event: str) -> tuple[Stateme
267294 def _collect_policies (self , statements : tuple [Statement , ...]) -> tuple [PolicyStatement , ...]:
268295 return tuple (statement for statement in statements if isinstance (statement , PolicyStatement ))
269296
297+ def _contract_metadata (self , program : Program ) -> dict [str , Any ]:
298+ attempts = [
299+ self ._attempt_metadata (statement )
300+ for statement in program .statements
301+ if isinstance (statement , AttemptBlock )
302+ ]
303+ obligation_count = sum (len (attempt ["obligations" ]) for attempt in attempts )
304+ evidence_count = sum (
305+ len (obligation ["evidence_required" ])
306+ for attempt in attempts
307+ for obligation in attempt ["obligations" ]
308+ )
309+ falsification_count = sum (
310+ len (obligation ["falsify_if" ])
311+ for attempt in attempts
312+ for obligation in attempt ["obligations" ]
313+ )
314+
315+ return {
316+ "contract_type" : "ix.cognition.contracts" ,
317+ "schema_version" : "1.0" ,
318+ "runtime_semantics" : "metadata_only_not_executed" ,
319+ "counts" : {
320+ "attempts" : len (attempts ),
321+ "obligations" : obligation_count ,
322+ "evidence_requirements" : evidence_count ,
323+ "falsification_gates" : falsification_count ,
324+ },
325+ "attempts" : attempts ,
326+ }
327+
328+ def _attempt_metadata (self , attempt : AttemptBlock ) -> dict [str , Any ]:
329+ purposes : list [str ] = []
330+ non_goals : list [str ] = []
331+ claim_boundaries : list [str ] = []
332+ approvals : list [str ] = []
333+ handoff_contracts : list [dict [str , Any ]] = []
334+ obligations : list [dict [str , Any ]] = []
335+
336+ for statement in attempt .statements :
337+ if isinstance (statement , PurposeStatement ):
338+ purposes .append (self ._contract_text (statement .text ))
339+ elif isinstance (statement , NonGoalStatement ):
340+ non_goals .append (self ._contract_text (statement .text ))
341+ elif isinstance (statement , ClaimBoundaryStatement ):
342+ claim_boundaries .append (self ._contract_text (statement .text ))
343+ elif isinstance (statement , RequireApprovalStatement ):
344+ approvals .append (self ._contract_text (statement .reason ))
345+ elif isinstance (statement , HandoffContractStatement ):
346+ handoff_contracts .append (
347+ {
348+ "target" : statement .target ,
349+ "schema" : statement .schema_name ,
350+ "source" : self ._span_dict (statement .span ),
351+ }
352+ )
353+ elif isinstance (statement , ObligationBlock ):
354+ obligations .append (self ._obligation_metadata (statement ))
355+
356+ return {
357+ "name" : attempt .name ,
358+ "source" : self ._span_dict (attempt .span ),
359+ "purpose" : purposes ,
360+ "non_goals" : non_goals ,
361+ "claim_boundaries" : claim_boundaries ,
362+ "human_approval_required" : approvals ,
363+ "handoff_contracts" : handoff_contracts ,
364+ "obligations" : obligations ,
365+ }
366+
367+ def _obligation_metadata (self , obligation : ObligationBlock ) -> dict [str , Any ]:
368+ evidence_required : list [str ] = []
369+ falsify_if : list [str ] = []
370+
371+ for statement in obligation .statements :
372+ if isinstance (statement , EvidenceRequirementStatement ):
373+ evidence_required .append (statement .artifact )
374+ elif isinstance (statement , FalsifyIfStatement ):
375+ falsify_if .append (statement .condition )
376+
377+ return {
378+ "id" : obligation .identifier ,
379+ "source" : self ._span_dict (obligation .span ),
380+ "evidence_required" : evidence_required ,
381+ "falsify_if" : falsify_if ,
382+ }
383+
384+ def _contract_text (self , value : str ) -> str :
385+ stripped = value .strip ()
386+ try :
387+ parsed = py_ast .literal_eval (stripped )
388+ except (SyntaxError , ValueError ):
389+ parsed = stripped
390+ if isinstance (parsed , str ):
391+ return parsed
392+ return stripped
393+
394+ def _span_dict (self , span : SourceSpan ) -> dict [str , Any ]:
395+ return {
396+ "filename" : span .filename ,
397+ "line" : span .line ,
398+ "column" : span .column ,
399+ }
400+
270401 def _execute_statement (
271402 self ,
272403 statement : Statement ,
0 commit comments