agent-apprenticeship 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +261 -1
  2. package/bin/agent-apprenticeship.js +1 -2
  3. package/package.json +4 -4
  4. package/pyproject.toml +10 -4
  5. package/schemas/environment_replay.schema.json +41 -0
  6. package/schemas/eval_package.schema.json +37 -0
  7. package/schemas/experience_compilation.schema.json +53 -0
  8. package/schemas/runtime_training.schema.json +33 -0
  9. package/schemas/source_evidence_map.schema.json +25 -0
  10. package/schemas/trace.schema.json +904 -0
  11. package/schemas/trace_step.schema.json +622 -0
  12. package/schemas/training_rows.schema.json +29 -0
  13. package/src/{agent_apprenticeship_trace → agent_apprenticeship}/__init__.py +1 -1
  14. package/src/agent_apprenticeship/agent_env_exporter.py +53 -0
  15. package/src/agent_apprenticeship/agent_env_verify.py +32 -0
  16. package/src/agent_apprenticeship/atif.py +152 -0
  17. package/src/agent_apprenticeship/atif_models/LICENSE +201 -0
  18. package/src/agent_apprenticeship/atif_models/__init__.py +38 -0
  19. package/src/agent_apprenticeship/atif_models/agent.py +35 -0
  20. package/src/agent_apprenticeship/atif_models/content.py +159 -0
  21. package/src/agent_apprenticeship/atif_models/final_metrics.py +43 -0
  22. package/src/agent_apprenticeship/atif_models/metrics.py +47 -0
  23. package/src/agent_apprenticeship/atif_models/observation.py +19 -0
  24. package/src/agent_apprenticeship/atif_models/observation_result.py +45 -0
  25. package/src/agent_apprenticeship/atif_models/step.py +142 -0
  26. package/src/agent_apprenticeship/atif_models/subagent_trajectory_ref.py +88 -0
  27. package/src/agent_apprenticeship/atif_models/tool_call.py +34 -0
  28. package/src/agent_apprenticeship/atif_models/trajectory.py +196 -0
  29. package/src/{agent_apprenticeship_trace → agent_apprenticeship}/batch_runner.py +1 -1
  30. package/src/{agent_apprenticeship_trace → agent_apprenticeship}/bundle_exporter.py +8 -1
  31. package/src/{agent_apprenticeship_trace → agent_apprenticeship}/certification.py +1 -1
  32. package/src/{agent_apprenticeship_trace → agent_apprenticeship}/cli.py +26 -7
  33. package/src/{agent_apprenticeship_trace → agent_apprenticeship}/codex_runner.py +17 -17
  34. package/src/{agent_apprenticeship_trace → agent_apprenticeship}/config.py +1 -1
  35. package/src/{agent_apprenticeship_trace → agent_apprenticeship}/ecosystem_r2.py +3 -1
  36. package/src/{agent_apprenticeship_trace → agent_apprenticeship}/evaluator.py +8 -27
  37. package/src/{agent_apprenticeship_trace → agent_apprenticeship}/grader.py +6 -5
  38. package/src/{agent_apprenticeship_trace → agent_apprenticeship}/integration_status.py +1 -1
  39. package/src/agent_apprenticeship/io.py +104 -0
  40. package/src/{agent_apprenticeship_trace → agent_apprenticeship}/learning.py +7 -1
  41. package/src/{agent_apprenticeship_trace → agent_apprenticeship}/loop.py +5 -5
  42. package/src/{agent_apprenticeship_trace → agent_apprenticeship}/loop_review.py +1 -1
  43. package/src/{agent_apprenticeship_trace → agent_apprenticeship}/mentor_checkpoints.py +1 -1
  44. package/src/{agent_apprenticeship_trace → agent_apprenticeship}/package_exporter.py +5 -3
  45. package/src/{agent_apprenticeship_trace → agent_apprenticeship}/progress.py +1 -1
  46. package/src/{agent_apprenticeship_trace → agent_apprenticeship}/public_run.py +1 -1
  47. package/src/{agent_apprenticeship_trace → agent_apprenticeship}/public_sanitizer.py +2 -2
  48. package/src/{agent_apprenticeship_trace → agent_apprenticeship}/release_exporter.py +7 -4
  49. package/src/{agent_apprenticeship_trace → agent_apprenticeship}/rubric_generation.py +1 -1
  50. package/src/{agent_apprenticeship_trace → agent_apprenticeship}/schemas.py +17 -4
  51. package/src/{agent_apprenticeship_trace → agent_apprenticeship}/session_events.py +4 -4
  52. package/src/{agent_apprenticeship_trace → agent_apprenticeship}/task_intake.py +1 -1
  53. package/src/{agent_apprenticeship_trace → agent_apprenticeship}/tdo.py +5 -1
  54. package/src/agent_apprenticeship/trace_normalizer.py +103 -0
  55. package/src/agent_apprenticeship/trace_prompt.py +48 -0
  56. package/src/{agent_apprenticeship_trace → agent_apprenticeship}/training_signals.py +1 -1
  57. package/src/{agent_apprenticeship_trace → agent_apprenticeship}/validation.py +3 -3
  58. package/src/{agent_apprenticeship_trace → agent_apprenticeship}/verifier.py +6 -5
  59. package/docs/bioactive_evidence_mapping_schema.md +0 -113
  60. package/docs/dataset_schema.md +0 -160
  61. package/docs/local_usage.md +0 -191
  62. package/docs/scaling_to_1000_tasks.md +0 -3
  63. package/docs/trace_schema.md +0 -192
  64. package/docs/v0.2_release_notes.md +0 -63
  65. package/src/agent_apprenticeship_trace/io.py +0 -20
  66. package/src/agent_apprenticeship_trace/trace_normalizer.py +0 -1026
  67. package/src/agent_apprenticeship_trace/trace_prompt.py +0 -78
  68. /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/actual_outputs_normalizer.py +0 -0
  69. /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/apprentice_adapters.py +0 -0
  70. /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/artifact_capture.py +0 -0
  71. /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/artifact_previews.py +0 -0
  72. /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/artifact_resolver.py +0 -0
  73. /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/command_discovery.py +0 -0
  74. /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/contract_diagnostics.py +0 -0
  75. /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/contract_verifier.py +0 -0
  76. /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/env.py +0 -0
  77. /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/lesson_extractor.py +0 -0
  78. /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/llm_output_normalizer.py +0 -0
  79. /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/openai_structured.py +0 -0
  80. /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/recipes.py +0 -0
  81. /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/revision.py +0 -0
  82. /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/role_runners.py +0 -0
  83. /package/src/{agent_apprenticeship_trace → agent_apprenticeship}/version.py +0 -0
@@ -16,8 +16,8 @@ from contextlib import contextmanager
16
16
  from datetime import datetime, timezone
17
17
  from pathlib import Path
18
18
  import typer
19
- from .schemas import RawTaskRecord, AgentTrace
20
- from .io import read_json, read_jsonl, write_json
19
+ from .schemas import RawTaskRecord, WorkflowTrace
20
+ from .io import read_workflow_json as read_json, read_workflow_jsonl as read_jsonl, write_json
21
21
  from .loop import run_task as run_one
22
22
  from .batch_runner import run_batch as run_batch_impl
23
23
  from .bundle_exporter import create_contribution_bundle
