okstra 0.32.1 → 0.33.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.
@@ -14,13 +14,13 @@ ctx dict 의 schema 는 `okstra_ctl.paths.compute_run_paths()` 의 반환값을
14
14
  기본으로, 호출자가 추가 키 (workflow state / model display / related tasks /
15
15
  session id 등) 를 덧붙여 전달한다.
16
16
  """
17
+
17
18
  from __future__ import annotations
18
19
 
19
20
  import json
20
21
  import sys
21
22
  from pathlib import Path
22
23
 
23
-
24
24
  # --------------------------------------------------------------------------- #
25
25
  # helpers
26
26
  # --------------------------------------------------------------------------- #
@@ -49,7 +49,7 @@ def _write_json(path: Path, payload: dict) -> None:
49
49
 
50
50
  _FM_DEFAULT = "no-classification"
51
51
 
52
- _FM_TAGS_BASE = ["obsidian", "okstra"]
52
+ _FM_TAGS_BASE = []
53
53
 
54
54
  _FM_TAGS_CATALOG: dict[str, list[str]] = {
55
55
  "task-brief": ["task-brief"],
@@ -139,7 +139,9 @@ def _frontmatter_mapping(ctx: dict) -> dict:
139
139
 
140
140
 
141
141
  def _resolve_workers(ctx: dict) -> list[str]:
142
- return [w.strip() for w in ctx.get("SELECTED_REVIEWERS", "").split(",") if w.strip()]
142
+ return [
143
+ w.strip() for w in ctx.get("SELECTED_REVIEWERS", "").split(",") if w.strip()
144
+ ]
143
145
 
144
146
 
145
147
  def _worker_catalog(ctx: dict) -> dict:
@@ -198,17 +200,19 @@ def render_team_state(team_state_path: str, ctx: dict) -> None:
198
200
  workers = []
199
201
  for w in selected:
200
202
  m = catalog[w]
201
- workers.append({
202
- "workerId": m["workerId"],
203
- "role": m["role"],
204
- "agent": m["agent"],
205
- "model": m["model"],
206
- "modelExecutionValue": m["modelExecutionValue"],
207
- "status": "not-run",
208
- "resultPath": m["resultPath"],
209
- "promptPath": m["promptPath"],
210
- "reason": "",
211
- })
203
+ workers.append(
204
+ {
205
+ "workerId": m["workerId"],
206
+ "role": m["role"],
207
+ "agent": m["agent"],
208
+ "model": m["model"],
209
+ "modelExecutionValue": m["modelExecutionValue"],
210
+ "status": "not-run",
211
+ "resultPath": m["resultPath"],
212
+ "promptPath": m["promptPath"],
213
+ "reason": "",
214
+ }
215
+ )
212
216
  payload = {
213
217
  "schemaVersion": "1.0",
214
218
  "taskKey": ctx.get("TASK_KEY", ""),
@@ -280,9 +284,15 @@ def render_reference_expectations(brief_path: str, output_path: str, ctx: dict)
280
284
  "## Configuration References and Expected Values",
281
285
  "",
282
286
  ]
283
- parts.append(config_text or "- No explicit configuration-file expectations were provided in the task brief.")
287
+ parts.append(
288
+ config_text
289
+ or "- No explicit configuration-file expectations were provided in the task brief."
290
+ )
284
291
  parts.extend(["", "## Deployment Manifests and Expected Values", ""])
285
- parts.append(deployment_text or "- No explicit deployment-manifest expectations were provided in the task brief.")
292
+ parts.append(
293
+ deployment_text
294
+ or "- No explicit deployment-manifest expectations were provided in the task brief."
295
+ )
286
296
  _write_text(Path(output_path), "\n".join(parts).rstrip() + "\n")
287
297
 
288
298
 
@@ -315,7 +325,11 @@ def render_task_catalog_discovery(output_path: str, ctx: dict) -> None:
315
325
  continue
316
326
  task_root = manifest_path.parent
317
327
  timeline_relative = s(manifest, "historyTimelinePath").strip()
318
- timeline_path = (project_root / timeline_relative) if timeline_relative else (task_root / "history" / "timeline.json")
328
+ timeline_path = (
329
+ (project_root / timeline_relative)
330
+ if timeline_relative
331
+ else (task_root / "history" / "timeline.json")
332
+ )
319
333
  latest_run = {}
320
334
  if timeline_path.is_file():
321
335
  try:
@@ -328,43 +342,69 @@ def render_task_catalog_discovery(output_path: str, ctx: dict) -> None:
328
342
  if isinstance(item, dict):
329
343
  latest_run = item
330
344
  break
331
- workflow = manifest.get("workflow") if isinstance(manifest.get("workflow"), dict) else {}
332
- entries.append({
333
- "taskKey": task_key,
334
- "taskGroup": s(manifest, "taskGroup"),
335
- "taskId": s(manifest, "taskId"),
336
- "taskGroupPathSegment": s(manifest, "taskGroupPathSegment"),
337
- "taskIdPathSegment": s(manifest, "taskIdPathSegment"),
338
- "taskType": s(manifest, "taskType"),
339
- "workCategory": s(manifest, "workCategory"),
340
- "currentStatus": s(manifest, "currentStatus"),
341
- "workStatus": s(manifest, "workStatus"),
342
- "workStatusUpdatedAt": s(manifest, "workStatusUpdatedAt"),
343
- "workStatusNote": s(manifest, "workStatusNote"),
344
- "updatedAt": s(manifest, "updatedAt"),
345
- "currentPhase": (workflow or {}).get("currentPhase", "") if isinstance(workflow, dict) else "",
346
- "currentPhaseState": (workflow or {}).get("currentPhaseState", "") if isinstance(workflow, dict) else "",
347
- "lastCompletedPhase": (workflow or {}).get("lastCompletedPhase", "") if isinstance(workflow, dict) else "",
348
- "nextRecommendedPhase": (workflow or {}).get("nextRecommendedPhase", "") if isinstance(workflow, dict) else "",
349
- "awaitingApproval": (workflow or {}).get("awaitingApproval", False) if isinstance(workflow, dict) else False,
350
- "routingStatus": (workflow or {}).get("routingStatus", "") if isinstance(workflow, dict) else "",
351
- "taskRootPath": s(manifest, "taskRootPath") or rel(task_root),
352
- "taskManifestPath": s(manifest, "taskManifestPath") or rel(manifest_path),
353
- "taskIndexPath": s(manifest, "taskIndexPath"),
354
- "instructionSetPath": s(manifest, "instructionSetPath"),
355
- "referenceExpectationsPath": s(manifest, "referenceExpectationsPath"),
356
- "taskBriefPath": s(manifest, "taskBriefPath"),
357
- "latestRunPath": s(manifest, "latestRunPath") or s(latest_run, "runDirectoryPath"),
358
- "latestRunManifestPath": s(latest_run, "runManifestPath"),
359
- "latestRunPromptsPath": s(manifest, "latestRunPromptsPath") or s(latest_run, "workerPromptDirectoryPath"),
360
- "latestPromptSnapshotPath": s(latest_run, "promptSnapshotPath"),
361
- "latestTeamStatePath": s(latest_run, "teamStatePath"),
362
- "latestRunStatus": s(manifest, "latestRunStatus") or s(latest_run, "status"),
363
- "latestReportPath": s(manifest, "latestReportPath") or s(latest_run, "reportPath"),
364
- "latestResumeCommandPath": s(manifest, "latestResumeCommandPath") or s(latest_run, "resumeCommandPath"),
365
- "historyTimelinePath": timeline_relative or rel(timeline_path),
366
- })
367
- entries.sort(key=lambda x: (x.get("updatedAt", ""), x.get("taskKey", "")), reverse=True)
345
+ workflow = (
346
+ manifest.get("workflow")
347
+ if isinstance(manifest.get("workflow"), dict)
348
+ else {}
349
+ )
350
+ entries.append(
351
+ {
352
+ "taskKey": task_key,
353
+ "taskGroup": s(manifest, "taskGroup"),
354
+ "taskId": s(manifest, "taskId"),
355
+ "taskGroupPathSegment": s(manifest, "taskGroupPathSegment"),
356
+ "taskIdPathSegment": s(manifest, "taskIdPathSegment"),
357
+ "taskType": s(manifest, "taskType"),
358
+ "workCategory": s(manifest, "workCategory"),
359
+ "currentStatus": s(manifest, "currentStatus"),
360
+ "workStatus": s(manifest, "workStatus"),
361
+ "workStatusUpdatedAt": s(manifest, "workStatusUpdatedAt"),
362
+ "workStatusNote": s(manifest, "workStatusNote"),
363
+ "updatedAt": s(manifest, "updatedAt"),
364
+ "currentPhase": (workflow or {}).get("currentPhase", "")
365
+ if isinstance(workflow, dict)
366
+ else "",
367
+ "currentPhaseState": (workflow or {}).get("currentPhaseState", "")
368
+ if isinstance(workflow, dict)
369
+ else "",
370
+ "lastCompletedPhase": (workflow or {}).get("lastCompletedPhase", "")
371
+ if isinstance(workflow, dict)
372
+ else "",
373
+ "nextRecommendedPhase": (workflow or {}).get("nextRecommendedPhase", "")
374
+ if isinstance(workflow, dict)
375
+ else "",
376
+ "awaitingApproval": (workflow or {}).get("awaitingApproval", False)
377
+ if isinstance(workflow, dict)
378
+ else False,
379
+ "routingStatus": (workflow or {}).get("routingStatus", "")
380
+ if isinstance(workflow, dict)
381
+ else "",
382
+ "taskRootPath": s(manifest, "taskRootPath") or rel(task_root),
383
+ "taskManifestPath": s(manifest, "taskManifestPath")
384
+ or rel(manifest_path),
385
+ "taskIndexPath": s(manifest, "taskIndexPath"),
386
+ "instructionSetPath": s(manifest, "instructionSetPath"),
387
+ "referenceExpectationsPath": s(manifest, "referenceExpectationsPath"),
388
+ "taskBriefPath": s(manifest, "taskBriefPath"),
389
+ "latestRunPath": s(manifest, "latestRunPath")
390
+ or s(latest_run, "runDirectoryPath"),
391
+ "latestRunManifestPath": s(latest_run, "runManifestPath"),
392
+ "latestRunPromptsPath": s(manifest, "latestRunPromptsPath")
393
+ or s(latest_run, "workerPromptDirectoryPath"),
394
+ "latestPromptSnapshotPath": s(latest_run, "promptSnapshotPath"),
395
+ "latestTeamStatePath": s(latest_run, "teamStatePath"),
396
+ "latestRunStatus": s(manifest, "latestRunStatus")
397
+ or s(latest_run, "status"),
398
+ "latestReportPath": s(manifest, "latestReportPath")
399
+ or s(latest_run, "reportPath"),
400
+ "latestResumeCommandPath": s(manifest, "latestResumeCommandPath")
401
+ or s(latest_run, "resumeCommandPath"),
402
+ "historyTimelinePath": timeline_relative or rel(timeline_path),
403
+ }
404
+ )
405
+ entries.sort(
406
+ key=lambda x: (x.get("updatedAt", ""), x.get("taskKey", "")), reverse=True
407
+ )
368
408
  payload = {
369
409
  "schemaVersion": "1.0",
370
410
  "projectId": ctx.get("PROJECT_ID", ""),
@@ -385,22 +425,34 @@ def render_latest_task_discovery(output_path: str, ctx: dict) -> None:
385
425
  task_manifest = json.loads(task_manifest_path.read_text(encoding="utf-8"))
386
426
  except Exception:
387
427
  task_manifest = {}
388
- workflow = task_manifest.get("workflow") if isinstance(task_manifest.get("workflow"), dict) else {}
428
+ workflow = (
429
+ task_manifest.get("workflow")
430
+ if isinstance(task_manifest.get("workflow"), dict)
431
+ else {}
432
+ )
389
433
  payload = {
390
434
  "schemaVersion": "1.0",
391
435
  "updatedAt": ctx.get("RUN_TIMESTAMP_ISO", ""),
392
436
  "taskKey": ctx.get("TASK_KEY", ""),
393
437
  "taskType": ctx.get("ANALYSIS_TYPE", ""),
394
- "workCategory": task_manifest.get("workCategory", ctx.get("WORKFLOW_WORK_CATEGORY", "unknown")),
395
- "currentStatus": task_manifest.get("currentStatus", ctx.get("CURRENT_TASK_STATUS", "")),
396
- "latestRunStatus": task_manifest.get("latestRunStatus", ctx.get("CURRENT_RUN_STATUS", "")),
438
+ "workCategory": task_manifest.get(
439
+ "workCategory", ctx.get("WORKFLOW_WORK_CATEGORY", "unknown")
440
+ ),
441
+ "currentStatus": task_manifest.get(
442
+ "currentStatus", ctx.get("CURRENT_TASK_STATUS", "")
443
+ ),
444
+ "latestRunStatus": task_manifest.get(
445
+ "latestRunStatus", ctx.get("CURRENT_RUN_STATUS", "")
446
+ ),
397
447
  "workflow": workflow,
398
448
  "taskRootPath": ctx.get("TASK_ROOT_RELATIVE_PATH", ""),
399
449
  "taskManifestPath": ctx.get("TASK_MANIFEST_RELATIVE_PATH", ""),
400
450
  "taskIndexPath": ctx.get("TASK_INDEX_RELATIVE_PATH", ""),
401
451
  "instructionSetPath": ctx.get("INSTRUCTION_SET_RELATIVE_PATH", ""),
402
452
  "taskCatalogPath": ctx.get("OKSTRA_TASK_CATALOG_RELATIVE_PATH", ""),
403
- "referenceExpectationsPath": ctx.get("REFERENCE_EXPECTATIONS_RELATIVE_PATH", ""),
453
+ "referenceExpectationsPath": ctx.get(
454
+ "REFERENCE_EXPECTATIONS_RELATIVE_PATH", ""
455
+ ),
404
456
  "latestRunPath": ctx.get("LATEST_RUN_RELATIVE_PATH", ""),
405
457
  "latestRunManifestPath": ctx.get("RUN_MANIFEST_RELATIVE_PATH", ""),
406
458
  "latestRunPromptsPath": ctx.get("RUN_PROMPTS_RELATIVE_PATH", ""),
@@ -508,8 +560,12 @@ def render_task_manifest(manifest_path: str, ctx: dict) -> None:
508
560
  reviewers = _resolve_workers(ctx)
509
561
  catalog = _worker_catalog(ctx)
510
562
  phase_sequence = [
511
- "requirements-discovery", "error-analysis", "implementation-planning",
512
- "implementation", "final-verification", "release-handoff",
563
+ "requirements-discovery",
564
+ "error-analysis",
565
+ "implementation-planning",
566
+ "implementation",
567
+ "final-verification",
568
+ "release-handoff",
513
569
  ]
514
570
  default_next_phase = {
515
571
  "requirements-discovery": "pending-routing-decision",
@@ -521,18 +577,32 @@ def render_task_manifest(manifest_path: str, ctx: dict) -> None:
521
577
  }
522
578
  required_worker_roles = _required_worker_roles(ctx, reviewers)
523
579
  worker_prompt_paths = {item: catalog[item]["promptPath"] for item in reviewers}
524
- required_agent_status_entries = ["Claude lead"] + [catalog[item]["role"] for item in reviewers]
580
+ required_agent_status_entries = ["Claude lead"] + [
581
+ catalog[item]["role"] for item in reviewers
582
+ ]
525
583
  related_tasks = json.loads(ctx.get("RELATED_TASKS_JSON", "[]"))
526
- current_report_relative = ctx.get("LATEST_REPORT_RELATIVE_PATH") or ctx.get("FINAL_REPORT_RELATIVE_PATH", "")
527
- workflow = existing.get("workflow", {}) if isinstance(existing.get("workflow"), dict) else {}
528
- phase_states = workflow.get("phaseStates", {}) if isinstance(workflow.get("phaseStates"), dict) else {}
584
+ current_report_relative = ctx.get("LATEST_REPORT_RELATIVE_PATH") or ctx.get(
585
+ "FINAL_REPORT_RELATIVE_PATH", ""
586
+ )
587
+ workflow = (
588
+ existing.get("workflow", {})
589
+ if isinstance(existing.get("workflow"), dict)
590
+ else {}
591
+ )
592
+ phase_states = (
593
+ workflow.get("phaseStates", {})
594
+ if isinstance(workflow.get("phaseStates"), dict)
595
+ else {}
596
+ )
529
597
  current_phase = ctx.get("WORKFLOW_CURRENT_PHASE", ctx.get("ANALYSIS_TYPE", ""))
530
598
  current_phase_state = ctx.get("WORKFLOW_CURRENT_PHASE_STATE", "not-started")
531
599
  for phase in phase_sequence:
532
600
  phase_states.setdefault(phase, "not-started")
533
601
  if current_phase:
534
602
  phase_states[current_phase] = current_phase_state
535
- work_category = existing.get("workCategory") or ctx.get("WORKFLOW_WORK_CATEGORY", "unknown")
603
+ work_category = existing.get("workCategory") or ctx.get(
604
+ "WORKFLOW_WORK_CATEGORY", "unknown"
605
+ )
536
606
  # Compute the canonical next phase from current_phase deterministically.
537
607
  # Only preserve `existing.workflow.nextRecommendedPhase` when it is a
538
608
  # legitimate forward pointer — i.e. NOT equal to `current_phase` itself
@@ -546,31 +616,63 @@ def render_task_manifest(manifest_path: str, ctx: dict) -> None:
546
616
  next_recommended_phase = existing_next
547
617
  else:
548
618
  next_recommended_phase = canonical_next
549
- last_completed_phase = workflow.get("lastCompletedPhase") or ctx.get("WORKFLOW_LAST_COMPLETED_PHASE", "")
550
- routing_status = workflow.get("routingStatus") or ctx.get("WORKFLOW_ROUTING_STATUS", "not-applicable")
619
+ last_completed_phase = workflow.get("lastCompletedPhase") or ctx.get(
620
+ "WORKFLOW_LAST_COMPLETED_PHASE", ""
621
+ )
622
+ routing_status = workflow.get("routingStatus") or ctx.get(
623
+ "WORKFLOW_ROUTING_STATUS", "not-applicable"
624
+ )
551
625
  awaiting_approval = workflow.get("awaitingApproval")
552
626
  if not isinstance(awaiting_approval, bool):
553
627
  awaiting_approval = ctx.get("WORKFLOW_AWAITING_APPROVAL", "false") == "true"
554
628
  render_only = ctx.get("RENDER_ONLY", "") == "true"
555
- existing_checkpoint = existing.get("workflow", {}).get("lastSafeCheckpoint", {}) if isinstance(existing.get("workflow"), dict) else {}
629
+ existing_checkpoint = (
630
+ existing.get("workflow", {}).get("lastSafeCheckpoint", {})
631
+ if isinstance(existing.get("workflow"), dict)
632
+ else {}
633
+ )
556
634
  if not isinstance(existing_checkpoint, dict):
557
635
  existing_checkpoint = {}
558
636
  if render_only:
559
637
  last_safe_checkpoint = {
560
638
  "label": existing_checkpoint.get("label", ""),
561
- "taskManifestPath": existing_checkpoint.get("taskManifestPath", ctx.get("TASK_MANIFEST_RELATIVE_PATH", "")),
562
- "taskIndexPath": existing_checkpoint.get("taskIndexPath", ctx.get("TASK_INDEX_RELATIVE_PATH", "")),
563
- "latestRunPath": existing_checkpoint.get("latestRunPath", existing.get("latestRunPath", "")),
564
- "latestRunManifestPath": existing_checkpoint.get("latestRunManifestPath", ""),
565
- "latestTeamStatePath": existing_checkpoint.get("latestTeamStatePath", existing.get("teamStatePath", "")),
566
- "latestReportPath": existing_checkpoint.get("latestReportPath", existing.get("latestReportPath", "")),
567
- "latestResumeCommandPath": existing_checkpoint.get("latestResumeCommandPath", existing.get("latestResumeCommandPath", "")),
639
+ "taskManifestPath": existing_checkpoint.get(
640
+ "taskManifestPath", ctx.get("TASK_MANIFEST_RELATIVE_PATH", "")
641
+ ),
642
+ "taskIndexPath": existing_checkpoint.get(
643
+ "taskIndexPath", ctx.get("TASK_INDEX_RELATIVE_PATH", "")
644
+ ),
645
+ "latestRunPath": existing_checkpoint.get(
646
+ "latestRunPath", existing.get("latestRunPath", "")
647
+ ),
648
+ "latestRunManifestPath": existing_checkpoint.get(
649
+ "latestRunManifestPath", ""
650
+ ),
651
+ "latestTeamStatePath": existing_checkpoint.get(
652
+ "latestTeamStatePath", existing.get("teamStatePath", "")
653
+ ),
654
+ "latestReportPath": existing_checkpoint.get(
655
+ "latestReportPath", existing.get("latestReportPath", "")
656
+ ),
657
+ "latestResumeCommandPath": existing_checkpoint.get(
658
+ "latestResumeCommandPath", existing.get("latestResumeCommandPath", "")
659
+ ),
568
660
  }
569
- latest_run_relative = existing.get("latestRunPath", "") or ctx.get("LATEST_RUN_RELATIVE_PATH", "")
570
- latest_run_status = existing.get("latestRunStatus", "") or ctx.get("CURRENT_RUN_STATUS", "")
571
- latest_run_prompts_relative = existing.get("latestRunPromptsPath", "") or ctx.get("RUN_PROMPTS_RELATIVE_PATH", "")
572
- latest_report_relative = existing.get("latestReportPath", "") or current_report_relative
573
- latest_team_state_relative = existing.get("teamStatePath", "") or ctx.get("TEAM_STATE_RELATIVE_PATH", "")
661
+ latest_run_relative = existing.get("latestRunPath", "") or ctx.get(
662
+ "LATEST_RUN_RELATIVE_PATH", ""
663
+ )
664
+ latest_run_status = existing.get("latestRunStatus", "") or ctx.get(
665
+ "CURRENT_RUN_STATUS", ""
666
+ )
667
+ latest_run_prompts_relative = existing.get(
668
+ "latestRunPromptsPath", ""
669
+ ) or ctx.get("RUN_PROMPTS_RELATIVE_PATH", "")
670
+ latest_report_relative = (
671
+ existing.get("latestReportPath", "") or current_report_relative
672
+ )
673
+ latest_team_state_relative = existing.get("teamStatePath", "") or ctx.get(
674
+ "TEAM_STATE_RELATIVE_PATH", ""
675
+ )
574
676
  latest_resume_command_relative = existing.get("latestResumeCommandPath", "")
575
677
  else:
576
678
  last_safe_checkpoint = {
@@ -581,14 +683,20 @@ def render_task_manifest(manifest_path: str, ctx: dict) -> None:
581
683
  "latestRunManifestPath": ctx.get("RUN_MANIFEST_RELATIVE_PATH", ""),
582
684
  "latestTeamStatePath": ctx.get("TEAM_STATE_RELATIVE_PATH", ""),
583
685
  "latestReportPath": current_report_relative,
584
- "latestResumeCommandPath": ctx.get("CLAUDE_RESUME_COMMAND_RELATIVE_PATH", ""),
686
+ "latestResumeCommandPath": ctx.get(
687
+ "CLAUDE_RESUME_COMMAND_RELATIVE_PATH", ""
688
+ ),
585
689
  }
586
690
  latest_run_relative = ctx.get("LATEST_RUN_RELATIVE_PATH", "")
587
691
  latest_run_status = ctx.get("CURRENT_RUN_STATUS", "")
588
692
  latest_run_prompts_relative = ctx.get("RUN_PROMPTS_RELATIVE_PATH", "")
589
- latest_report_relative = current_report_relative or existing.get("latestReportPath", "")
693
+ latest_report_relative = current_report_relative or existing.get(
694
+ "latestReportPath", ""
695
+ )
590
696
  latest_team_state_relative = ctx.get("TEAM_STATE_RELATIVE_PATH", "")
591
- latest_resume_command_relative = ctx.get("CLAUDE_RESUME_COMMAND_RELATIVE_PATH", "") or existing.get("latestResumeCommandPath", "")
697
+ latest_resume_command_relative = ctx.get(
698
+ "CLAUDE_RESUME_COMMAND_RELATIVE_PATH", ""
699
+ ) or existing.get("latestResumeCommandPath", "")
592
700
  convergence_block = _build_convergence_block(ctx)
593
701
  payload = {
594
702
  "schemaVersion": "1.0",
@@ -614,7 +722,9 @@ def render_task_manifest(manifest_path: str, ctx: dict) -> None:
614
722
  "historyPath": ctx.get("HISTORY_RELATIVE_PATH", ""),
615
723
  "historyTimelinePath": ctx.get("TIMELINE_RELATIVE_PATH", ""),
616
724
  "taskCatalogPath": ctx.get("OKSTRA_TASK_CATALOG_RELATIVE_PATH", ""),
617
- "referenceExpectationsPath": ctx.get("REFERENCE_EXPECTATIONS_RELATIVE_PATH", ""),
725
+ "referenceExpectationsPath": ctx.get(
726
+ "REFERENCE_EXPECTATIONS_RELATIVE_PATH", ""
727
+ ),
618
728
  "latestRunPath": latest_run_relative,
619
729
  "latestRunStatus": latest_run_status,
620
730
  "latestRunPromptsPath": latest_run_prompts_relative,
@@ -633,15 +743,23 @@ def render_task_manifest(manifest_path: str, ctx: dict) -> None:
633
743
  "lastSafeCheckpoint": last_safe_checkpoint,
634
744
  },
635
745
  "artifacts": {
636
- "analysisProfilePath": ctx.get("INSTRUCTION_SET_RELATIVE_PATH", "") + "/analysis-profile.md",
637
- "analysisMaterialPath": ctx.get("INSTRUCTION_SET_RELATIVE_PATH", "") + "/analysis-material.md",
638
- "taskBriefCopyPath": ctx.get("INSTRUCTION_SET_RELATIVE_PATH", "") + "/task-brief.md",
639
- "referenceExpectationsPath": ctx.get("REFERENCE_EXPECTATIONS_RELATIVE_PATH", ""),
640
- "claudeExecutionPromptPath": ctx.get("INSTRUCTION_SET_RELATIVE_PATH", "") + "/claude-execution-prompt.md",
746
+ "analysisProfilePath": ctx.get("INSTRUCTION_SET_RELATIVE_PATH", "")
747
+ + "/analysis-profile.md",
748
+ "analysisMaterialPath": ctx.get("INSTRUCTION_SET_RELATIVE_PATH", "")
749
+ + "/analysis-material.md",
750
+ "taskBriefCopyPath": ctx.get("INSTRUCTION_SET_RELATIVE_PATH", "")
751
+ + "/task-brief.md",
752
+ "referenceExpectationsPath": ctx.get(
753
+ "REFERENCE_EXPECTATIONS_RELATIVE_PATH", ""
754
+ ),
755
+ "claudeExecutionPromptPath": ctx.get("INSTRUCTION_SET_RELATIVE_PATH", "")
756
+ + "/claude-execution-prompt.md",
641
757
  "leadPromptSnapshotPath": ctx.get("RUN_PROMPT_SNAPSHOT_RELATIVE_PATH", ""),
642
758
  "workerPromptsDirectoryPath": ctx.get("RUN_PROMPTS_RELATIVE_PATH", ""),
643
759
  "workerPromptPathByWorkerId": worker_prompt_paths,
644
- "finalReportTemplatePath": ctx.get("FINAL_REPORT_TEMPLATE_RELATIVE_PATH", ""),
760
+ "finalReportTemplatePath": ctx.get(
761
+ "FINAL_REPORT_TEMPLATE_RELATIVE_PATH", ""
762
+ ),
645
763
  "resumeCommandPath": ctx.get("CLAUDE_RESUME_COMMAND_RELATIVE_PATH", ""),
646
764
  "teamStatePath": ctx.get("TEAM_STATE_RELATIVE_PATH", ""),
647
765
  "workerResultsDirectoryPath": ctx.get("WORKER_RESULTS_RELATIVE_PATH", ""),
@@ -670,20 +788,33 @@ def render_task_manifest(manifest_path: str, ctx: dict) -> None:
670
788
  "disallowLeadSoloAnalysisAsWorkerResult": True,
671
789
  "disallowGenericParallelOnlyExecution": True,
672
790
  "workerOutputSections": [
673
- "Findings", "Missing Information or Assumptions",
674
- "Safe or Reasonable Areas", "Uncertain Points",
791
+ "Findings",
792
+ "Missing Information or Assumptions",
793
+ "Safe or Reasonable Areas",
794
+ "Uncertain Points",
675
795
  "Recommended Next Actions",
676
796
  ],
677
797
  "finalReportSections": [
678
- "Problem or Validation Summary", "Agent Execution Status",
679
- "Cross Verification Result", "Final Verdict",
798
+ "Problem or Validation Summary",
799
+ "Agent Execution Status",
800
+ "Cross Verification Result",
801
+ "Final Verdict",
680
802
  "Evidence and Detailed Analysis",
681
- "Missing Information and Risk", "Recommended Next Actions",
803
+ "Missing Information and Risk",
804
+ "Recommended Next Actions",
682
805
  ],
683
806
  "statusLabels": [
684
- "prepared", "team-created", "workers-dispatched",
685
- "worker-results-collected", "synthesis-written", "in-progress",
686
- "completed", "contract-violated", "timeout", "error", "not-run",
807
+ "prepared",
808
+ "team-created",
809
+ "workers-dispatched",
810
+ "worker-results-collected",
811
+ "synthesis-written",
812
+ "in-progress",
813
+ "completed",
814
+ "contract-violated",
815
+ "timeout",
816
+ "error",
817
+ "not-run",
687
818
  ],
688
819
  },
689
820
  "contractValidation": {
@@ -749,7 +880,11 @@ def render_run_manifest(run_manifest_path: str, ctx: dict) -> None:
749
880
  required_worker_roles = _required_worker_roles(ctx, reviewers)
750
881
  worker_prompt_paths = {item: catalog[item]["promptPath"] for item in reviewers}
751
882
  related_tasks = json.loads(ctx.get("RELATED_TASKS_JSON", "[]"))
752
- workflow = task_manifest.get("workflow", {}) if isinstance(task_manifest.get("workflow"), dict) else {}
883
+ workflow = (
884
+ task_manifest.get("workflow", {})
885
+ if isinstance(task_manifest.get("workflow"), dict)
886
+ else {}
887
+ )
753
888
  payload = {
754
889
  "schemaVersion": "1.0",
755
890
  "okstraVersion": ctx.get("OKSTRA_VERSION", ""),
@@ -758,7 +893,9 @@ def render_run_manifest(run_manifest_path: str, ctx: dict) -> None:
758
893
  "taskId": ctx.get("TASK_ID", ""),
759
894
  "taskKey": ctx.get("TASK_KEY", ""),
760
895
  "taskType": ctx.get("ANALYSIS_TYPE", ""),
761
- "workCategory": task_manifest.get("workCategory", ctx.get("WORKFLOW_WORK_CATEGORY", "unknown")),
896
+ "workCategory": task_manifest.get(
897
+ "workCategory", ctx.get("WORKFLOW_WORK_CATEGORY", "unknown")
898
+ ),
762
899
  "taskBriefPath": ctx.get("BRIEF_RELATIVE_PATH", ""),
763
900
  "relatedTasks": related_tasks,
764
901
  "recommendedWorkers": reviewers,
@@ -766,7 +903,9 @@ def render_run_manifest(run_manifest_path: str, ctx: dict) -> None:
766
903
  "taskManifestPath": ctx.get("TASK_MANIFEST_RELATIVE_PATH", ""),
767
904
  "instructionSetPath": ctx.get("INSTRUCTION_SET_RELATIVE_PATH", ""),
768
905
  "taskCatalogPath": ctx.get("OKSTRA_TASK_CATALOG_RELATIVE_PATH", ""),
769
- "referenceExpectationsPath": ctx.get("REFERENCE_EXPECTATIONS_RELATIVE_PATH", ""),
906
+ "referenceExpectationsPath": ctx.get(
907
+ "REFERENCE_EXPECTATIONS_RELATIVE_PATH", ""
908
+ ),
770
909
  "runDirectoryPath": ctx.get("RUN_DIR_RELATIVE_PATH", ""),
771
910
  "runDateTimeSegment": ctx.get("RUN_DATETIME_SEGMENT", ""),
772
911
  "runSequencesByCategory": {
@@ -792,13 +931,26 @@ def render_run_manifest(run_manifest_path: str, ctx: dict) -> None:
792
931
  "resumeCommandPath": ctx.get("CLAUDE_RESUME_COMMAND_RELATIVE_PATH", ""),
793
932
  "workflowSnapshot": {
794
933
  "phaseSequence": workflow.get("phaseSequence", []),
795
- "currentPhase": workflow.get("currentPhase", ctx.get("WORKFLOW_CURRENT_PHASE", "")),
796
- "currentPhaseState": workflow.get("currentPhaseState", ctx.get("WORKFLOW_CURRENT_PHASE_STATE", "")),
934
+ "currentPhase": workflow.get(
935
+ "currentPhase", ctx.get("WORKFLOW_CURRENT_PHASE", "")
936
+ ),
937
+ "currentPhaseState": workflow.get(
938
+ "currentPhaseState", ctx.get("WORKFLOW_CURRENT_PHASE_STATE", "")
939
+ ),
797
940
  "phaseStates": workflow.get("phaseStates", {}),
798
- "lastCompletedPhase": workflow.get("lastCompletedPhase", ctx.get("WORKFLOW_LAST_COMPLETED_PHASE", "")),
799
- "nextRecommendedPhase": workflow.get("nextRecommendedPhase", ctx.get("WORKFLOW_NEXT_RECOMMENDED_PHASE", "")),
800
- "awaitingApproval": workflow.get("awaitingApproval", ctx.get("WORKFLOW_AWAITING_APPROVAL", "false") == "true"),
801
- "routingStatus": workflow.get("routingStatus", ctx.get("WORKFLOW_ROUTING_STATUS", "")),
941
+ "lastCompletedPhase": workflow.get(
942
+ "lastCompletedPhase", ctx.get("WORKFLOW_LAST_COMPLETED_PHASE", "")
943
+ ),
944
+ "nextRecommendedPhase": workflow.get(
945
+ "nextRecommendedPhase", ctx.get("WORKFLOW_NEXT_RECOMMENDED_PHASE", "")
946
+ ),
947
+ "awaitingApproval": workflow.get(
948
+ "awaitingApproval",
949
+ ctx.get("WORKFLOW_AWAITING_APPROVAL", "false") == "true",
950
+ ),
951
+ "routingStatus": workflow.get(
952
+ "routingStatus", ctx.get("WORKFLOW_ROUTING_STATUS", "")
953
+ ),
802
954
  "lastSafeCheckpoint": workflow.get("lastSafeCheckpoint", {}),
803
955
  },
804
956
  "teamContract": {
@@ -810,7 +962,8 @@ def render_run_manifest(run_manifest_path: str, ctx: dict) -> None:
810
962
  "finalSynthesisOwner": "Claude lead",
811
963
  "requiredWorkerAttempts": reviewers,
812
964
  "requiredWorkerRoles": required_worker_roles,
813
- "requiredAgentStatusEntries": ["Claude lead"] + [catalog[item]["role"] for item in reviewers],
965
+ "requiredAgentStatusEntries": ["Claude lead"]
966
+ + [catalog[item]["role"] for item in reviewers],
814
967
  "requireDistinctLeadFromClaudeWorker": True,
815
968
  "requireAllRequiredWorkerAttempts": True,
816
969
  "requireGeminiWorkerAttempt": "gemini" in reviewers,
@@ -867,49 +1020,61 @@ def render_timeline(timeline_path: str, ctx: dict) -> None:
867
1020
  current_run_manifest_path = ctx.get("RUN_MANIFEST_FILE", "")
868
1021
  current_run_manifest_relative_path = ctx.get("RUN_MANIFEST_RELATIVE_PATH", "")
869
1022
  filtered = [
870
- item for item in runs
1023
+ item
1024
+ for item in runs
871
1025
  if item.get("runManifestPath") != current_run_manifest_relative_path
872
1026
  and item.get("runManifestPath") != current_run_manifest_path
873
1027
  ]
874
- workflow = task_manifest.get("workflow") if isinstance(task_manifest.get("workflow"), dict) else {}
1028
+ workflow = (
1029
+ task_manifest.get("workflow")
1030
+ if isinstance(task_manifest.get("workflow"), dict)
1031
+ else {}
1032
+ )
875
1033
  workflow = workflow or {}
876
- filtered.append({
877
- "runTimestamp": ctx.get("RUN_TIMESTAMP_ISO", ""),
878
- "runDirectoryPath": ctx.get("RUN_DIR_RELATIVE_PATH", ""),
879
- "runManifestPath": ctx.get("RUN_MANIFEST_RELATIVE_PATH", ""),
880
- "runDateTimeSegment": ctx.get("RUN_DATETIME_SEGMENT", ""),
881
- "runSequencesByCategory": {
882
- "manifests": ctx.get("RUN_MANIFESTS_SEQ", ""),
883
- "prompts": ctx.get("RUN_PROMPTS_SEQ", ""),
884
- "reports": ctx.get("RUN_REPORTS_SEQ", ""),
885
- "status": ctx.get("RUN_STATUS_SEQ", ""),
886
- "state": ctx.get("RUN_STATE_SEQ", ""),
887
- "sessions": ctx.get("RUN_SESSIONS_SEQ", ""),
888
- "workerResults": ctx.get("WORKER_RESULTS_SEQ", ""),
889
- },
890
- "taskType": ctx.get("ANALYSIS_TYPE", ""),
891
- "workCategory": task_manifest.get("workCategory", ctx.get("WORKFLOW_WORK_CATEGORY", "unknown")),
892
- "status": ctx.get("CURRENT_RUN_STATUS", ""),
893
- "taskBriefPath": ctx.get("BRIEF_RELATIVE_PATH", ""),
894
- "promptSnapshotPath": ctx.get("RUN_PROMPT_SNAPSHOT_RELATIVE_PATH", ""),
895
- "workerPromptDirectoryPath": ctx.get("RUN_PROMPTS_RELATIVE_PATH", ""),
896
- "workerPromptPathByWorkerId": {item: worker_prompt_paths[item] for item in reviewers},
897
- "reportPath": ctx.get("LATEST_REPORT_RELATIVE_PATH") or ctx.get("FINAL_REPORT_RELATIVE_PATH", ""),
898
- "teamStatePath": ctx.get("TEAM_STATE_RELATIVE_PATH", ""),
899
- "resumeCommandPath": ctx.get("CLAUDE_RESUME_COMMAND_RELATIVE_PATH", ""),
900
- "relatedTasks": json.loads(ctx.get("RELATED_TASKS_JSON", "[]")),
901
- "workflowSnapshot": {
902
- "phaseSequence": workflow.get("phaseSequence", []),
903
- "currentPhase": workflow.get("currentPhase", ""),
904
- "currentPhaseState": workflow.get("currentPhaseState", ""),
905
- "phaseStates": workflow.get("phaseStates", {}),
906
- "lastCompletedPhase": workflow.get("lastCompletedPhase", ""),
907
- "nextRecommendedPhase": workflow.get("nextRecommendedPhase", ""),
908
- "awaitingApproval": workflow.get("awaitingApproval", False),
909
- "routingStatus": workflow.get("routingStatus", ""),
910
- "lastSafeCheckpoint": workflow.get("lastSafeCheckpoint", {}),
911
- },
912
- })
1034
+ filtered.append(
1035
+ {
1036
+ "runTimestamp": ctx.get("RUN_TIMESTAMP_ISO", ""),
1037
+ "runDirectoryPath": ctx.get("RUN_DIR_RELATIVE_PATH", ""),
1038
+ "runManifestPath": ctx.get("RUN_MANIFEST_RELATIVE_PATH", ""),
1039
+ "runDateTimeSegment": ctx.get("RUN_DATETIME_SEGMENT", ""),
1040
+ "runSequencesByCategory": {
1041
+ "manifests": ctx.get("RUN_MANIFESTS_SEQ", ""),
1042
+ "prompts": ctx.get("RUN_PROMPTS_SEQ", ""),
1043
+ "reports": ctx.get("RUN_REPORTS_SEQ", ""),
1044
+ "status": ctx.get("RUN_STATUS_SEQ", ""),
1045
+ "state": ctx.get("RUN_STATE_SEQ", ""),
1046
+ "sessions": ctx.get("RUN_SESSIONS_SEQ", ""),
1047
+ "workerResults": ctx.get("WORKER_RESULTS_SEQ", ""),
1048
+ },
1049
+ "taskType": ctx.get("ANALYSIS_TYPE", ""),
1050
+ "workCategory": task_manifest.get(
1051
+ "workCategory", ctx.get("WORKFLOW_WORK_CATEGORY", "unknown")
1052
+ ),
1053
+ "status": ctx.get("CURRENT_RUN_STATUS", ""),
1054
+ "taskBriefPath": ctx.get("BRIEF_RELATIVE_PATH", ""),
1055
+ "promptSnapshotPath": ctx.get("RUN_PROMPT_SNAPSHOT_RELATIVE_PATH", ""),
1056
+ "workerPromptDirectoryPath": ctx.get("RUN_PROMPTS_RELATIVE_PATH", ""),
1057
+ "workerPromptPathByWorkerId": {
1058
+ item: worker_prompt_paths[item] for item in reviewers
1059
+ },
1060
+ "reportPath": ctx.get("LATEST_REPORT_RELATIVE_PATH")
1061
+ or ctx.get("FINAL_REPORT_RELATIVE_PATH", ""),
1062
+ "teamStatePath": ctx.get("TEAM_STATE_RELATIVE_PATH", ""),
1063
+ "resumeCommandPath": ctx.get("CLAUDE_RESUME_COMMAND_RELATIVE_PATH", ""),
1064
+ "relatedTasks": json.loads(ctx.get("RELATED_TASKS_JSON", "[]")),
1065
+ "workflowSnapshot": {
1066
+ "phaseSequence": workflow.get("phaseSequence", []),
1067
+ "currentPhase": workflow.get("currentPhase", ""),
1068
+ "currentPhaseState": workflow.get("currentPhaseState", ""),
1069
+ "phaseStates": workflow.get("phaseStates", {}),
1070
+ "lastCompletedPhase": workflow.get("lastCompletedPhase", ""),
1071
+ "nextRecommendedPhase": workflow.get("nextRecommendedPhase", ""),
1072
+ "awaitingApproval": workflow.get("awaitingApproval", False),
1073
+ "routingStatus": workflow.get("routingStatus", ""),
1074
+ "lastSafeCheckpoint": workflow.get("lastSafeCheckpoint", {}),
1075
+ },
1076
+ }
1077
+ )
913
1078
  payload = {
914
1079
  "schemaVersion": "1.0",
915
1080
  "projectId": ctx.get("PROJECT_ID", ""),
@@ -930,20 +1095,35 @@ def render_task_index(template_path: str, output_path: str, ctx: dict) -> None:
930
1095
  task_manifest = json.loads(task_manifest_path.read_text(encoding="utf-8"))
931
1096
  except Exception:
932
1097
  task_manifest = {}
933
- workflow = task_manifest.get("workflow", {}) if isinstance(task_manifest.get("workflow"), dict) else {}
934
- phase_states = workflow.get("phaseStates", {}) if isinstance(workflow.get("phaseStates"), dict) else {}
1098
+ workflow = (
1099
+ task_manifest.get("workflow", {})
1100
+ if isinstance(task_manifest.get("workflow"), dict)
1101
+ else {}
1102
+ )
1103
+ phase_states = (
1104
+ workflow.get("phaseStates", {})
1105
+ if isinstance(workflow.get("phaseStates"), dict)
1106
+ else {}
1107
+ )
935
1108
  phase_order = workflow.get("phaseSequence", [])
936
1109
  if not isinstance(phase_order, list) or not phase_order:
937
1110
  phase_order = [
938
- "requirements-discovery", "error-analysis",
939
- "implementation-planning", "implementation", "final-verification",
1111
+ "requirements-discovery",
1112
+ "error-analysis",
1113
+ "implementation-planning",
1114
+ "implementation",
1115
+ "final-verification",
940
1116
  "release-handoff",
941
1117
  ]
942
1118
  phase_state_lines = [
943
1119
  f"- `{phase}`: `{phase_states.get(phase, 'not-started')}`"
944
1120
  for phase in phase_order
945
1121
  ]
946
- checkpoint = workflow.get("lastSafeCheckpoint", {}) if isinstance(workflow.get("lastSafeCheckpoint"), dict) else {}
1122
+ checkpoint = (
1123
+ workflow.get("lastSafeCheckpoint", {})
1124
+ if isinstance(workflow.get("lastSafeCheckpoint"), dict)
1125
+ else {}
1126
+ )
947
1127
  checkpoint_lines = [
948
1128
  f"- Label: `{checkpoint.get('label', 'unknown')}`",
949
1129
  f"- Run manifest: `{checkpoint.get('latestRunManifestPath', ctx.get('RUN_MANIFEST_RELATIVE_PATH', ''))}`",
@@ -951,9 +1131,21 @@ def render_task_index(template_path: str, output_path: str, ctx: dict) -> None:
951
1131
  f"- Report: `{checkpoint.get('latestReportPath', task_manifest.get('latestReportPath', '--')) or '--'}`",
952
1132
  f"- Resume command: `{checkpoint.get('latestResumeCommandPath', task_manifest.get('latestResumeCommandPath', '--')) or '--'}`",
953
1133
  ]
954
- rc = task_manifest.get("resultContract") if isinstance(task_manifest.get("resultContract"), dict) else {}
955
- cv = task_manifest.get("contractValidation") if isinstance(task_manifest.get("contractValidation"), dict) else {}
956
- art = task_manifest.get("artifacts") if isinstance(task_manifest.get("artifacts"), dict) else {}
1134
+ rc = (
1135
+ task_manifest.get("resultContract")
1136
+ if isinstance(task_manifest.get("resultContract"), dict)
1137
+ else {}
1138
+ )
1139
+ cv = (
1140
+ task_manifest.get("contractValidation")
1141
+ if isinstance(task_manifest.get("contractValidation"), dict)
1142
+ else {}
1143
+ )
1144
+ art = (
1145
+ task_manifest.get("artifacts")
1146
+ if isinstance(task_manifest.get("artifacts"), dict)
1147
+ else {}
1148
+ )
957
1149
  mapping = {
958
1150
  "{{TASK_KEY}}": task_manifest.get("taskKey", ctx.get("TASK_KEY", "")),
959
1151
  "{{TASK_TYPE}}": task_manifest.get("taskType", ctx.get("ANALYSIS_TYPE", "")),
@@ -961,44 +1153,96 @@ def render_task_index(template_path: str, output_path: str, ctx: dict) -> None:
961
1153
  "{{PROJECT_ID}}": ctx.get("PROJECT_ID", ""),
962
1154
  "{{TASK_GROUP}}": ctx.get("TASK_GROUP", ""),
963
1155
  "{{TASK_ID}}": ctx.get("TASK_ID", ""),
964
- "{{CURRENT_TASK_STATUS}}": task_manifest.get("currentStatus", ctx.get("CURRENT_TASK_STATUS", "")),
965
- "{{CURRENT_RUN_STATUS}}": task_manifest.get("latestRunStatus", ctx.get("CURRENT_RUN_STATUS", "")),
1156
+ "{{CURRENT_TASK_STATUS}}": task_manifest.get(
1157
+ "currentStatus", ctx.get("CURRENT_TASK_STATUS", "")
1158
+ ),
1159
+ "{{CURRENT_RUN_STATUS}}": task_manifest.get(
1160
+ "latestRunStatus", ctx.get("CURRENT_RUN_STATUS", "")
1161
+ ),
966
1162
  "{{RELATED_TASKS_INLINE}}": ctx.get("RELATED_TASKS_INLINE", "None"),
967
- "{{RECOMMENDED_ANALYSERS}}": ", ".join(task_manifest.get("recommendedWorkers", [])),
1163
+ "{{RECOMMENDED_ANALYSERS}}": ", ".join(
1164
+ task_manifest.get("recommendedWorkers", [])
1165
+ ),
968
1166
  "{{LEAD_MODEL}}": rc.get("leadModel", ctx.get("LEAD_MODEL_DISPLAY", "")),
969
1167
  "{{OKSTRA_VERSION}}": ctx.get("OKSTRA_VERSION", ""),
970
- "{{LATEST_RUN_RELATIVE_PATH}}": task_manifest.get("latestRunPath", ctx.get("LATEST_RUN_RELATIVE_PATH", "")),
971
- "{{LATEST_REPORT_RELATIVE_PATH}}": task_manifest.get("latestReportPath", ctx.get("LATEST_REPORT_RELATIVE_PATH", "")),
972
- "{{TEAM_STATE_RELATIVE_PATH}}": task_manifest.get("teamStatePath", ctx.get("TEAM_STATE_RELATIVE_PATH", "")),
973
- "{{VALIDATION_STATUS}}": cv.get("status", ctx.get("VALIDATION_STATUS", "not-run")),
974
- "{{CLAUDE_RESUME_COMMAND_RELATIVE_PATH}}": task_manifest.get("latestResumeCommandPath", ctx.get("CLAUDE_RESUME_COMMAND_RELATIVE_PATH", "")),
975
- "{{MODEL_ASSIGNMENT_LINES}}": "\n".join([
976
- f"- `Claude lead`: `{rc.get('leadModel', ctx.get('LEAD_MODEL_DISPLAY', ''))}`",
977
- f"- `Claude worker`: `{ctx.get('CLAUDE_WORKER_MODEL_DISPLAY', '')}`",
978
- f"- `Codex worker`: `{ctx.get('CODEX_WORKER_MODEL_DISPLAY', '')}`",
979
- f"- `Gemini worker`: `{ctx.get('GEMINI_WORKER_MODEL_DISPLAY', '')}`",
980
- f"- `Report writer worker`: `{ctx.get('REPORT_WRITER_MODEL_DISPLAY', '')}`",
981
- ]),
982
- "{{TASK_MANIFEST_RELATIVE_PATH}}": task_manifest.get("taskManifestPath", ctx.get("TASK_MANIFEST_RELATIVE_PATH", "")),
983
- "{{OKSTRA_LATEST_TASK_RELATIVE_PATH}}": ctx.get("OKSTRA_LATEST_TASK_RELATIVE_PATH", ""),
984
- "{{INSTRUCTION_SET_RELATIVE_PATH}}": task_manifest.get("instructionSetPath", ctx.get("INSTRUCTION_SET_RELATIVE_PATH", "")),
985
- "{{REFERENCE_EXPECTATIONS_RELATIVE_PATH}}": task_manifest.get("referenceExpectationsPath", ctx.get("REFERENCE_EXPECTATIONS_RELATIVE_PATH", "")),
986
- "{{FINAL_REPORT_TEMPLATE_RELATIVE_PATH}}": art.get("finalReportTemplatePath", ctx.get("FINAL_REPORT_TEMPLATE_RELATIVE_PATH", "")),
1168
+ "{{LATEST_RUN_RELATIVE_PATH}}": task_manifest.get(
1169
+ "latestRunPath", ctx.get("LATEST_RUN_RELATIVE_PATH", "")
1170
+ ),
1171
+ "{{LATEST_REPORT_RELATIVE_PATH}}": task_manifest.get(
1172
+ "latestReportPath", ctx.get("LATEST_REPORT_RELATIVE_PATH", "")
1173
+ ),
1174
+ "{{TEAM_STATE_RELATIVE_PATH}}": task_manifest.get(
1175
+ "teamStatePath", ctx.get("TEAM_STATE_RELATIVE_PATH", "")
1176
+ ),
1177
+ "{{VALIDATION_STATUS}}": cv.get(
1178
+ "status", ctx.get("VALIDATION_STATUS", "not-run")
1179
+ ),
1180
+ "{{CLAUDE_RESUME_COMMAND_RELATIVE_PATH}}": task_manifest.get(
1181
+ "latestResumeCommandPath",
1182
+ ctx.get("CLAUDE_RESUME_COMMAND_RELATIVE_PATH", ""),
1183
+ ),
1184
+ "{{MODEL_ASSIGNMENT_LINES}}": "\n".join(
1185
+ [
1186
+ f"- `Claude lead`: `{rc.get('leadModel', ctx.get('LEAD_MODEL_DISPLAY', ''))}`",
1187
+ f"- `Claude worker`: `{ctx.get('CLAUDE_WORKER_MODEL_DISPLAY', '')}`",
1188
+ f"- `Codex worker`: `{ctx.get('CODEX_WORKER_MODEL_DISPLAY', '')}`",
1189
+ f"- `Gemini worker`: `{ctx.get('GEMINI_WORKER_MODEL_DISPLAY', '')}`",
1190
+ f"- `Report writer worker`: `{ctx.get('REPORT_WRITER_MODEL_DISPLAY', '')}`",
1191
+ ]
1192
+ ),
1193
+ "{{TASK_MANIFEST_RELATIVE_PATH}}": task_manifest.get(
1194
+ "taskManifestPath", ctx.get("TASK_MANIFEST_RELATIVE_PATH", "")
1195
+ ),
1196
+ "{{OKSTRA_LATEST_TASK_RELATIVE_PATH}}": ctx.get(
1197
+ "OKSTRA_LATEST_TASK_RELATIVE_PATH", ""
1198
+ ),
1199
+ "{{INSTRUCTION_SET_RELATIVE_PATH}}": task_manifest.get(
1200
+ "instructionSetPath", ctx.get("INSTRUCTION_SET_RELATIVE_PATH", "")
1201
+ ),
1202
+ "{{REFERENCE_EXPECTATIONS_RELATIVE_PATH}}": task_manifest.get(
1203
+ "referenceExpectationsPath",
1204
+ ctx.get("REFERENCE_EXPECTATIONS_RELATIVE_PATH", ""),
1205
+ ),
1206
+ "{{FINAL_REPORT_TEMPLATE_RELATIVE_PATH}}": art.get(
1207
+ "finalReportTemplatePath",
1208
+ ctx.get("FINAL_REPORT_TEMPLATE_RELATIVE_PATH", ""),
1209
+ ),
987
1210
  "{{RUN_MANIFESTS_RELATIVE_PATH}}": ctx.get("RUN_MANIFESTS_RELATIVE_PATH", ""),
988
1211
  "{{RUN_STATE_RELATIVE_PATH}}": ctx.get("RUN_STATE_RELATIVE_PATH", ""),
989
- "{{RUN_PROMPTS_RELATIVE_PATH}}": task_manifest.get("latestRunPromptsPath", ctx.get("RUN_PROMPTS_RELATIVE_PATH", "")),
1212
+ "{{RUN_PROMPTS_RELATIVE_PATH}}": task_manifest.get(
1213
+ "latestRunPromptsPath", ctx.get("RUN_PROMPTS_RELATIVE_PATH", "")
1214
+ ),
990
1215
  "{{RUN_REPORTS_RELATIVE_PATH}}": ctx.get("RUN_REPORTS_RELATIVE_PATH", ""),
991
1216
  "{{RUN_STATUS_RELATIVE_PATH}}": ctx.get("RUN_STATUS_RELATIVE_PATH", ""),
992
1217
  "{{RUN_SESSIONS_RELATIVE_PATH}}": ctx.get("RUN_SESSIONS_RELATIVE_PATH", ""),
993
- "{{WORKER_RESULTS_RELATIVE_PATH}}": art.get("workerResultsDirectoryPath", ctx.get("WORKER_RESULTS_RELATIVE_PATH", "")),
994
- "{{RUN_VALIDATOR_RELATIVE_PATH}}": cv.get("validatorScriptPath", ctx.get("RUN_VALIDATOR_RELATIVE_PATH", "")),
995
- "{{WORK_CATEGORY}}": task_manifest.get("workCategory", ctx.get("WORKFLOW_WORK_CATEGORY", "unknown")),
996
- "{{WORKFLOW_CURRENT_PHASE}}": workflow.get("currentPhase", ctx.get("WORKFLOW_CURRENT_PHASE", "")),
997
- "{{WORKFLOW_CURRENT_PHASE_STATE}}": workflow.get("currentPhaseState", ctx.get("WORKFLOW_CURRENT_PHASE_STATE", "")),
998
- "{{WORKFLOW_LAST_COMPLETED_PHASE}}": workflow.get("lastCompletedPhase", ctx.get("WORKFLOW_LAST_COMPLETED_PHASE", "")) or "--",
999
- "{{WORKFLOW_NEXT_RECOMMENDED_PHASE}}": workflow.get("nextRecommendedPhase", ctx.get("WORKFLOW_NEXT_RECOMMENDED_PHASE", "")),
1000
- "{{WORKFLOW_AWAITING_APPROVAL}}": "yes" if workflow.get("awaitingApproval", False) else "no",
1001
- "{{WORKFLOW_ROUTING_STATUS}}": workflow.get("routingStatus", ctx.get("WORKFLOW_ROUTING_STATUS", "")),
1218
+ "{{WORKER_RESULTS_RELATIVE_PATH}}": art.get(
1219
+ "workerResultsDirectoryPath", ctx.get("WORKER_RESULTS_RELATIVE_PATH", "")
1220
+ ),
1221
+ "{{RUN_VALIDATOR_RELATIVE_PATH}}": cv.get(
1222
+ "validatorScriptPath", ctx.get("RUN_VALIDATOR_RELATIVE_PATH", "")
1223
+ ),
1224
+ "{{WORK_CATEGORY}}": task_manifest.get(
1225
+ "workCategory", ctx.get("WORKFLOW_WORK_CATEGORY", "unknown")
1226
+ ),
1227
+ "{{WORKFLOW_CURRENT_PHASE}}": workflow.get(
1228
+ "currentPhase", ctx.get("WORKFLOW_CURRENT_PHASE", "")
1229
+ ),
1230
+ "{{WORKFLOW_CURRENT_PHASE_STATE}}": workflow.get(
1231
+ "currentPhaseState", ctx.get("WORKFLOW_CURRENT_PHASE_STATE", "")
1232
+ ),
1233
+ "{{WORKFLOW_LAST_COMPLETED_PHASE}}": workflow.get(
1234
+ "lastCompletedPhase", ctx.get("WORKFLOW_LAST_COMPLETED_PHASE", "")
1235
+ )
1236
+ or "--",
1237
+ "{{WORKFLOW_NEXT_RECOMMENDED_PHASE}}": workflow.get(
1238
+ "nextRecommendedPhase", ctx.get("WORKFLOW_NEXT_RECOMMENDED_PHASE", "")
1239
+ ),
1240
+ "{{WORKFLOW_AWAITING_APPROVAL}}": "yes"
1241
+ if workflow.get("awaitingApproval", False)
1242
+ else "no",
1243
+ "{{WORKFLOW_ROUTING_STATUS}}": workflow.get(
1244
+ "routingStatus", ctx.get("WORKFLOW_ROUTING_STATUS", "")
1245
+ ),
1002
1246
  "{{WORKFLOW_PHASE_STATE_LINES}}": "\n".join(phase_state_lines),
1003
1247
  "{{WORKFLOW_LAST_SAFE_CHECKPOINT_LINES}}": "\n".join(checkpoint_lines),
1004
1248
  }
@@ -1055,7 +1299,9 @@ def build_available_mcp_servers_block(project_root: Path) -> str:
1055
1299
  if description:
1056
1300
  parts.append(description)
1057
1301
  if isinstance(tools, list) and tools:
1058
- tool_names = ", ".join(f"`{str(t).strip()}`" for t in tools if str(t).strip())
1302
+ tool_names = ", ".join(
1303
+ f"`{str(t).strip()}`" for t in tools if str(t).strip()
1304
+ )
1059
1305
  if tool_names:
1060
1306
  parts.append(f"Tools: {tool_names}")
1061
1307
  if notes:
@@ -1083,7 +1329,9 @@ def render_template_file(template_path: str, output_path: str, ctx: dict) -> Non
1083
1329
 
1084
1330
  worker_result_lines = []
1085
1331
  team_role_lines = [f" 1. `Claude lead` (assigned model: `{lead_model}`)"]
1086
- model_assignment_lines = [fmt_assignment("Claude lead", lead_model, lead_model_execution)]
1332
+ model_assignment_lines = [
1333
+ fmt_assignment("Claude lead", lead_model, lead_model_execution)
1334
+ ]
1087
1335
  worker_role_labels = []
1088
1336
  execution_status_entries = ["`Claude lead`"]
1089
1337
  execution_status_table_lines = [
@@ -1094,24 +1342,34 @@ def render_template_file(template_path: str, output_path: str, ctx: dict) -> Non
1094
1342
  for index, worker in enumerate(selected, start=2):
1095
1343
  m = catalog[worker]
1096
1344
  worker_result_lines.append(
1097
- f"- {m['role']} result path: `{m['resultPath']}` (assigned model: `{m['model']}`)")
1098
- team_role_lines.append(f" {index}. `{m['role']}` (assigned model: `{m['model']}`)")
1099
- model_assignment_lines.append(fmt_assignment(m["role"], m["model"], m["modelExecutionValue"]))
1345
+ f"- {m['role']} result path: `{m['resultPath']}` (assigned model: `{m['model']}`)"
1346
+ )
1347
+ team_role_lines.append(
1348
+ f" {index}. `{m['role']}` (assigned model: `{m['model']}`)"
1349
+ )
1350
+ model_assignment_lines.append(
1351
+ fmt_assignment(m["role"], m["model"], m["modelExecutionValue"])
1352
+ )
1100
1353
  worker_role_labels.append(f"`{m['role']}`")
1101
1354
  execution_status_entries.append(f"`{m['role']}`")
1102
1355
  execution_status_table_lines.append(
1103
- f"| {m['agentLabel']} | {m['role']} | {m['model']} | completed / timeout / error / not-run | {m['role']}의 핵심 발견 요약 |")
1356
+ f"| {m['agentLabel']} | {m['role']} | {m['model']} | completed / timeout / error / not-run | {m['role']}의 핵심 발견 요약 |"
1357
+ )
1104
1358
 
1105
1359
  if worker_role_labels:
1106
1360
  if len(worker_role_labels) == 1:
1107
- worker_role_sentence = f"- {worker_role_labels[0]} is the required worker role."
1361
+ worker_role_sentence = (
1362
+ f"- {worker_role_labels[0]} is the required worker role."
1363
+ )
1108
1364
  else:
1109
1365
  worker_role_sentence = (
1110
1366
  f"- {', '.join(worker_role_labels[:-1])}, "
1111
- f"and {worker_role_labels[-1]} are the required worker roles.")
1367
+ f"and {worker_role_labels[-1]} are the required worker roles."
1368
+ )
1112
1369
  preferred_results_sentence = (
1113
1370
  f"- Aim to collect completed results from all "
1114
- f"{len(worker_role_labels)} required workers.")
1371
+ f"{len(worker_role_labels)} required workers."
1372
+ )
1115
1373
  else:
1116
1374
  worker_role_sentence = "- No worker roles were selected for this run."
1117
1375
  preferred_results_sentence = "- No worker results are expected for this run."
@@ -1144,7 +1402,7 @@ def render_template_file(template_path: str, output_path: str, ctx: dict) -> Non
1144
1402
  "## Team Creation Gate (BLOCKING)\n"
1145
1403
  "\n"
1146
1404
  "Before any `Agent` dispatch for workers, you MUST perform Phase 3 of the\n"
1147
- "`okstra` skill (`agents/SKILL.md` → \"Phase 3 — Team creation\"). Skipping\n"
1405
+ '`okstra` skill (`agents/SKILL.md` → "Phase 3 — Team creation"). Skipping\n'
1148
1406
  "this gate silently degrades the run to in-process background dispatch and\n"
1149
1407
  "loses the Teams split-pane observability surface, even though worker\n"
1150
1408
  "outputs may still appear correct on disk.\n"
@@ -1155,16 +1413,16 @@ def render_template_file(template_path: str, output_path: str, ctx: dict) -> Non
1155
1413
  "\n"
1156
1414
  "1. Invoke the `okstra-team-contract` skill and verify the selected worker\n"
1157
1415
  " roster against `task-manifest.json`'s `resultContract.requiredWorkerRoles`.\n"
1158
- f"2. Call `TeamCreate(team_name: \"okstra-{ctx.get('TASK_KEY', '')}\", description: ...)`.\n"
1416
+ f'2. Call `TeamCreate(team_name: "okstra-{ctx.get("TASK_KEY", "")}", description: ...)`.\n'
1159
1417
  "3. Record the outcome in team-state under\n"
1160
- " `teamCreate: { attempted: true, status: \"ok\" | \"error\", error?: <msg> }`\n"
1418
+ ' `teamCreate: { attempted: true, status: "ok" | "error", error?: <msg> }`\n'
1161
1419
  " BEFORE any `Agent(...)` worker dispatch.\n"
1162
1420
  "4. Only after `teamCreate` is persisted may you dispatch workers — with\n"
1163
1421
  " `team_name` on success, or with `run_in_background: true` and no\n"
1164
- " `team_name` ONLY when `teamCreate.status == \"error\"` was recorded.\n"
1422
+ ' `team_name` ONLY when `teamCreate.status == "error"` was recorded.\n'
1165
1423
  "\n"
1166
- "If the Agent tool rejects a dispatch with `\"team must be created first\"` /\n"
1167
- "`\"team을 먼저 생성하거나 team_name 없이 호출해야 합니다\"`, the correct\n"
1424
+ 'If the Agent tool rejects a dispatch with `"team must be created first"` /\n'
1425
+ '`"team을 먼저 생성하거나 team_name 없이 호출해야 합니다"`, the correct\n'
1168
1426
  "response is to go back to step 2 — NOT to strip `team_name` and retry."
1169
1427
  )
1170
1428
 
@@ -1178,7 +1436,9 @@ def render_template_file(template_path: str, output_path: str, ctx: dict) -> Non
1178
1436
  "{{BRIEF_RELATIVE_PATH}}": ctx.get("BRIEF_RELATIVE_PATH", ""),
1179
1437
  "{{BRIEF_FILE_PATH}}": ctx.get("BRIEF_FILE_PATH", ""),
1180
1438
  "{{CLARIFICATION_RESPONSE_PATH}}": ctx.get("CLARIFICATION_RESPONSE_FILE", ""),
1181
- "{{CLARIFICATION_RESPONSE_RELATIVE_PATH}}": ctx.get("CLARIFICATION_RESPONSE_RELATIVE_PATH", ""),
1439
+ "{{CLARIFICATION_RESPONSE_RELATIVE_PATH}}": ctx.get(
1440
+ "CLARIFICATION_RESPONSE_RELATIVE_PATH", ""
1441
+ ),
1182
1442
  "{{RUN_DIR}}": ctx.get("RUN_DIR", ""),
1183
1443
  "{{RUN_DIR_RELATIVE_PATH}}": ctx.get("RUN_DIR_RELATIVE_PATH", ""),
1184
1444
  "{{RUN_MANIFESTS_RELATIVE_PATH}}": ctx.get("RUN_MANIFESTS_RELATIVE_PATH", ""),
@@ -1205,31 +1465,63 @@ def render_template_file(template_path: str, output_path: str, ctx: dict) -> Non
1205
1465
  "{{WORKER_RESULTS_RELATIVE_PATH}}": ctx.get("WORKER_RESULTS_RELATIVE_PATH", ""),
1206
1466
  "{{RUN_VALIDATOR_PATH}}": ctx.get("RUN_VALIDATOR_SCRIPT", ""),
1207
1467
  "{{RUN_VALIDATOR_RELATIVE_PATH}}": ctx.get("RUN_VALIDATOR_RELATIVE_PATH", ""),
1208
- "{{CLAUDE_WORKER_RESULT_RELATIVE_PATH}}": ctx.get("CLAUDE_WORKER_RESULT_RELATIVE_PATH", ""),
1209
- "{{CODEX_WORKER_RESULT_RELATIVE_PATH}}": ctx.get("CODEX_WORKER_RESULT_RELATIVE_PATH", ""),
1210
- "{{GEMINI_WORKER_RESULT_RELATIVE_PATH}}": ctx.get("GEMINI_WORKER_RESULT_RELATIVE_PATH", ""),
1211
- "{{REPORT_WRITER_WORKER_RESULT_RELATIVE_PATH}}": ctx.get("REPORT_WRITER_WORKER_RESULT_RELATIVE_PATH", ""),
1468
+ "{{CLAUDE_WORKER_RESULT_RELATIVE_PATH}}": ctx.get(
1469
+ "CLAUDE_WORKER_RESULT_RELATIVE_PATH", ""
1470
+ ),
1471
+ "{{CODEX_WORKER_RESULT_RELATIVE_PATH}}": ctx.get(
1472
+ "CODEX_WORKER_RESULT_RELATIVE_PATH", ""
1473
+ ),
1474
+ "{{GEMINI_WORKER_RESULT_RELATIVE_PATH}}": ctx.get(
1475
+ "GEMINI_WORKER_RESULT_RELATIVE_PATH", ""
1476
+ ),
1477
+ "{{REPORT_WRITER_WORKER_RESULT_RELATIVE_PATH}}": ctx.get(
1478
+ "REPORT_WRITER_WORKER_RESULT_RELATIVE_PATH", ""
1479
+ ),
1212
1480
  "{{RUN_ERRORS_LOG_PATH}}": ctx.get("RUN_ERRORS_LOG_FILE", ""),
1213
1481
  "{{RUN_ERRORS_LOG_RELATIVE_PATH}}": ctx.get("RUN_ERRORS_LOG_RELATIVE_PATH", ""),
1214
- "{{CLAUDE_WORKER_ERRORS_SIDECAR_PATH}}": ctx.get("CLAUDE_WORKER_ERRORS_SIDECAR_FILE", ""),
1215
- "{{CLAUDE_WORKER_ERRORS_SIDECAR_RELATIVE_PATH}}": ctx.get("CLAUDE_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", ""),
1216
- "{{CODEX_WORKER_ERRORS_SIDECAR_PATH}}": ctx.get("CODEX_WORKER_ERRORS_SIDECAR_FILE", ""),
1217
- "{{CODEX_WORKER_ERRORS_SIDECAR_RELATIVE_PATH}}": ctx.get("CODEX_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", ""),
1218
- "{{GEMINI_WORKER_ERRORS_SIDECAR_PATH}}": ctx.get("GEMINI_WORKER_ERRORS_SIDECAR_FILE", ""),
1219
- "{{GEMINI_WORKER_ERRORS_SIDECAR_RELATIVE_PATH}}": ctx.get("GEMINI_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", ""),
1220
- "{{REPORT_WRITER_WORKER_ERRORS_SIDECAR_PATH}}": ctx.get("REPORT_WRITER_WORKER_ERRORS_SIDECAR_FILE", ""),
1221
- "{{REPORT_WRITER_WORKER_ERRORS_SIDECAR_RELATIVE_PATH}}": ctx.get("REPORT_WRITER_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", ""),
1482
+ "{{CLAUDE_WORKER_ERRORS_SIDECAR_PATH}}": ctx.get(
1483
+ "CLAUDE_WORKER_ERRORS_SIDECAR_FILE", ""
1484
+ ),
1485
+ "{{CLAUDE_WORKER_ERRORS_SIDECAR_RELATIVE_PATH}}": ctx.get(
1486
+ "CLAUDE_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", ""
1487
+ ),
1488
+ "{{CODEX_WORKER_ERRORS_SIDECAR_PATH}}": ctx.get(
1489
+ "CODEX_WORKER_ERRORS_SIDECAR_FILE", ""
1490
+ ),
1491
+ "{{CODEX_WORKER_ERRORS_SIDECAR_RELATIVE_PATH}}": ctx.get(
1492
+ "CODEX_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", ""
1493
+ ),
1494
+ "{{GEMINI_WORKER_ERRORS_SIDECAR_PATH}}": ctx.get(
1495
+ "GEMINI_WORKER_ERRORS_SIDECAR_FILE", ""
1496
+ ),
1497
+ "{{GEMINI_WORKER_ERRORS_SIDECAR_RELATIVE_PATH}}": ctx.get(
1498
+ "GEMINI_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", ""
1499
+ ),
1500
+ "{{REPORT_WRITER_WORKER_ERRORS_SIDECAR_PATH}}": ctx.get(
1501
+ "REPORT_WRITER_WORKER_ERRORS_SIDECAR_FILE", ""
1502
+ ),
1503
+ "{{REPORT_WRITER_WORKER_ERRORS_SIDECAR_RELATIVE_PATH}}": ctx.get(
1504
+ "REPORT_WRITER_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", ""
1505
+ ),
1222
1506
  "{{LEAD_MODEL}}": lead_model,
1223
1507
  "{{LEAD_MODEL_EXECUTION_VALUE}}": lead_model_execution,
1224
1508
  "{{OKSTRA_VERSION}}": ctx.get("OKSTRA_VERSION", ""),
1225
1509
  "{{CLAUDE_WORKER_MODEL}}": ctx.get("CLAUDE_WORKER_MODEL_DISPLAY", ""),
1226
- "{{CLAUDE_WORKER_MODEL_EXECUTION_VALUE}}": ctx.get("CLAUDE_WORKER_MODEL_EXECUTION_VALUE", ""),
1510
+ "{{CLAUDE_WORKER_MODEL_EXECUTION_VALUE}}": ctx.get(
1511
+ "CLAUDE_WORKER_MODEL_EXECUTION_VALUE", ""
1512
+ ),
1227
1513
  "{{CODEX_WORKER_MODEL}}": ctx.get("CODEX_WORKER_MODEL_DISPLAY", ""),
1228
- "{{CODEX_WORKER_MODEL_EXECUTION_VALUE}}": ctx.get("CODEX_WORKER_MODEL_EXECUTION_VALUE", ""),
1514
+ "{{CODEX_WORKER_MODEL_EXECUTION_VALUE}}": ctx.get(
1515
+ "CODEX_WORKER_MODEL_EXECUTION_VALUE", ""
1516
+ ),
1229
1517
  "{{GEMINI_WORKER_MODEL}}": ctx.get("GEMINI_WORKER_MODEL_DISPLAY", ""),
1230
- "{{GEMINI_WORKER_MODEL_EXECUTION_VALUE}}": ctx.get("GEMINI_WORKER_MODEL_EXECUTION_VALUE", ""),
1518
+ "{{GEMINI_WORKER_MODEL_EXECUTION_VALUE}}": ctx.get(
1519
+ "GEMINI_WORKER_MODEL_EXECUTION_VALUE", ""
1520
+ ),
1231
1521
  "{{REPORT_WRITER_MODEL}}": ctx.get("REPORT_WRITER_MODEL_DISPLAY", ""),
1232
- "{{REPORT_WRITER_MODEL_EXECUTION_VALUE}}": ctx.get("REPORT_WRITER_MODEL_EXECUTION_VALUE", ""),
1522
+ "{{REPORT_WRITER_MODEL_EXECUTION_VALUE}}": ctx.get(
1523
+ "REPORT_WRITER_MODEL_EXECUTION_VALUE", ""
1524
+ ),
1233
1525
  "{{WORKER_RESULT_PATH_LINES}}": "\n".join(worker_result_lines),
1234
1526
  "{{MODEL_ASSIGNMENT_LINES}}": "\n".join(model_assignment_lines),
1235
1527
  "{{TEAM_ROLE_LINES}}": "\n".join(team_role_lines),
@@ -1239,30 +1531,48 @@ def render_template_file(template_path: str, output_path: str, ctx: dict) -> Non
1239
1531
  "{{EXECUTION_STATUS_EXACT_ENTRIES}}": ", ".join(execution_status_entries),
1240
1532
  "{{EXECUTION_STATUS_TABLE_ROWS}}": "\n".join(execution_status_table_lines),
1241
1533
  "{{FINAL_REPORT_TEMPLATE_PATH}}": ctx.get("FINAL_REPORT_TEMPLATE_FILE", ""),
1242
- "{{FINAL_REPORT_TEMPLATE_RELATIVE_PATH}}": ctx.get("FINAL_REPORT_TEMPLATE_RELATIVE_PATH", ""),
1243
- "{{REFERENCE_EXPECTATIONS_RELATIVE_PATH}}": ctx.get("REFERENCE_EXPECTATIONS_RELATIVE_PATH", ""),
1534
+ "{{FINAL_REPORT_TEMPLATE_RELATIVE_PATH}}": ctx.get(
1535
+ "FINAL_REPORT_TEMPLATE_RELATIVE_PATH", ""
1536
+ ),
1537
+ "{{REFERENCE_EXPECTATIONS_RELATIVE_PATH}}": ctx.get(
1538
+ "REFERENCE_EXPECTATIONS_RELATIVE_PATH", ""
1539
+ ),
1244
1540
  "{{CLAUDE_SESSION_ID}}": ctx.get("CLAUDE_SESSION_ID", ""),
1245
1541
  "{{CLAUDE_RESUME_COMMAND_PATH}}": ctx.get("CLAUDE_RESUME_COMMAND_FILE", ""),
1246
- "{{CLAUDE_RESUME_COMMAND_RELATIVE_PATH}}": ctx.get("CLAUDE_RESUME_COMMAND_RELATIVE_PATH", ""),
1542
+ "{{CLAUDE_RESUME_COMMAND_RELATIVE_PATH}}": ctx.get(
1543
+ "CLAUDE_RESUME_COMMAND_RELATIVE_PATH", ""
1544
+ ),
1247
1545
  "{{TASK_ROOT_RELATIVE_PATH}}": ctx.get("TASK_ROOT_RELATIVE_PATH", ""),
1248
1546
  "{{TASK_MANIFEST_RELATIVE_PATH}}": ctx.get("TASK_MANIFEST_RELATIVE_PATH", ""),
1249
1547
  "{{TASK_INDEX_RELATIVE_PATH}}": ctx.get("TASK_INDEX_RELATIVE_PATH", ""),
1250
- "{{INSTRUCTION_SET_RELATIVE_PATH}}": ctx.get("INSTRUCTION_SET_RELATIVE_PATH", ""),
1548
+ "{{INSTRUCTION_SET_RELATIVE_PATH}}": ctx.get(
1549
+ "INSTRUCTION_SET_RELATIVE_PATH", ""
1550
+ ),
1251
1551
  "{{RUNS_RELATIVE_PATH}}": ctx.get("RUNS_RELATIVE_PATH", ""),
1252
1552
  "{{HISTORY_RELATIVE_PATH}}": ctx.get("HISTORY_RELATIVE_PATH", ""),
1253
- "{{OKSTRA_DISCOVERY_RELATIVE_PATH}}": ctx.get("OKSTRA_DISCOVERY_RELATIVE_PATH", ""),
1254
- "{{OKSTRA_LATEST_TASK_RELATIVE_PATH}}": ctx.get("OKSTRA_LATEST_TASK_RELATIVE_PATH", ""),
1255
- "{{OKSTRA_TASK_CATALOG_RELATIVE_PATH}}": ctx.get("OKSTRA_TASK_CATALOG_RELATIVE_PATH", ""),
1553
+ "{{OKSTRA_DISCOVERY_RELATIVE_PATH}}": ctx.get(
1554
+ "OKSTRA_DISCOVERY_RELATIVE_PATH", ""
1555
+ ),
1556
+ "{{OKSTRA_LATEST_TASK_RELATIVE_PATH}}": ctx.get(
1557
+ "OKSTRA_LATEST_TASK_RELATIVE_PATH", ""
1558
+ ),
1559
+ "{{OKSTRA_TASK_CATALOG_RELATIVE_PATH}}": ctx.get(
1560
+ "OKSTRA_TASK_CATALOG_RELATIVE_PATH", ""
1561
+ ),
1256
1562
  "{{LATEST_RUN_RELATIVE_PATH}}": ctx.get("LATEST_RUN_RELATIVE_PATH", ""),
1257
1563
  "{{LATEST_REPORT_RELATIVE_PATH}}": ctx.get("LATEST_REPORT_RELATIVE_PATH", ""),
1258
1564
  "{{TIMELINE_RELATIVE_PATH}}": ctx.get("TIMELINE_RELATIVE_PATH", ""),
1259
1565
  "{{CURRENT_TASK_STATUS}}": ctx.get("CURRENT_TASK_STATUS", ""),
1260
1566
  "{{CURRENT_RUN_STATUS}}": ctx.get("CURRENT_RUN_STATUS", ""),
1261
1567
  "{{VALIDATION_STATUS}}": ctx.get("VALIDATION_STATUS", "not-run"),
1262
- "{{RELATED_TASKS_BULLETS}}": ctx.get("RELATED_TASKS_BULLETS", "- None recorded"),
1568
+ "{{RELATED_TASKS_BULLETS}}": ctx.get(
1569
+ "RELATED_TASKS_BULLETS", "- None recorded"
1570
+ ),
1263
1571
  "{{RELATED_TASKS_INLINE}}": ctx.get("RELATED_TASKS_INLINE", "None"),
1264
1572
  "{{WORKFLOW_CURRENT_PHASE}}": ctx.get("WORKFLOW_CURRENT_PHASE", ""),
1265
- "{{WORKFLOW_NEXT_RECOMMENDED_PHASE}}": ctx.get("WORKFLOW_NEXT_RECOMMENDED_PHASE", ""),
1573
+ "{{WORKFLOW_NEXT_RECOMMENDED_PHASE}}": ctx.get(
1574
+ "WORKFLOW_NEXT_RECOMMENDED_PHASE", ""
1575
+ ),
1266
1576
  "{{PHASE_ALLOWED_OUTPUTS}}": ctx.get("PHASE_ALLOWED_OUTPUTS", ""),
1267
1577
  "{{PHASE_FORBIDDEN_ACTIONS}}": ctx.get("PHASE_FORBIDDEN_ACTIONS", ""),
1268
1578
  "{{AVAILABLE_MCP_SERVERS}}": ctx.get(