@@ -2357,6 +2357,8 @@ def _safe_bundle_path(path: Path) -> Path:
2357
2357
  manifest = bundle / "contribution_manifest.json"
2358
2358
  if not manifest.exists():
2359
2359
  raise FileNotFoundError(f"Experience Compilation manifest not found: {manifest}")
2360
+ from .io import validate_trace_tree
2361
+ validate_trace_tree(bundle)
2360
2362
  return bundle
2361
2363
 
2362
2364
 
@@ -3519,6 +3521,9 @@ def _locate_package_root(path: Path) -> Path:
3519
3521
 
3520
3522
  def _copy_or_zip_package(src: Path, output: Path, *, item_id: str, source: str) -> Path:
3521
3523
  src = _locate_package_root(src)
3524
+ from .agent_env_exporter import export_agent_env
3525
+ from .io import validate_trace_tree
3526
+ validate_trace_tree(src)
3522
3527
  output = output.expanduser()
3523
3528
  manifest = {
3524
3529
  "export_type": "full_experience_compilation",
@@ -3547,6 +3552,7 @@ def _copy_or_zip_package(src: Path, output: Path, *, item_id: str, source: str)
3547
3552
  shutil.rmtree(staging)
3548
3553
  (staging / "package").mkdir(parents=True)
3549
3554
  shutil.copytree(src, staging / "package", dirs_exist_ok=True)
3555
+ export_agent_env(staging / "package")
3550
3556
  write_json(staging / "export_manifest.json", manifest)
3551
3557
  _zip_dir(staging, output)
3552
3558
  shutil.rmtree(staging)
@@ -3555,6 +3561,7 @@ def _copy_or_zip_package(src: Path, output: Path, *, item_id: str, source: str)
3555
3561
  shutil.rmtree(output)
3556
3562
  output.mkdir(parents=True)
3557
3563
  shutil.copytree(src, output / "package", dirs_exist_ok=True)
3564
+ export_agent_env(output / "package")
3558
3565
  write_json(output / "export_manifest.json", manifest)
3559
3566
  return output
3560
3567
 
@@ -3602,6 +3609,8 @@ def ecosystem_pull(
3602
3609
  typer.echo(f"Could not pull ecosystem item from R2: {redact_secrets(str(exc))}")
3603
3610
  typer.echo("Fallback: apprentice ecosystem pull " + bundle_id + " --storage github")
3604
3611
  raise typer.Exit(1)
3612
+ from .io import validate_trace_tree
3613
+ validate_trace_tree(dest)
3605
3614
  typer.echo(f'pulled_bundle={dest}')
3606
3615
  if full or mode == "full":
3607
3616
  export_path = _copy_or_zip_package(dest, get_settings().app_home / "ecosystem" / "exports" / (_row_id(row) or bundle_id), item_id=_row_id(row) or bundle_id, source="r2_pull")
@@ -3660,6 +3669,8 @@ def ecosystem_pull(
3660
3669
  typer.echo(f'Reason: {exc}')
3661
3670
  typer.echo('Next action: set AA_HOME to a writable directory and try again.')
3662
3671
  raise typer.Exit(1)
3672
+ from .io import validate_trace_tree
3673
+ validate_trace_tree(dest)
3663
3674
  typer.echo(f'pulled_bundle={dest}')
3664
3675
  return
3665
3676
  source=Path(src).expanduser()
@@ -3677,6 +3688,8 @@ def ecosystem_pull(
3677
3688
  typer.echo(f'Reason: {exc}')
3678
3689
  typer.echo('Next action: set AA_HOME to a writable directory and try again.')
3679
3690
  raise typer.Exit(1)
3691
+ from .io import validate_trace_tree
3692
+ validate_trace_tree(dest)
3680
3693
  typer.echo(f'pulled_bundle={dest}')
3681
3694
  if full or mode == "full":
3682
3695
  export_path = _copy_or_zip_package(dest, get_settings().app_home / "ecosystem" / "exports" / bundle_id, item_id=bundle_id, source="local_pull")
@@ -3948,6 +3961,11 @@ def bundle_check(path: Path=typer.Argument(..., help='Experience Compilation fol
3948
3961
 
3949
3962
  issues: list[str] = []
3950
3963
  bundle = Path(path).expanduser()
3964
+ from .io import validate_trace_tree
3965
+ try:
3966
+ validate_trace_tree(bundle)
3967
+ except ValueError as exc:
3968
+ issues.append(str(exc))
3951
3969
  manifest_candidates = [
3952
3970
  bundle / "contribution_manifest.json",
3953
3971
  bundle / "task_manifest.json",
@@ -4204,11 +4222,11 @@ def validate_public_release(release_root: Path=typer.Option(...)):
4204
4222
  @app.command('repair-roles', hidden=True)
4205
4223
  def repair_roles(run_root: Path=typer.Option(...), task_id: str=typer.Option(...), roles: str=typer.Option('evaluator_agent,grader_agent,verifier_agent'), attempts: str=typer.Option('baseline,revised'), rebuild_release: Path|None=typer.Option(None)):
4206
4224
  """Re-run model evaluation roles from existing task package artifacts/traces."""
4207
- from .schemas import RubricSpec, ActualOutputs, AgentTrace, GraderResult, VerifierResult
4225
+ from .schemas import RubricSpec, ActualOutputs, WorkflowTrace, GraderResult, VerifierResult
4208
4226
  from .grader import grade_attempt, apply_score_reliability
4209
4227
  from .verifier import verify_attempt
4210
4228
  from .evaluator import evaluate_attempt
4211
- from .io import read_json, write_json
4229
+ from .io import read_workflow_json as read_json, write_json
4212
4230
  from datetime import datetime, timezone
4213
4231
  import shutil
4214
4232
  pkg=run_root/'packages'/task_id
@@ -4224,7 +4242,7 @@ def repair_roles(run_root: Path=typer.Option(...), task_id: str=typer.Option(...
4224
4242
  typer.echo(f'skipping {attempt}: missing existing attempt outputs')
4225
4243
  continue
4226
4244
  actual=ActualOutputs.model_validate(read_json(actual_path))
4227
- trace=AgentTrace.model_validate(read_json(trace_path))
4245
+ trace=WorkflowTrace.model_validate(read_json(trace_path))
4228
4246
  grade_path=pkg/'grading'/f'{attempt}_grader_result.json'
4229
4247
  ver_path=pkg/'grading'/f'{attempt}_verifier_result.json'
4230
4248
  grader=GraderResult.model_validate(read_json(grade_path)) if grade_path.exists() else None
@@ -4296,9 +4314,10 @@ def summarize_releases(release_root: Path=typer.Option(Path('outputs/releases'))
4296
4314
  typer.echo('task_ids_publishable_with_warnings=' + ','.join(warnings))
4297
4315
  typer.echo('task_ids_clean_publishable=' + ','.join(clean))
4298
4316
 
4299
- @app.command('inspect-trace', hidden=True)
4317
+ @app.command('inspect-trace')
4300
4318
  def inspect_trace(trace_path: Path):
4301
- t=AgentTrace.model_validate_json(trace_path.read_text()); typer.echo(f'trace_id={t.trace_id}\ntask_id={t.task_id}\nsteps={len(t.steps)}')
4319
+ from .atif import load_trace
4320
+ t=WorkflowTrace.model_validate(load_trace(trace_path)); typer.echo(f'trace_id={t.trace_id}\ntask_id={t.task_id}\nsteps={len(t.steps)}')
4302
4321
 
4303
4322
  @app.command('codex-smoke', hidden=True)
4304
4323
  def codex_smoke():
@@ -4,7 +4,7 @@ import signal
4
4
  import shutil, subprocess
5
5
  from pathlib import Path
6
6
  import re
7
- from .schemas import AgentTrace, AgentTraceStep, ActualOutputs, RawTaskRecord, TaskIntakeSpec
7
+ from .schemas import WorkflowTrace, WorkflowTraceStep, ActualOutputs, RawTaskRecord, TaskIntakeSpec
8
8
  from .trace_prompt import build_worker_prompt
9
9
  from .io import write_json
10
10
  from .config import get_settings
@@ -35,14 +35,14 @@ def _safe_agent_config(settings, runner_kind: str, attempt_kind: str) -> dict:
35
35
 
36
36
 
37
37
  def _enrich_runtime_trace(
38
- trace: AgentTrace,
38
+ trace: WorkflowTrace,
39
39
  actual: ActualOutputs,
40
40
  package_root: Path,
41
41
  attempt_kind: str,
42
42
  runner_kind: str,
43
43
  started_at: str | None,
44
44
  ended_at: str | None,
45
- ) -> AgentTrace:
45
+ ) -> WorkflowTrace:
46
46
  settings = get_settings()
47
47
  trace.run_id = package_root.parent.parent.name if package_root.parent.parent.name else None
48
48
  trace.package_id = package_root.name
@@ -243,24 +243,24 @@ def codex_exec_help(command: str = "codex") -> str:
243
243
  def _codex_exec_supports(flag: str, command: str = "codex") -> bool:
244
244
  return flag in codex_exec_help(command)
245
245
 
246
- def _fallback_trace(spec: TaskIntakeSpec, attempt_kind: str, prompt: str, actual: ActualOutputs, error_type: str, error_message: str, codex_sandbox: str) -> AgentTrace:
246
+ def _fallback_trace(spec: TaskIntakeSpec, attempt_kind: str, prompt: str, actual: ActualOutputs, error_type: str, error_message: str, codex_sandbox: str) -> WorkflowTrace:
247
247
  cls=classify_provider_failure(error_message)
248
248
  attempt_id=f'{spec.task_id}_{attempt_kind}'
249
249
  actor=f'agent:{"reviser" if attempt_kind=="revised" else "worker"}'
250
250
  safe=(public_error_summary(error_message) if cls else redact_secrets(error_message)[-3000:])
251
251
  error_type=cls.get('error_type') or error_type
252
252
  steps=[
253
- AgentTraceStep(step=1, turn=1, actor='user', action='user_message', input=spec.normalized_instruction, message_role='direct_request'),
254
- AgentTraceStep(step=2, turn=1, actor=actor, action='error', operation='other', tool='codex_cli', environment_domain='terminal', execution_mode='serial', observation='Codex attempt ended without a valid agent_trace.json or actual_outputs.json.', input='Validate required output contract for ./agent_trace.json and ./actual_outputs.json.', input_source={'source':'runtime_contract_check'}, output=safe, state_change='A minimal failure trace and failed actual_outputs.json were written by the runner.', reasoning='The package must preserve failure evidence without fabricating a successful detailed trace.', caused_by=[1], causal_type='dependency_on_tool_result', success=False, step_outcome='failed', error_type=error_type, error_message=safe, artifact_refs=[f'attempts/{attempt_kind}/prompt.md', f'attempts/{attempt_kind}/stdout.txt', f'attempts/{attempt_kind}/stderr.txt', f'attempts/{attempt_kind}/final_message.txt']),
253
+ WorkflowTraceStep(step=1, turn=1, actor='user', action='user_message', input=spec.normalized_instruction, message_role='direct_request'),
254
+ WorkflowTraceStep(step=2, turn=1, actor=actor, action='error', operation='other', tool='codex_cli', environment_domain='terminal', execution_mode='serial', observation='Codex attempt ended without a valid agent_trace.json or actual_outputs.json.', input='Validate required output contract for ./agent_trace.json and ./actual_outputs.json.', input_source={'source':'runtime_contract_check'}, output=safe, state_change='A minimal failure trace and failed actual_outputs.json were written by the runner.', reasoning='The package must preserve failure evidence without fabricating a successful detailed trace.', caused_by=[1], causal_type='dependency_on_tool_result', success=False, step_outcome='failed', error_type=error_type, error_message=safe, artifact_refs=[f'attempts/{attempt_kind}/prompt.md', f'attempts/{attempt_kind}/stdout.txt', f'attempts/{attempt_kind}/stderr.txt', f'attempts/{attempt_kind}/final_message.txt']),
255
255
  ]
256
- return AgentTrace(trace_id=f'trace_{attempt_id}_fallback', collection_id=None, trace_mode='live', task=spec.normalized_instruction, task_id=spec.task_id, attempt_id=attempt_id, attempt_kind=attempt_kind if attempt_kind in ['baseline','revised'] else 'other', agent_tools=['codex_cli','Bash','python','file_read','file_write'], system_prompt=prompt, system_prompt_hash=None, skills=['agent_trace_skill'], learning='When a live agent cannot write outputs, preserve logs and create a minimal failure trace.', termination_reason='agent_blocked', steps=steps, actual_outputs=actual, artifacts=[], metadata_json={'trace_valid':False,'fallback_trace_created':True,'codex_sandbox':codex_sandbox, **cls})
256
+ return WorkflowTrace(trace_id=f'trace_{attempt_id}_fallback', collection_id=None, trace_mode='live', task=spec.normalized_instruction, task_id=spec.task_id, attempt_id=attempt_id, attempt_kind=attempt_kind if attempt_kind in ['baseline','revised'] else 'other', agent_tools=['codex_cli','Bash','python','file_read','file_write'], system_prompt=prompt, system_prompt_hash=None, skills=['agent_trace_skill'], learning='When a live agent cannot write outputs, preserve logs and create a minimal failure trace.', termination_reason='agent_blocked', steps=steps, actual_outputs=actual, artifacts=[], metadata_json={'trace_valid':False,'fallback_trace_created':True,'codex_sandbox':codex_sandbox, **cls})
257
257
 
258
258
  def _write_fallback_report(d: Path, spec: TaskIntakeSpec, attempt_kind: str, reason: str, raw_parse_error: bool=False) -> None:
259
259
  report=TraceNormalizationReport(task_id=spec.task_id, attempt_id=f'{spec.task_id}_{attempt_kind}', attempt_kind=attempt_kind, raw_trace_ref='agent_trace.raw.json' if (d/'agent_trace.raw.json').exists() else None, normalized_trace_ref=None, canonical_trace_ref='agent_trace.json', trace_schema_valid=True, trace_normalized=False, trace_lossless=True, fallback_trace=True, raw_step_count=0, normalized_step_count=2, discarded_step_count=0, raw_trace_parse_error=raw_parse_error, validation_errors=[reason], metadata_json={'fallback_reason': reason})
260
260
  write_json(d/'trace_normalization_report.json', report)
261
261
 
262
262
 
263
- def ensure_attempt_outputs(package_root: Path, spec: TaskIntakeSpec, attempt_kind: str, prompt: str, codex_sandbox: str, validation_error: Exception | None=None) -> tuple[AgentTrace, ActualOutputs, bool]:
263
+ def ensure_attempt_outputs(package_root: Path, spec: TaskIntakeSpec, attempt_kind: str, prompt: str, codex_sandbox: str, validation_error: Exception | None=None) -> tuple[WorkflowTrace, ActualOutputs, bool]:
264
264
  d=_attempt_dir(package_root, attempt_kind)
265
265
  recovered_contract_files = _recover_misplaced_contract_files(d)
266
266
  actual_path=d/'actual_outputs.json'; trace_path=d/'agent_trace.json'; raw_path=d/'agent_trace.raw.json'
@@ -279,14 +279,10 @@ def ensure_attempt_outputs(package_root: Path, spec: TaskIntakeSpec, attempt_kin
279
279
  context=TraceNormalizationContext(task_id=spec.task_id, attempt_id=f'{spec.task_id}_{attempt_kind}', attempt_kind=attempt_kind, task=spec.normalized_instruction, actual_outputs=actual)
280
280
  result=repair_agent_trace_file(raw_path, context)
281
281
  if result.normalized_trace and (result.report.raw_step_count or result.normalized_trace.get('steps')):
282
- try:
283
- AgentTrace.model_validate_json(raw_path.read_text())
284
- except Exception:
285
- (d/'agent_trace.invalid.json').write_text(raw_path.read_text())
286
282
  write_json(d/'agent_trace.normalized.json', result.normalized_trace)
287
283
  write_json(d/'agent_trace.json', result.normalized_trace)
288
284
  write_json(d/'trace_normalization_report.json', result.report)
289
- trace = AgentTrace.model_validate(result.normalized_trace)
285
+ trace = WorkflowTrace.model_validate(result.normalized_trace)
290
286
  if recovered_contract_files:
291
287
  trace.metadata_json["contract_files_recovered_from"] = recovered_contract_files
292
288
  actual.metadata_json.setdefault("contract_files_recovered_from", recovered_contract_files)
@@ -297,6 +293,10 @@ def ensure_attempt_outputs(package_root: Path, spec: TaskIntakeSpec, attempt_kin
297
293
  msg=f'{reason}\n\nfinal_message:\n{final}\n\nstderr:\n{stderr}'
298
294
  trace=_fallback_trace(spec, attempt_kind, prompt, actual, 'TraceNormalizationError', msg, codex_sandbox)
299
295
  trace.metadata_json['fallback_reason']=classify_provider_failure(msg).get('fallback_reason') or ('unparseable_trace' if result.fallback_required else 'missing_steps')
296
+ if raw_path.exists():
297
+ raw_path.replace(d/'rejected_agent_response.txt')
298
+ write_json(d/'agent_trace.raw.json', trace)
299
+ write_json(d/'agent_trace.normalized.json', trace)
300
300
  write_json(trace_path, trace); _write_fallback_report(d, spec, attempt_kind, trace.metadata_json['fallback_reason'], result.fallback_required)
301
301
  return trace, actual, False
302
302
  final=(d/'final_message.txt').read_text(errors='ignore') if (d/'final_message.txt').exists() else ''
@@ -326,12 +326,12 @@ def deterministic_attempt(package_root: Path, raw: RawTaskRecord, spec: TaskInta
326
326
  files.append(f'attempts/{attempt_kind}/artifacts/{fname}')
327
327
  actual=ActualOutputs(task_id=spec.task_id, attempt_id=attempt_id, attempt_kind=attempt_kind, status='success', output_summary=f'Deterministic {attempt_kind} outputs for {spec.normalized_title}', primary_output_ref=files[0] if files else None, input_artifact_refs=[f'input/{name}' for name in input_files], deliverable_refs=files, final_message_ref=f'attempts/{attempt_kind}/final_message.txt', artifact_refs=files, files_created=files, files_modified=[], files_deleted=[], stdout_ref=f'attempts/{attempt_kind}/stdout.txt', stderr_ref=f'attempts/{attempt_kind}/stderr.txt', raw_log_refs=[f'attempts/{attempt_kind}/stdout.txt', f'attempts/{attempt_kind}/stderr.txt'], error_type=None, error_message=None, metadata_json={'runner':'deterministic','expected_deliverable_items':deliverables,'produced_deliverable_items':[Path(f).name for f in files]})
328
328
  steps=[
329
- AgentTraceStep(step=1, turn=1, actor='user', action='user_message', input=spec.normalized_instruction, message_role='direct_request'),
330
- AgentTraceStep(step=2, turn=1, actor=f'agent:{"reviser" if attempt_kind=="revised" else "worker"}', action='agent_step', operation='plan', tool=None, environment_domain='file', execution_mode='serial', observation='Task instructions, ./input files, and worker-visible rubric are available.', input='Review task and required deliverables.', input_source={'source':'task_packet'}, output='Plan created.', state_change='The attempt plan identified required artifacts.', reasoning='A short plan reduces missed deliverables.', caused_by=[1], causal_type='user_request', success=True, step_outcome='progress', artifact_refs=[f'input/{name}' for name in input_files]),
331
- AgentTraceStep(step=3, turn=1, actor=f'agent:{"reviser" if attempt_kind=="revised" else "worker"}', action='agent_step', operation='write', tool='file_write', environment_domain='file', execution_mode='serial', observation='Deliverable names are known.', input=', '.join(files), input_source={'source':'planned_deliverables'}, output='Artifacts written.', state_change='Required deterministic artifacts were created under ./artifacts.', reasoning='Writing explicit files satisfies the artifact contract.', caused_by=[2], causal_type='execution_of_plan', success=True, step_outcome='completed', artifact_refs=files),
329
+ WorkflowTraceStep(step=1, turn=1, actor='user', action='user_message', input=spec.normalized_instruction, message_role='direct_request'),
330
+ WorkflowTraceStep(step=2, turn=1, actor=f'agent:{"reviser" if attempt_kind=="revised" else "worker"}', action='agent_step', operation='plan', tool=None, environment_domain='file', execution_mode='serial', observation='Task instructions, ./input files, and worker-visible rubric are available.', input='Review task and required deliverables.', input_source={'source':'task_packet'}, output='Plan created.', state_change='The attempt plan identified required artifacts.', reasoning='A short plan reduces missed deliverables.', caused_by=[1], causal_type='user_request', success=True, step_outcome='progress', artifact_refs=[f'input/{name}' for name in input_files]),
331
+ WorkflowTraceStep(step=3, turn=1, actor=f'agent:{"reviser" if attempt_kind=="revised" else "worker"}', action='agent_step', operation='write', tool='file_write', environment_domain='file', execution_mode='serial', observation='Deliverable names are known.', input=', '.join(files), input_source={'source':'planned_deliverables'}, output='Artifacts written.', state_change='Required deterministic artifacts were created under ./artifacts.', reasoning='Writing explicit files satisfies the artifact contract.', caused_by=[2], causal_type='execution_of_plan', success=True, step_outcome='completed', artifact_refs=files),
332
332
  ]
333
333
  ended_at = utc_now()
334
- trace=AgentTrace(trace_id=f'trace_{attempt_id}', collection_id=None, trace_mode='live', task=spec.normalized_instruction, task_id=spec.task_id, attempt_id=attempt_id, attempt_kind=attempt_kind if attempt_kind in ['baseline','revised'] else 'other', agent_tools=['deterministic_runner','file_write'], system_prompt=prompt, system_prompt_hash=None, skills=['agent_trace_skill'], learning='Materialize required artifacts and keep evaluation outside the trace.', termination_reason='task_complete', steps=steps, actual_outputs=actual, artifacts=[], metadata_json={'trace_valid': True})
334
+ trace=WorkflowTrace(trace_id=f'trace_{attempt_id}', collection_id=None, trace_mode='live', task=spec.normalized_instruction, task_id=spec.task_id, attempt_id=attempt_id, attempt_kind=attempt_kind if attempt_kind in ['baseline','revised'] else 'other', agent_tools=['deterministic_runner','file_write'], system_prompt=prompt, system_prompt_hash=None, skills=['agent_trace_skill'], learning='Materialize required artifacts and keep evaluation outside the trace.', termination_reason='task_complete', steps=steps, actual_outputs=actual, artifacts=[], metadata_json={'trace_valid': True})
335
335
  trace = _enrich_runtime_trace(trace, actual, package_root, attempt_kind, 'deterministic', started_at, ended_at)
336
336
  write_json(d/'actual_outputs.json', actual)
337
337
  actual_ctx=ActualOutputsNormalizationContext(task_id=spec.task_id, attempt_id=attempt_id, attempt_kind=attempt_kind, package_root=package_root, required_artifacts=deliverables)
@@ -12,7 +12,7 @@ from pydantic import BaseModel, Field
12
12
 
13
13
  from .env import load_local_env
14
14
  from .command_discovery import resolve_command, resolve_agent_command, gui_app_hint
15
- from .io import read_json, write_json
15
+ from .io import read_workflow_json as read_json, write_json
16
16
  from .recipes import MODEL_PROVIDER_RECIPES, WORKER_AGENT_RECIPES
17
17
 
18
18
 
@@ -22,7 +22,7 @@ from .config import (
22
22
  get_settings,
23
23
  )
24
24
  from .env import redact_secrets
25
- from .io import append_jsonl, read_json, read_jsonl, write_json
25
+ from .io import append_jsonl, read_workflow_json as read_json, read_workflow_jsonl as read_jsonl, write_json
26
26
  from .public_sanitizer import sanitize_public_obj
27
27
  from .version import get_package_version
28
28
 
@@ -485,6 +485,8 @@ def download_r2_package(item_id: str, dest: Path, settings: Settings | None = No
485
485
  if not target.resolve().is_relative_to(dest.resolve()):
486
486
  raise RuntimeError("R2 package contains an unsafe path.")
487
487
  zf.extractall(dest)
488
+ from .io import validate_trace_tree
489
+ validate_trace_tree(dest)
488
490
  return dest
489
491
 
490
492
 
@@ -2,9 +2,9 @@ from __future__ import annotations
2
2
  import json
3
3
  from pathlib import Path
4
4
  from pydantic import BaseModel
5
- from .schemas import EvaluatorFeedback, GraderResult, VerifierResult, AgentTrace, ActualOutputs, RevisionPlan
5
+ from .schemas import EvaluatorFeedback, GraderResult, VerifierResult, WorkflowTrace, ActualOutputs, RevisionPlan
6
6
  from .config import get_settings
7
- from .io import read_json
7
+ from .io import read_workflow_json as read_json
8
8
  from .openai_structured import get_model_provider_status, run_structured_role
9
9
  from .public_sanitizer import sha256_text
10
10
  from .artifact_previews import build_artifact_previews
@@ -60,30 +60,11 @@ def _latest_review_packet(package_root: Path | None, attempt_kind: str) -> dict:
60
60
  return packet
61
61
 
62
62
 
63
- def _compact_trace_summary(trace: AgentTrace | None) -> dict:
63
+ def _compact_trace_summary(trace: WorkflowTrace | None) -> dict:
64
64
  if trace is None:
65
65
  return {}
66
- data = trace.model_dump()
67
- steps = data.get("steps") or []
68
- return {
69
- "trace_id": data.get("trace_id"),
70
- "attempt_id": data.get("attempt_id"),
71
- "attempt_kind": data.get("attempt_kind"),
72
- "step_count": len(steps),
73
- "key_steps": [
74
- {
75
- "step": step.get("step"),
76
- "operation": step.get("operation"),
77
- "interaction_surface": step.get("interaction_surface"),
78
- "environment_domain": step.get("environment_domain"),
79
- "step_outcome": step.get("step_outcome"),
80
- "artifact_refs": (step.get("artifact_refs") or [])[:5],
81
- "state_change": str(step.get("state_change") or "")[:240],
82
- }
83
- for step in steps[:10]
84
- if isinstance(step, dict) and step.get("action") != "user_message"
85
- ],
86
- }
66
+ from .atif import from_workflow
67
+ return from_workflow(trace.model_dump())
87
68
 
88
69
 
89
70
  def _compact_grader(grader: GraderResult) -> dict:
@@ -142,13 +123,13 @@ def _compact_artifact_previews(bundle: dict | None) -> dict:
142
123
  }
143
124
 
144
125
 
145
- def _evaluator_prompt(grader: GraderResult, verifier: VerifierResult, outputs: ActualOutputs, trace: AgentTrace | None, target_attempt_id: str, artifact_preview_bundle: dict | None=None, review_packet: dict | None=None) -> str:
126
+ def _evaluator_prompt(grader: GraderResult, verifier: VerifierResult, outputs: ActualOutputs, trace: WorkflowTrace | None, target_attempt_id: str, artifact_preview_bundle: dict | None=None, review_packet: dict | None=None) -> str:
146
127
  return """Return only valid JSON. Do not include markdown. Do not add extra top-level fields; place extras under metadata_json.extra_model_fields.
147
128
  Required skeleton: {"evaluator_feedback":{"feedback_id":"feedback_<task_id>_<attempt_kind>","task_id":"...","attempt_id":"...","target_actor":"apprentice","feedback_type":"other","failed_rubric_items":[],"evidence_refs":[],"artifact_refs":[],"feedback_summary":"...","actionable_feedback":[],"suggested_revision":"...","revision_priority":"medium","confidence":0.7,"hidden_reference_used":false,"hidden_reference_leaked":false,"failed_or_weak_rubric_items":[],"artifact_specific_comments":[],"trace_specific_comments":[],"metadata_json":{}},"revision_plan":{"revision_plan_id":"revision_plan_<task_id>","task_id":"...","source_attempt_id":"...","target_attempt_id":"...","revision_kind":"local_fix","revision_reason":"...","failed_rubric_items":[],"planned_changes":[],"risk_of_regression":"medium","uses_evaluator_feedback":true,"metadata_json":{}}}.
148
129
  Evaluate the attempt by first reading the loop review packet when provided, then checking compact Mentor grader/verifier outputs, actual_outputs.json, artifact refs, artifact content previews, and trace evidence. Treat the review packet as the current iteration dossier: it summarizes outputs, artifacts, trace refs, rubric/verifier status, known failures, diff evidence, and recommended review focus. Produce grounded actionable feedback and a concrete revision plan whose revision guidance can be mapped to feedback_type/feedback_content in future traces. Keep the response concise. Do not rely on Apprentice Agent self-assessment or self evaluation as outcome evidence. Do not use deterministic fallback judgement; structural guardrails are integrity checks only and are not outcome judgments.
149
- Target revised attempt id: """ + target_attempt_id + "\nLoop Review Packet JSON:\n" + json.dumps(review_packet or {}, sort_keys=True)[:5000] + "\nCompact GraderResult JSON:\n" + json.dumps(_compact_grader(grader), sort_keys=True) + "\nCompact VerifierResult JSON:\n" + json.dumps(_compact_verifier(verifier), sort_keys=True) + "\nCompact ActualOutputs JSON:\n" + json.dumps(_compact_actual(outputs), sort_keys=True) + "\nArtifact content previews JSON:\n" + json.dumps(_compact_artifact_previews(artifact_preview_bundle), sort_keys=True)[:5000] + "\nCompact Trace summary JSON:\n" + json.dumps(_compact_trace_summary(trace), sort_keys=True)[:3000]
130
+ Target revised attempt id: """ + target_attempt_id + "\nLoop Review Packet JSON:\n" + json.dumps(review_packet or {}, sort_keys=True)[:5000] + "\nCompact GraderResult JSON:\n" + json.dumps(_compact_grader(grader), sort_keys=True) + "\nCompact VerifierResult JSON:\n" + json.dumps(_compact_verifier(verifier), sort_keys=True) + "\nCompact ActualOutputs JSON:\n" + json.dumps(_compact_actual(outputs), sort_keys=True) + "\nArtifact content previews JSON:\n" + json.dumps(_compact_artifact_previews(artifact_preview_bundle), sort_keys=True)[:5000] + "\nCompact Trace summary JSON:\n" + json.dumps(_compact_trace_summary(trace), sort_keys=True)
150
131
 
151
- def evaluate_attempt(grader: GraderResult, verifier: VerifierResult, outputs: ActualOutputs, trace: AgentTrace | None, target_attempt_id: str, role_root: Path | None=None, package_root: Path | None=None) -> tuple[EvaluatorFeedback, RevisionPlan]:
132
+ def evaluate_attempt(grader: GraderResult, verifier: VerifierResult, outputs: ActualOutputs, trace: WorkflowTrace | None, target_attempt_id: str, role_root: Path | None=None, package_root: Path | None=None) -> tuple[EvaluatorFeedback, RevisionPlan]:
152
133
  settings=get_settings(); role_root=role_root or Path('outputs/roles')
153
134
  artifact_preview_bundle=build_artifact_previews(package_root, grader.evidence_refs or outputs.deliverable_refs or outputs.artifact_refs or outputs.files_created)
154
135
  review_packet=_latest_review_packet(package_root, outputs.attempt_kind)
@@ -1,10 +1,11 @@
1
1
  from __future__ import annotations
2
+ from .atif import from_workflow
2
3
  from pathlib import Path
3
4
  import json, re
4
5
  from .schemas import GraderResult, RubricSpec, ActualOutputs, RubricItemScore
5
6
  from .config import get_settings
6
- from .schemas import AgentTrace
7
- from .io import read_json, write_json
7
+ from .schemas import WorkflowTrace
8
+ from .io import read_workflow_json as read_json, write_json
8
9
  from .openai_structured import get_model_provider_status, run_structured_role
9
10
  from .public_sanitizer import sha256_text
10
11
  from .artifact_previews import build_artifact_previews
@@ -128,13 +129,13 @@ def deterministic_grade(rubric: RubricSpec, outputs: ActualOutputs, attempt_kind
128
129
  score=artifact_contract_score
129
130
  return GraderResult(grader_result_id=f'grader_{outputs.attempt_id}', task_id=outputs.task_id, attempt_id=outputs.attempt_id, attempt_kind=attempt_kind, rubric_id=rubric.rubric_id, grader_kind='structural_guardrail', score_source='structural_guardrail', score=score, max_score=1.0, passed=artifact_contract_passed, rubric_item_scores=item_scores, failed_criteria=[s.rubric_item_id for s in item_scores if not s.passed], passed_criteria=[s.rubric_item_id for s in item_scores if s.passed], evidence_refs=evidence, confidence=0.8, reasoning_summary='Structural guardrail checked artifact presence/readiness only; Mentor grading is unavailable unless a Mentor Model or human grader runs.', limitations=['semantic_quality_not_judged','mentor_grade_unavailable'], hidden_reference_used=False, hidden_reference_leaked=False, artifact_contract_score=artifact_contract_score, semantic_score=None, final_score=score, model=None, provider=None, deterministic_precheck_ref=None, public_response_summary='Structural artifact-contract guardrail completed.', score_reliability='needs_review', metadata_json={'structural_guardrail': True, 'role_status': 'mentor_grader_unavailable', 'artifact_contract_passed': artifact_contract_passed, 'semantic_quality_not_judged': True, 'missing_artifacts': missing, **match})
130
131
 
131
- def _grader_prompt(rubric: RubricSpec, outputs: ActualOutputs, trace: AgentTrace | None, precheck: GraderResult, artifact_preview_bundle: dict | None=None) -> str:
132
+ def _grader_prompt(rubric: RubricSpec, outputs: ActualOutputs, trace: WorkflowTrace | None, precheck: GraderResult, artifact_preview_bundle: dict | None=None) -> str:
132
133
  return """Return only valid JSON matching GraderResult. Do not include markdown.
133
134
  You are the Mentor Model grader. Score each rubric item using the Apprentice-generated plus Mentor-audited task rubric, attempt trace, actual_outputs.json, artifact refs, and artifact content previews when available. Return a JSON object with exactly these required top-level fields: grader_result_id, task_id, attempt_id, attempt_kind, rubric_id, grader_kind, score_source, artifact_contract_score, model_score, semantic_score, final_score, score, max_score, passed, confidence, evidence_refs, rubric_item_scores, limitations, hidden_reference_used, hidden_reference_leaked, metadata_json. Each rubric_item_scores entry must include artifact_presence_ok, semantic_correctness_score, evidence_refs, reasoning_summary, confidence, failure_mode, and improvement_suggestion. Cite evidence_refs from actual_outputs/artifacts/trace, distinguish artifact presence from Mentor-judged correctness, and set score_source=model_judged and grader_kind=model. Do not rely on Apprentice Agent self-assessment or self evaluation. Do not use deterministic fallback judgement; structural artifact-contract guardrails are auxiliary only and are not grader judgments.
134
135
  Rubric JSON:
135
- """ + json.dumps(rubric.model_dump(), sort_keys=True) + "\nActualOutputs JSON:\n" + json.dumps(outputs.model_dump(), sort_keys=True) + "\nArtifact content previews JSON:\n" + json.dumps(artifact_preview_bundle or {}, sort_keys=True)[:16000] + "\nTrace summary JSON:\n" + json.dumps((trace.model_dump() if trace else {}), sort_keys=True)[:12000] + "\nStructural guardrail JSON:\n" + json.dumps(precheck.model_dump(), sort_keys=True)
136
+ """ + json.dumps(rubric.model_dump(), sort_keys=True) + "\nActualOutputs JSON:\n" + json.dumps(outputs.model_dump(), sort_keys=True) + "\nArtifact content previews JSON:\n" + json.dumps(artifact_preview_bundle or {}, sort_keys=True)[:16000] + "\nTrace summary JSON:\n" + json.dumps((from_workflow(trace.model_dump()) if trace else {}), sort_keys=True) + "\nStructural guardrail JSON:\n" + json.dumps(precheck.model_dump(), sort_keys=True)
136
137
 
137
- def grade_attempt(rubric: RubricSpec, outputs: ActualOutputs, attempt_kind: str, trace: AgentTrace | None=None, role_root: Path | None=None, package_root: Path | None=None) -> GraderResult:
138
+ def grade_attempt(rubric: RubricSpec, outputs: ActualOutputs, attempt_kind: str, trace: WorkflowTrace | None=None, role_root: Path | None=None, package_root: Path | None=None) -> GraderResult:
138
139
  settings=get_settings()
139
140
  pre=deterministic_grade(rubric, outputs, attempt_kind, package_root)
140
141
  role_root=role_root or Path('outputs/roles')
@@ -5,7 +5,7 @@ from pathlib import Path
5
5
  from typing import Any
6
6
 
7
7
  from .config import get_settings, mentor_model_provider_readiness, apprentice_agent_readiness_status
8
- from .io import append_jsonl, read_jsonl
8
+ from .io import append_jsonl, read_workflow_jsonl as read_jsonl
9
9
  from .recipes import MODEL_PROVIDER_RECIPES, WORKER_AGENT_RECIPES
10
10
 
11
11
 
@@ -0,0 +1,104 @@
1
+ from __future__ import annotations
2
+ import json, hashlib
3
+ from pathlib import Path
4
+ from pydantic import BaseModel
5
+ from .atif import encode_traces, decode_traces, validate_trace, event_trace, NAMESPACE
6
+
7
+ TRACE_FILES = {'agent_trace.json', 'agent_trace.raw.json', 'agent_trace.normalized.json', 'trajectory.json', 'trace.json', 'trace.jsonl', 'agent_traces.jsonl', 'raw_agent_traces.jsonl'}
8
+ EVENT_FILES = {'tdo_trace.jsonl', 'compiler_trace.jsonl', 'session_events.jsonl'}
9
+
10
+
11
+ def _encode(path: Path, obj):
12
+ data = obj.model_dump(mode='json') if isinstance(obj, BaseModel) else obj
13
+ data = encode_traces(data)
14
+ if path.name in EVENT_FILES and isinstance(data, dict) and 'agent' not in data:
15
+ data = event_trace(data, 'experience-compiler' if path.name != 'session_events.jsonl' else 'agent-apprenticeship')
16
+ if path.name in TRACE_FILES | EVENT_FILES:
17
+ data = validate_trace(data)
18
+ return data
19
+
20
+
21
+ def write_json(path: Path, obj):
22
+ data = _encode(path, obj)
23
+ path.parent.mkdir(parents=True, exist_ok=True)
24
+ path.write_text(json.dumps(data, indent=2, sort_keys=True, ensure_ascii=False)+'\n', encoding='utf-8')
25
+
26
+
27
+ def read_json(path: Path):
28
+ data = json.loads(path.read_text(encoding='utf-8'))
29
+ if path.name in TRACE_FILES | EVENT_FILES:
30
+ return validate_trace(data)
31
+ return data
32
+
33
+
34
+ def append_jsonl(path: Path, obj):
35
+ data = _encode(path, obj)
36
+ path.parent.mkdir(parents=True, exist_ok=True)
37
+ with path.open('a', encoding='utf-8') as f:
38
+ f.write(json.dumps(data, sort_keys=True, ensure_ascii=False)+'\n')
39
+
40
+
41
+ def read_jsonl(path: Path):
42
+ if not path.exists(): return []
43
+ rows = [json.loads(line) for line in path.read_text(encoding='utf-8').splitlines() if line.strip()]
44
+ if path.name in TRACE_FILES | EVENT_FILES:
45
+ return [validate_trace(row) for row in rows]
46
+ return rows
47
+
48
+
49
+ def _workflow(data, path):
50
+ if path.name in EVENT_FILES:
51
+ return data['extra'][NAMESPACE]['event']
52
+ return decode_traces(data)
53
+
54
+
55
+ def read_workflow_json(path: Path):
56
+ return _workflow(read_json(path), path)
57
+
58
+
59
+ def read_workflow_jsonl(path: Path):
60
+ return [_workflow(row, path) for row in read_jsonl(path)]
61
+
62
+
63
+ def sha256_file(path: Path):
64
+ return hashlib.sha256(path.read_bytes()).hexdigest()
65
+
66
+
67
+ def validate_trace_tree(root: Path) -> int:
68
+ """Validate trace documents and embedded trajectories before package exchange."""
69
+ count = 0
70
+
71
+ def check(value):
72
+ nonlocal count
73
+ if isinstance(value, list):
74
+ for item in value:
75
+ check(item)
76
+ elif isinstance(value, dict):
77
+ if isinstance(value.get('steps'), list) and ('agent' in value or 'trace_id' in value):
78
+ validate_trace(value)
79
+ count += 1
80
+ else:
81
+ for item in value.values():
82
+ check(item)
83
+
84
+ for path in root.rglob('*'):
85
+ if not path.is_file() or path.suffix not in {'.json', '.jsonl'}:
86
+ continue
87
+ try:
88
+ if path.suffix == '.jsonl':
89
+ for row in read_jsonl(path):
90
+ check(row)
91
+ else:
92
+ check(read_json(path))
93
+ except (ValueError, TypeError) as exc:
94
+ if path.name in TRACE_FILES | EVENT_FILES:
95
+ raise ValueError(f'{path}: invalid ATIF v1.8 trace: {exc}') from exc
96
+ # Non-trace artifacts can contain arbitrary or partial JSON. A parsed
97
+ # envelope containing a recognizable invalid trace must still fail.
98
+ try:
99
+ rows = [json.loads(line) for line in path.read_text().splitlines() if line.strip()] if path.suffix == '.jsonl' else [json.loads(path.read_text())]
100
+ except ValueError:
101
+ continue
102
+ for row in rows:
103
+ check(row)
104
+ return count
@@ -11,7 +11,7 @@ from typing import Any
11
11
 
12
12
  from .config import Settings, get_settings
13
13
  from .env import contains_secret
14
- from .io import append_jsonl, read_json, read_jsonl, write_json
14
+ from .io import append_jsonl, read_workflow_json as read_json, read_workflow_jsonl as read_jsonl, write_json
15
15
  from .version import get_package_version
16
16
 
17
17
 
@@ -169,6 +169,8 @@ def _source_id(row: dict[str, Any]) -> str:
169
169
 
170
170
 
171
171
  def _bundle_source(path: Path) -> dict[str, Any]:
172
+ from .io import validate_trace_tree
173
+ validate_trace_tree(path)
172
174
  manifest_path = path / "contribution_manifest.json"
173
175
  manifest = read_json(manifest_path) if manifest_path.exists() else {}
174
176
  bundle_id = str(manifest.get("bundle_id") or path.name)
@@ -424,6 +426,8 @@ def compile_experience_pack(
424
426
  write_json(pack_path / "source_refs.json", {"sources": source_refs})
425
427
  (pack_path / "skill.md").write_text(skill)
426
428
  (pack_path / "SUMMARY.md").write_text(summary)
429
+ from .agent_env_exporter import export_agent_env
430
+ export_agent_env(pack_path)
427
431
  return pack_path
428
432
 
429
433
 
@@ -692,6 +696,8 @@ def install_runtime_training_from_bundle(
692
696
  """Install runtime-useful Experience Compiler outputs into the local registry."""
693
697
  s = settings or get_settings()
694
698
  bundle = _find_bundle_root(bundle_path)
699
+ from .io import validate_trace_tree
700
+ validate_trace_tree(bundle)
695
701
  compiler = bundle / "experience_compiler"
696
702
  skill_dir = compiler / "skill_pack"
697
703
  runtime_dir = compiler / "runtime_learning_package"
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
  from pathlib import Path
3
3
  from typing import Callable
4
- from .schemas import RawTaskRecord, ActualOutputs, AgentTrace
4
+ from .schemas import RawTaskRecord, ActualOutputs, WorkflowTrace
5
5
  from .config import get_settings
6
6
  from .task_intake import task_intake
7
7
  from .rubric_generation import generate_rubric
@@ -15,7 +15,7 @@ from .loop_review import reviewer_feedback_from_evaluator, write_loop_review_ite
15
15
  from .lesson_extractor import extract_lesson
16
16
  from .training_signals import process_supervision_from_trace, reward_modeling, training_signals
17
17
  from .revision import compute_hillclimb, preference_pair
18
- from .io import write_json, append_jsonl, read_json
18
+ from .io import write_json, append_jsonl, read_workflow_json as read_json
19
19
 
20
20
 
21
21
  def _attempt(package_root, raw, spec, kind, runner, feedback=None):
@@ -37,7 +37,7 @@ def _is_apprentice_operational_failure(actual: ActualOutputs) -> bool:
37
37
  )
38
38
 
39
39
 
40
- def _write_baseline_only_signals(pkg: Path, b_actual: ActualOutputs, b_grade, b_ver, b_trace: AgentTrace, fb) -> None:
40
+ def _write_baseline_only_signals(pkg: Path, b_actual: ActualOutputs, b_grade, b_ver, b_trace: WorkflowTrace, fb) -> None:
41
41
  lesson=extract_lesson(compute_hillclimb(b_grade, b_grade)); write_json(pkg/'signals/lesson_pack.json', lesson)
42
42
  for ex in process_supervision_from_trace(b_trace, b_grade, b_ver, fb): append_jsonl(pkg/'signals/process_supervision.jsonl', ex)
43
43
  append_jsonl(pkg/'signals/reward_modeling.jsonl', reward_modeling(b_actual,b_grade))
@@ -76,7 +76,7 @@ def run_task(
76
76
  actual_iterations=1
77
77
  baseline = _attempt(pkg, raw, spec, 'baseline', runner)
78
78
  b_actual=ActualOutputs.model_validate(read_json(pkg/'attempts/baseline/actual_outputs.json'))
79
- b_trace=AgentTrace.model_validate(read_json(pkg/'attempts/baseline/agent_trace.json'))
79
+ b_trace=WorkflowTrace.model_validate(read_json(pkg/'attempts/baseline/agent_trace.json'))
80
80
  if attempt_complete_callback:
81
81
  attempt_complete_callback(pkg, 'baseline')
82
82
  revised_attempt_ids=[]; selected_attempt_id=b_actual.attempt_id
@@ -165,7 +165,7 @@ def run_task(
165
165
  revised = _attempt(pkg, raw, spec, 'revised', runner, fb.feedback_summary)
166
166
  actual_iterations=2
167
167
  r_actual=ActualOutputs.model_validate(read_json(pkg/'attempts/revised/actual_outputs.json'))
168
- r_trace=AgentTrace.model_validate(read_json(pkg/'attempts/revised/agent_trace.json'))
168
+ r_trace=WorkflowTrace.model_validate(read_json(pkg/'attempts/revised/agent_trace.json'))
169
169
  r_grade=grade_attempt(rubric, r_actual, 'revised', r_trace, role_root, pkg)
170
170
  r_ver=verify_attempt(r_grade, r_actual, r_trace, role_root, pkg)
171
171
  r_grade=apply_score_reliability(r_grade, r_ver)
@@ -5,7 +5,7 @@ from datetime import datetime, timezone
5
5
  from pathlib import Path
6
6
  from typing import Any
7
7
 
8
- from .io import read_json, write_json
8
+ from .io import read_workflow_json as read_json, write_json
9
9
  from .public_sanitizer import sanitize_public_obj, sanitize_public_text
10
10
 
11
11
 
@@ -7,7 +7,7 @@ from pathlib import Path
7
7
  from typing import Any, Iterable
8
8
 
9
9
  from .config import Settings
10
- from .io import read_json, write_json
10
+ from .io import read_workflow_json as read_json, write_json
11
11
 
12
12
 
13
13
  def _now() -> str:
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
  from pathlib import Path
3
3
  import hashlib, mimetypes, json, shutil
4
4
  from .schemas import *
5
- from .io import write_json, append_jsonl
5
+ from .io import write_json, append_jsonl, read_workflow_json as read_json
6
6
  from .rubric_generation import worker_visible_markdown
7
7
  from .artifact_resolver import artifact_ref_candidates, normalize_artifact_ref
8
8
 
@@ -220,7 +220,7 @@ def _trace_artifact_links(package_root: Path) -> dict[str, list[dict]]:
220
220
  links: dict[str, list[dict]] = {}
221
221
  for trace_path in package_root.glob('attempts/*/agent_trace.json'):
222
222
  try:
223
- trace=json.loads(trace_path.read_text())
223
+ trace=read_json(trace_path)
224
224
  except Exception:
225
225
  continue
226
226
  attempt_id=trace.get('attempt_id')
@@ -250,7 +250,7 @@ def write_artifacts_index(package_root: Path):
250
250
  trace_links=_trace_artifact_links(package_root)
251
251
  for f in package_root.rglob('*'):
252
252
  rel=f.relative_to(package_root)
253
- if is_ignored_release_path(rel):
253
+ if 'agent_env' in rel.parts or is_ignored_release_path(rel):
254
254
  continue
255
255
  if f.is_file() and f.name != 'artifacts_index.json':
256
256
  mime=mimetypes.guess_type(f.name)[0]
@@ -300,4 +300,6 @@ def write_artifacts_index(package_root: Path):
300
300
  manifest['trace_counters']=counters
301
301
  write_json(manifest_path, manifest)
302
302
  write_json(package_root/'artifacts_index.json', paths)
303
+ from .agent_env_exporter import export_agent_env
304
+ export_agent_env(package_root)
303
305
  return paths
@@ -6,7 +6,7 @@ from datetime import datetime, timezone
6
6
  from pathlib import Path
7
7
  from typing import Any, Callable, Literal
8
8
 
9
- from .io import append_jsonl, read_json, read_jsonl, write_json
9
+ from .io import append_jsonl, read_workflow_json as read_json, read_workflow_jsonl as read_jsonl, write_json
10
10
 
11
11
  RUN_TERMINAL_STATUSES = {"completed", "partial", "failed"}
12
12
 
@@ -10,7 +10,7 @@ from pathlib import Path
10
10
  from .bundle_exporter import create_contribution_bundle
11
11
  from .config import Settings, apprentice_agent_display_name, apprentice_agent_readiness_status, get_settings, settings_override
12
12
  from .env import redact_secrets
13
- from .io import read_json, write_json
13
+ from .io import read_workflow_json as read_json, write_json
14
14
  from .loop import run_task
15
15
  from .loop_review import record_followup_review_update, record_human_loop_feedback
16
16
  from .mentor_checkpoints import write_mentor_checkpoints