okstra 0.32.1 → 0.34.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.
- package/docs/kr/architecture.md +1 -1
- package/docs/kr/cli.md +1 -1
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/SKILL.md +22 -0
- package/runtime/agents/workers/claude-worker.md +1 -0
- package/runtime/agents/workers/codex-worker.md +1 -1
- package/runtime/agents/workers/gemini-worker.md +1 -1
- package/runtime/bin/okstra-codex-exec.sh +110 -16
- package/runtime/bin/okstra-gemini-exec.sh +103 -18
- package/runtime/prompts/launch.template.md +4 -0
- package/runtime/prompts/profiles/_common-contract.md +1 -1
- package/runtime/python/okstra_ctl/render.py +578 -227
- package/runtime/skills/okstra-team-contract/SKILL.md +3 -3
|
@@ -14,13 +14,14 @@ 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
|
|
21
|
+
import re
|
|
20
22
|
import sys
|
|
21
23
|
from pathlib import Path
|
|
22
24
|
|
|
23
|
-
|
|
24
25
|
# --------------------------------------------------------------------------- #
|
|
25
26
|
# helpers
|
|
26
27
|
# --------------------------------------------------------------------------- #
|
|
@@ -47,9 +48,47 @@ def _write_json(path: Path, payload: dict) -> None:
|
|
|
47
48
|
_write_text(path, json.dumps(payload, indent=2, ensure_ascii=False) + "\n")
|
|
48
49
|
|
|
49
50
|
|
|
51
|
+
_PHASE_BLOCK_RE = re.compile(
|
|
52
|
+
r"\{% if header\.taskType == '(implementation-planning|release-handoff|implementation|final-verification)' %\}\n(.*?)\{% endif %\}\n",
|
|
53
|
+
re.DOTALL,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _strip_phase_blocks(text: str, current_phase: str) -> str:
|
|
58
|
+
"""Resolve phase-conditional blocks (`{% if header.taskType == 'X' %}
|
|
59
|
+
... {% endif %}`) against *current_phase*.
|
|
60
|
+
|
|
61
|
+
Blocks whose target equals *current_phase* keep their body (jinja
|
|
62
|
+
markers dropped); blocks targeting a different phase are removed
|
|
63
|
+
entirely. When *current_phase* is empty or not one of the four
|
|
64
|
+
block-targetable phases (e.g. `requirements-discovery`,
|
|
65
|
+
`error-analysis`), every block is dropped — correct because none of
|
|
66
|
+
the `## 4.5` / `4.6` / `4.7` / `4.8` deliverable sections apply
|
|
67
|
+
there.
|
|
68
|
+
|
|
69
|
+
Observed (fontsninja-classifier-v2 RD run): the raw final-report
|
|
70
|
+
template copied into instruction-set/final-report-template.md was
|
|
71
|
+
43 KB / 631 lines; ~30 KB / ~330 lines belonged to the four other
|
|
72
|
+
phases' deliverables and was never relevant to that run. Stripping
|
|
73
|
+
at copy time cuts the lead/report-writer's baseline by ~7 K tokens
|
|
74
|
+
per phase entry.
|
|
75
|
+
|
|
76
|
+
Inline conditionals (those that begin and end on the same line) are
|
|
77
|
+
intentionally untouched — the regex only matches block-form
|
|
78
|
+
`{% if ... %}\\n ... \\n{% endif %}\\n`.
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
def repl(m: "re.Match[str]") -> str:
|
|
82
|
+
target_phase = m.group(1)
|
|
83
|
+
body = m.group(2)
|
|
84
|
+
return body if target_phase == current_phase else ""
|
|
85
|
+
|
|
86
|
+
return _PHASE_BLOCK_RE.sub(repl, text)
|
|
87
|
+
|
|
88
|
+
|
|
50
89
|
_FM_DEFAULT = "no-classification"
|
|
51
90
|
|
|
52
|
-
_FM_TAGS_BASE = [
|
|
91
|
+
_FM_TAGS_BASE = []
|
|
53
92
|
|
|
54
93
|
_FM_TAGS_CATALOG: dict[str, list[str]] = {
|
|
55
94
|
"task-brief": ["task-brief"],
|
|
@@ -139,7 +178,9 @@ def _frontmatter_mapping(ctx: dict) -> dict:
|
|
|
139
178
|
|
|
140
179
|
|
|
141
180
|
def _resolve_workers(ctx: dict) -> list[str]:
|
|
142
|
-
return [
|
|
181
|
+
return [
|
|
182
|
+
w.strip() for w in ctx.get("SELECTED_REVIEWERS", "").split(",") if w.strip()
|
|
183
|
+
]
|
|
143
184
|
|
|
144
185
|
|
|
145
186
|
def _worker_catalog(ctx: dict) -> dict:
|
|
@@ -198,17 +239,19 @@ def render_team_state(team_state_path: str, ctx: dict) -> None:
|
|
|
198
239
|
workers = []
|
|
199
240
|
for w in selected:
|
|
200
241
|
m = catalog[w]
|
|
201
|
-
workers.append(
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
242
|
+
workers.append(
|
|
243
|
+
{
|
|
244
|
+
"workerId": m["workerId"],
|
|
245
|
+
"role": m["role"],
|
|
246
|
+
"agent": m["agent"],
|
|
247
|
+
"model": m["model"],
|
|
248
|
+
"modelExecutionValue": m["modelExecutionValue"],
|
|
249
|
+
"status": "not-run",
|
|
250
|
+
"resultPath": m["resultPath"],
|
|
251
|
+
"promptPath": m["promptPath"],
|
|
252
|
+
"reason": "",
|
|
253
|
+
}
|
|
254
|
+
)
|
|
212
255
|
payload = {
|
|
213
256
|
"schemaVersion": "1.0",
|
|
214
257
|
"taskKey": ctx.get("TASK_KEY", ""),
|
|
@@ -280,9 +323,15 @@ def render_reference_expectations(brief_path: str, output_path: str, ctx: dict)
|
|
|
280
323
|
"## Configuration References and Expected Values",
|
|
281
324
|
"",
|
|
282
325
|
]
|
|
283
|
-
parts.append(
|
|
326
|
+
parts.append(
|
|
327
|
+
config_text
|
|
328
|
+
or "- No explicit configuration-file expectations were provided in the task brief."
|
|
329
|
+
)
|
|
284
330
|
parts.extend(["", "## Deployment Manifests and Expected Values", ""])
|
|
285
|
-
parts.append(
|
|
331
|
+
parts.append(
|
|
332
|
+
deployment_text
|
|
333
|
+
or "- No explicit deployment-manifest expectations were provided in the task brief."
|
|
334
|
+
)
|
|
286
335
|
_write_text(Path(output_path), "\n".join(parts).rstrip() + "\n")
|
|
287
336
|
|
|
288
337
|
|
|
@@ -315,7 +364,11 @@ def render_task_catalog_discovery(output_path: str, ctx: dict) -> None:
|
|
|
315
364
|
continue
|
|
316
365
|
task_root = manifest_path.parent
|
|
317
366
|
timeline_relative = s(manifest, "historyTimelinePath").strip()
|
|
318
|
-
timeline_path = (
|
|
367
|
+
timeline_path = (
|
|
368
|
+
(project_root / timeline_relative)
|
|
369
|
+
if timeline_relative
|
|
370
|
+
else (task_root / "history" / "timeline.json")
|
|
371
|
+
)
|
|
319
372
|
latest_run = {}
|
|
320
373
|
if timeline_path.is_file():
|
|
321
374
|
try:
|
|
@@ -328,43 +381,69 @@ def render_task_catalog_discovery(output_path: str, ctx: dict) -> None:
|
|
|
328
381
|
if isinstance(item, dict):
|
|
329
382
|
latest_run = item
|
|
330
383
|
break
|
|
331
|
-
workflow =
|
|
332
|
-
|
|
333
|
-
"
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
384
|
+
workflow = (
|
|
385
|
+
manifest.get("workflow")
|
|
386
|
+
if isinstance(manifest.get("workflow"), dict)
|
|
387
|
+
else {}
|
|
388
|
+
)
|
|
389
|
+
entries.append(
|
|
390
|
+
{
|
|
391
|
+
"taskKey": task_key,
|
|
392
|
+
"taskGroup": s(manifest, "taskGroup"),
|
|
393
|
+
"taskId": s(manifest, "taskId"),
|
|
394
|
+
"taskGroupPathSegment": s(manifest, "taskGroupPathSegment"),
|
|
395
|
+
"taskIdPathSegment": s(manifest, "taskIdPathSegment"),
|
|
396
|
+
"taskType": s(manifest, "taskType"),
|
|
397
|
+
"workCategory": s(manifest, "workCategory"),
|
|
398
|
+
"currentStatus": s(manifest, "currentStatus"),
|
|
399
|
+
"workStatus": s(manifest, "workStatus"),
|
|
400
|
+
"workStatusUpdatedAt": s(manifest, "workStatusUpdatedAt"),
|
|
401
|
+
"workStatusNote": s(manifest, "workStatusNote"),
|
|
402
|
+
"updatedAt": s(manifest, "updatedAt"),
|
|
403
|
+
"currentPhase": (workflow or {}).get("currentPhase", "")
|
|
404
|
+
if isinstance(workflow, dict)
|
|
405
|
+
else "",
|
|
406
|
+
"currentPhaseState": (workflow or {}).get("currentPhaseState", "")
|
|
407
|
+
if isinstance(workflow, dict)
|
|
408
|
+
else "",
|
|
409
|
+
"lastCompletedPhase": (workflow or {}).get("lastCompletedPhase", "")
|
|
410
|
+
if isinstance(workflow, dict)
|
|
411
|
+
else "",
|
|
412
|
+
"nextRecommendedPhase": (workflow or {}).get("nextRecommendedPhase", "")
|
|
413
|
+
if isinstance(workflow, dict)
|
|
414
|
+
else "",
|
|
415
|
+
"awaitingApproval": (workflow or {}).get("awaitingApproval", False)
|
|
416
|
+
if isinstance(workflow, dict)
|
|
417
|
+
else False,
|
|
418
|
+
"routingStatus": (workflow or {}).get("routingStatus", "")
|
|
419
|
+
if isinstance(workflow, dict)
|
|
420
|
+
else "",
|
|
421
|
+
"taskRootPath": s(manifest, "taskRootPath") or rel(task_root),
|
|
422
|
+
"taskManifestPath": s(manifest, "taskManifestPath")
|
|
423
|
+
or rel(manifest_path),
|
|
424
|
+
"taskIndexPath": s(manifest, "taskIndexPath"),
|
|
425
|
+
"instructionSetPath": s(manifest, "instructionSetPath"),
|
|
426
|
+
"referenceExpectationsPath": s(manifest, "referenceExpectationsPath"),
|
|
427
|
+
"taskBriefPath": s(manifest, "taskBriefPath"),
|
|
428
|
+
"latestRunPath": s(manifest, "latestRunPath")
|
|
429
|
+
or s(latest_run, "runDirectoryPath"),
|
|
430
|
+
"latestRunManifestPath": s(latest_run, "runManifestPath"),
|
|
431
|
+
"latestRunPromptsPath": s(manifest, "latestRunPromptsPath")
|
|
432
|
+
or s(latest_run, "workerPromptDirectoryPath"),
|
|
433
|
+
"latestPromptSnapshotPath": s(latest_run, "promptSnapshotPath"),
|
|
434
|
+
"latestTeamStatePath": s(latest_run, "teamStatePath"),
|
|
435
|
+
"latestRunStatus": s(manifest, "latestRunStatus")
|
|
436
|
+
or s(latest_run, "status"),
|
|
437
|
+
"latestReportPath": s(manifest, "latestReportPath")
|
|
438
|
+
or s(latest_run, "reportPath"),
|
|
439
|
+
"latestResumeCommandPath": s(manifest, "latestResumeCommandPath")
|
|
440
|
+
or s(latest_run, "resumeCommandPath"),
|
|
441
|
+
"historyTimelinePath": timeline_relative or rel(timeline_path),
|
|
442
|
+
}
|
|
443
|
+
)
|
|
444
|
+
entries.sort(
|
|
445
|
+
key=lambda x: (x.get("updatedAt", ""), x.get("taskKey", "")), reverse=True
|
|
446
|
+
)
|
|
368
447
|
payload = {
|
|
369
448
|
"schemaVersion": "1.0",
|
|
370
449
|
"projectId": ctx.get("PROJECT_ID", ""),
|
|
@@ -385,22 +464,34 @@ def render_latest_task_discovery(output_path: str, ctx: dict) -> None:
|
|
|
385
464
|
task_manifest = json.loads(task_manifest_path.read_text(encoding="utf-8"))
|
|
386
465
|
except Exception:
|
|
387
466
|
task_manifest = {}
|
|
388
|
-
workflow =
|
|
467
|
+
workflow = (
|
|
468
|
+
task_manifest.get("workflow")
|
|
469
|
+
if isinstance(task_manifest.get("workflow"), dict)
|
|
470
|
+
else {}
|
|
471
|
+
)
|
|
389
472
|
payload = {
|
|
390
473
|
"schemaVersion": "1.0",
|
|
391
474
|
"updatedAt": ctx.get("RUN_TIMESTAMP_ISO", ""),
|
|
392
475
|
"taskKey": ctx.get("TASK_KEY", ""),
|
|
393
476
|
"taskType": ctx.get("ANALYSIS_TYPE", ""),
|
|
394
|
-
"workCategory": task_manifest.get(
|
|
395
|
-
|
|
396
|
-
|
|
477
|
+
"workCategory": task_manifest.get(
|
|
478
|
+
"workCategory", ctx.get("WORKFLOW_WORK_CATEGORY", "unknown")
|
|
479
|
+
),
|
|
480
|
+
"currentStatus": task_manifest.get(
|
|
481
|
+
"currentStatus", ctx.get("CURRENT_TASK_STATUS", "")
|
|
482
|
+
),
|
|
483
|
+
"latestRunStatus": task_manifest.get(
|
|
484
|
+
"latestRunStatus", ctx.get("CURRENT_RUN_STATUS", "")
|
|
485
|
+
),
|
|
397
486
|
"workflow": workflow,
|
|
398
487
|
"taskRootPath": ctx.get("TASK_ROOT_RELATIVE_PATH", ""),
|
|
399
488
|
"taskManifestPath": ctx.get("TASK_MANIFEST_RELATIVE_PATH", ""),
|
|
400
489
|
"taskIndexPath": ctx.get("TASK_INDEX_RELATIVE_PATH", ""),
|
|
401
490
|
"instructionSetPath": ctx.get("INSTRUCTION_SET_RELATIVE_PATH", ""),
|
|
402
491
|
"taskCatalogPath": ctx.get("OKSTRA_TASK_CATALOG_RELATIVE_PATH", ""),
|
|
403
|
-
"referenceExpectationsPath": ctx.get(
|
|
492
|
+
"referenceExpectationsPath": ctx.get(
|
|
493
|
+
"REFERENCE_EXPECTATIONS_RELATIVE_PATH", ""
|
|
494
|
+
),
|
|
404
495
|
"latestRunPath": ctx.get("LATEST_RUN_RELATIVE_PATH", ""),
|
|
405
496
|
"latestRunManifestPath": ctx.get("RUN_MANIFEST_RELATIVE_PATH", ""),
|
|
406
497
|
"latestRunPromptsPath": ctx.get("RUN_PROMPTS_RELATIVE_PATH", ""),
|
|
@@ -508,8 +599,12 @@ def render_task_manifest(manifest_path: str, ctx: dict) -> None:
|
|
|
508
599
|
reviewers = _resolve_workers(ctx)
|
|
509
600
|
catalog = _worker_catalog(ctx)
|
|
510
601
|
phase_sequence = [
|
|
511
|
-
"requirements-discovery",
|
|
512
|
-
"
|
|
602
|
+
"requirements-discovery",
|
|
603
|
+
"error-analysis",
|
|
604
|
+
"implementation-planning",
|
|
605
|
+
"implementation",
|
|
606
|
+
"final-verification",
|
|
607
|
+
"release-handoff",
|
|
513
608
|
]
|
|
514
609
|
default_next_phase = {
|
|
515
610
|
"requirements-discovery": "pending-routing-decision",
|
|
@@ -521,18 +616,32 @@ def render_task_manifest(manifest_path: str, ctx: dict) -> None:
|
|
|
521
616
|
}
|
|
522
617
|
required_worker_roles = _required_worker_roles(ctx, reviewers)
|
|
523
618
|
worker_prompt_paths = {item: catalog[item]["promptPath"] for item in reviewers}
|
|
524
|
-
required_agent_status_entries = ["Claude lead"] + [
|
|
619
|
+
required_agent_status_entries = ["Claude lead"] + [
|
|
620
|
+
catalog[item]["role"] for item in reviewers
|
|
621
|
+
]
|
|
525
622
|
related_tasks = json.loads(ctx.get("RELATED_TASKS_JSON", "[]"))
|
|
526
|
-
current_report_relative = ctx.get("LATEST_REPORT_RELATIVE_PATH") or ctx.get(
|
|
527
|
-
|
|
528
|
-
|
|
623
|
+
current_report_relative = ctx.get("LATEST_REPORT_RELATIVE_PATH") or ctx.get(
|
|
624
|
+
"FINAL_REPORT_RELATIVE_PATH", ""
|
|
625
|
+
)
|
|
626
|
+
workflow = (
|
|
627
|
+
existing.get("workflow", {})
|
|
628
|
+
if isinstance(existing.get("workflow"), dict)
|
|
629
|
+
else {}
|
|
630
|
+
)
|
|
631
|
+
phase_states = (
|
|
632
|
+
workflow.get("phaseStates", {})
|
|
633
|
+
if isinstance(workflow.get("phaseStates"), dict)
|
|
634
|
+
else {}
|
|
635
|
+
)
|
|
529
636
|
current_phase = ctx.get("WORKFLOW_CURRENT_PHASE", ctx.get("ANALYSIS_TYPE", ""))
|
|
530
637
|
current_phase_state = ctx.get("WORKFLOW_CURRENT_PHASE_STATE", "not-started")
|
|
531
638
|
for phase in phase_sequence:
|
|
532
639
|
phase_states.setdefault(phase, "not-started")
|
|
533
640
|
if current_phase:
|
|
534
641
|
phase_states[current_phase] = current_phase_state
|
|
535
|
-
work_category = existing.get("workCategory") or ctx.get(
|
|
642
|
+
work_category = existing.get("workCategory") or ctx.get(
|
|
643
|
+
"WORKFLOW_WORK_CATEGORY", "unknown"
|
|
644
|
+
)
|
|
536
645
|
# Compute the canonical next phase from current_phase deterministically.
|
|
537
646
|
# Only preserve `existing.workflow.nextRecommendedPhase` when it is a
|
|
538
647
|
# legitimate forward pointer — i.e. NOT equal to `current_phase` itself
|
|
@@ -546,31 +655,63 @@ def render_task_manifest(manifest_path: str, ctx: dict) -> None:
|
|
|
546
655
|
next_recommended_phase = existing_next
|
|
547
656
|
else:
|
|
548
657
|
next_recommended_phase = canonical_next
|
|
549
|
-
last_completed_phase = workflow.get("lastCompletedPhase") or ctx.get(
|
|
550
|
-
|
|
658
|
+
last_completed_phase = workflow.get("lastCompletedPhase") or ctx.get(
|
|
659
|
+
"WORKFLOW_LAST_COMPLETED_PHASE", ""
|
|
660
|
+
)
|
|
661
|
+
routing_status = workflow.get("routingStatus") or ctx.get(
|
|
662
|
+
"WORKFLOW_ROUTING_STATUS", "not-applicable"
|
|
663
|
+
)
|
|
551
664
|
awaiting_approval = workflow.get("awaitingApproval")
|
|
552
665
|
if not isinstance(awaiting_approval, bool):
|
|
553
666
|
awaiting_approval = ctx.get("WORKFLOW_AWAITING_APPROVAL", "false") == "true"
|
|
554
667
|
render_only = ctx.get("RENDER_ONLY", "") == "true"
|
|
555
|
-
existing_checkpoint =
|
|
668
|
+
existing_checkpoint = (
|
|
669
|
+
existing.get("workflow", {}).get("lastSafeCheckpoint", {})
|
|
670
|
+
if isinstance(existing.get("workflow"), dict)
|
|
671
|
+
else {}
|
|
672
|
+
)
|
|
556
673
|
if not isinstance(existing_checkpoint, dict):
|
|
557
674
|
existing_checkpoint = {}
|
|
558
675
|
if render_only:
|
|
559
676
|
last_safe_checkpoint = {
|
|
560
677
|
"label": existing_checkpoint.get("label", ""),
|
|
561
|
-
"taskManifestPath": existing_checkpoint.get(
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
"
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
"
|
|
678
|
+
"taskManifestPath": existing_checkpoint.get(
|
|
679
|
+
"taskManifestPath", ctx.get("TASK_MANIFEST_RELATIVE_PATH", "")
|
|
680
|
+
),
|
|
681
|
+
"taskIndexPath": existing_checkpoint.get(
|
|
682
|
+
"taskIndexPath", ctx.get("TASK_INDEX_RELATIVE_PATH", "")
|
|
683
|
+
),
|
|
684
|
+
"latestRunPath": existing_checkpoint.get(
|
|
685
|
+
"latestRunPath", existing.get("latestRunPath", "")
|
|
686
|
+
),
|
|
687
|
+
"latestRunManifestPath": existing_checkpoint.get(
|
|
688
|
+
"latestRunManifestPath", ""
|
|
689
|
+
),
|
|
690
|
+
"latestTeamStatePath": existing_checkpoint.get(
|
|
691
|
+
"latestTeamStatePath", existing.get("teamStatePath", "")
|
|
692
|
+
),
|
|
693
|
+
"latestReportPath": existing_checkpoint.get(
|
|
694
|
+
"latestReportPath", existing.get("latestReportPath", "")
|
|
695
|
+
),
|
|
696
|
+
"latestResumeCommandPath": existing_checkpoint.get(
|
|
697
|
+
"latestResumeCommandPath", existing.get("latestResumeCommandPath", "")
|
|
698
|
+
),
|
|
568
699
|
}
|
|
569
|
-
latest_run_relative = existing.get("latestRunPath", "") or ctx.get(
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
700
|
+
latest_run_relative = existing.get("latestRunPath", "") or ctx.get(
|
|
701
|
+
"LATEST_RUN_RELATIVE_PATH", ""
|
|
702
|
+
)
|
|
703
|
+
latest_run_status = existing.get("latestRunStatus", "") or ctx.get(
|
|
704
|
+
"CURRENT_RUN_STATUS", ""
|
|
705
|
+
)
|
|
706
|
+
latest_run_prompts_relative = existing.get(
|
|
707
|
+
"latestRunPromptsPath", ""
|
|
708
|
+
) or ctx.get("RUN_PROMPTS_RELATIVE_PATH", "")
|
|
709
|
+
latest_report_relative = (
|
|
710
|
+
existing.get("latestReportPath", "") or current_report_relative
|
|
711
|
+
)
|
|
712
|
+
latest_team_state_relative = existing.get("teamStatePath", "") or ctx.get(
|
|
713
|
+
"TEAM_STATE_RELATIVE_PATH", ""
|
|
714
|
+
)
|
|
574
715
|
latest_resume_command_relative = existing.get("latestResumeCommandPath", "")
|
|
575
716
|
else:
|
|
576
717
|
last_safe_checkpoint = {
|
|
@@ -581,14 +722,20 @@ def render_task_manifest(manifest_path: str, ctx: dict) -> None:
|
|
|
581
722
|
"latestRunManifestPath": ctx.get("RUN_MANIFEST_RELATIVE_PATH", ""),
|
|
582
723
|
"latestTeamStatePath": ctx.get("TEAM_STATE_RELATIVE_PATH", ""),
|
|
583
724
|
"latestReportPath": current_report_relative,
|
|
584
|
-
"latestResumeCommandPath": ctx.get(
|
|
725
|
+
"latestResumeCommandPath": ctx.get(
|
|
726
|
+
"CLAUDE_RESUME_COMMAND_RELATIVE_PATH", ""
|
|
727
|
+
),
|
|
585
728
|
}
|
|
586
729
|
latest_run_relative = ctx.get("LATEST_RUN_RELATIVE_PATH", "")
|
|
587
730
|
latest_run_status = ctx.get("CURRENT_RUN_STATUS", "")
|
|
588
731
|
latest_run_prompts_relative = ctx.get("RUN_PROMPTS_RELATIVE_PATH", "")
|
|
589
|
-
latest_report_relative = current_report_relative or existing.get(
|
|
732
|
+
latest_report_relative = current_report_relative or existing.get(
|
|
733
|
+
"latestReportPath", ""
|
|
734
|
+
)
|
|
590
735
|
latest_team_state_relative = ctx.get("TEAM_STATE_RELATIVE_PATH", "")
|
|
591
|
-
latest_resume_command_relative = ctx.get(
|
|
736
|
+
latest_resume_command_relative = ctx.get(
|
|
737
|
+
"CLAUDE_RESUME_COMMAND_RELATIVE_PATH", ""
|
|
738
|
+
) or existing.get("latestResumeCommandPath", "")
|
|
592
739
|
convergence_block = _build_convergence_block(ctx)
|
|
593
740
|
payload = {
|
|
594
741
|
"schemaVersion": "1.0",
|
|
@@ -614,7 +761,9 @@ def render_task_manifest(manifest_path: str, ctx: dict) -> None:
|
|
|
614
761
|
"historyPath": ctx.get("HISTORY_RELATIVE_PATH", ""),
|
|
615
762
|
"historyTimelinePath": ctx.get("TIMELINE_RELATIVE_PATH", ""),
|
|
616
763
|
"taskCatalogPath": ctx.get("OKSTRA_TASK_CATALOG_RELATIVE_PATH", ""),
|
|
617
|
-
"referenceExpectationsPath": ctx.get(
|
|
764
|
+
"referenceExpectationsPath": ctx.get(
|
|
765
|
+
"REFERENCE_EXPECTATIONS_RELATIVE_PATH", ""
|
|
766
|
+
),
|
|
618
767
|
"latestRunPath": latest_run_relative,
|
|
619
768
|
"latestRunStatus": latest_run_status,
|
|
620
769
|
"latestRunPromptsPath": latest_run_prompts_relative,
|
|
@@ -633,15 +782,23 @@ def render_task_manifest(manifest_path: str, ctx: dict) -> None:
|
|
|
633
782
|
"lastSafeCheckpoint": last_safe_checkpoint,
|
|
634
783
|
},
|
|
635
784
|
"artifacts": {
|
|
636
|
-
"analysisProfilePath": ctx.get("INSTRUCTION_SET_RELATIVE_PATH", "")
|
|
637
|
-
|
|
638
|
-
"
|
|
639
|
-
"
|
|
640
|
-
"
|
|
785
|
+
"analysisProfilePath": ctx.get("INSTRUCTION_SET_RELATIVE_PATH", "")
|
|
786
|
+
+ "/analysis-profile.md",
|
|
787
|
+
"analysisMaterialPath": ctx.get("INSTRUCTION_SET_RELATIVE_PATH", "")
|
|
788
|
+
+ "/analysis-material.md",
|
|
789
|
+
"taskBriefCopyPath": ctx.get("INSTRUCTION_SET_RELATIVE_PATH", "")
|
|
790
|
+
+ "/task-brief.md",
|
|
791
|
+
"referenceExpectationsPath": ctx.get(
|
|
792
|
+
"REFERENCE_EXPECTATIONS_RELATIVE_PATH", ""
|
|
793
|
+
),
|
|
794
|
+
"claudeExecutionPromptPath": ctx.get("INSTRUCTION_SET_RELATIVE_PATH", "")
|
|
795
|
+
+ "/claude-execution-prompt.md",
|
|
641
796
|
"leadPromptSnapshotPath": ctx.get("RUN_PROMPT_SNAPSHOT_RELATIVE_PATH", ""),
|
|
642
797
|
"workerPromptsDirectoryPath": ctx.get("RUN_PROMPTS_RELATIVE_PATH", ""),
|
|
643
798
|
"workerPromptPathByWorkerId": worker_prompt_paths,
|
|
644
|
-
"finalReportTemplatePath": ctx.get(
|
|
799
|
+
"finalReportTemplatePath": ctx.get(
|
|
800
|
+
"FINAL_REPORT_TEMPLATE_RELATIVE_PATH", ""
|
|
801
|
+
),
|
|
645
802
|
"resumeCommandPath": ctx.get("CLAUDE_RESUME_COMMAND_RELATIVE_PATH", ""),
|
|
646
803
|
"teamStatePath": ctx.get("TEAM_STATE_RELATIVE_PATH", ""),
|
|
647
804
|
"workerResultsDirectoryPath": ctx.get("WORKER_RESULTS_RELATIVE_PATH", ""),
|
|
@@ -670,20 +827,33 @@ def render_task_manifest(manifest_path: str, ctx: dict) -> None:
|
|
|
670
827
|
"disallowLeadSoloAnalysisAsWorkerResult": True,
|
|
671
828
|
"disallowGenericParallelOnlyExecution": True,
|
|
672
829
|
"workerOutputSections": [
|
|
673
|
-
"Findings",
|
|
674
|
-
"
|
|
830
|
+
"Findings",
|
|
831
|
+
"Missing Information or Assumptions",
|
|
832
|
+
"Safe or Reasonable Areas",
|
|
833
|
+
"Uncertain Points",
|
|
675
834
|
"Recommended Next Actions",
|
|
676
835
|
],
|
|
677
836
|
"finalReportSections": [
|
|
678
|
-
"Problem or Validation Summary",
|
|
679
|
-
"
|
|
837
|
+
"Problem or Validation Summary",
|
|
838
|
+
"Agent Execution Status",
|
|
839
|
+
"Cross Verification Result",
|
|
840
|
+
"Final Verdict",
|
|
680
841
|
"Evidence and Detailed Analysis",
|
|
681
|
-
"Missing Information and Risk",
|
|
842
|
+
"Missing Information and Risk",
|
|
843
|
+
"Recommended Next Actions",
|
|
682
844
|
],
|
|
683
845
|
"statusLabels": [
|
|
684
|
-
"prepared",
|
|
685
|
-
"
|
|
686
|
-
"
|
|
846
|
+
"prepared",
|
|
847
|
+
"team-created",
|
|
848
|
+
"workers-dispatched",
|
|
849
|
+
"worker-results-collected",
|
|
850
|
+
"synthesis-written",
|
|
851
|
+
"in-progress",
|
|
852
|
+
"completed",
|
|
853
|
+
"contract-violated",
|
|
854
|
+
"timeout",
|
|
855
|
+
"error",
|
|
856
|
+
"not-run",
|
|
687
857
|
],
|
|
688
858
|
},
|
|
689
859
|
"contractValidation": {
|
|
@@ -749,7 +919,11 @@ def render_run_manifest(run_manifest_path: str, ctx: dict) -> None:
|
|
|
749
919
|
required_worker_roles = _required_worker_roles(ctx, reviewers)
|
|
750
920
|
worker_prompt_paths = {item: catalog[item]["promptPath"] for item in reviewers}
|
|
751
921
|
related_tasks = json.loads(ctx.get("RELATED_TASKS_JSON", "[]"))
|
|
752
|
-
workflow =
|
|
922
|
+
workflow = (
|
|
923
|
+
task_manifest.get("workflow", {})
|
|
924
|
+
if isinstance(task_manifest.get("workflow"), dict)
|
|
925
|
+
else {}
|
|
926
|
+
)
|
|
753
927
|
payload = {
|
|
754
928
|
"schemaVersion": "1.0",
|
|
755
929
|
"okstraVersion": ctx.get("OKSTRA_VERSION", ""),
|
|
@@ -758,7 +932,9 @@ def render_run_manifest(run_manifest_path: str, ctx: dict) -> None:
|
|
|
758
932
|
"taskId": ctx.get("TASK_ID", ""),
|
|
759
933
|
"taskKey": ctx.get("TASK_KEY", ""),
|
|
760
934
|
"taskType": ctx.get("ANALYSIS_TYPE", ""),
|
|
761
|
-
"workCategory": task_manifest.get(
|
|
935
|
+
"workCategory": task_manifest.get(
|
|
936
|
+
"workCategory", ctx.get("WORKFLOW_WORK_CATEGORY", "unknown")
|
|
937
|
+
),
|
|
762
938
|
"taskBriefPath": ctx.get("BRIEF_RELATIVE_PATH", ""),
|
|
763
939
|
"relatedTasks": related_tasks,
|
|
764
940
|
"recommendedWorkers": reviewers,
|
|
@@ -766,7 +942,9 @@ def render_run_manifest(run_manifest_path: str, ctx: dict) -> None:
|
|
|
766
942
|
"taskManifestPath": ctx.get("TASK_MANIFEST_RELATIVE_PATH", ""),
|
|
767
943
|
"instructionSetPath": ctx.get("INSTRUCTION_SET_RELATIVE_PATH", ""),
|
|
768
944
|
"taskCatalogPath": ctx.get("OKSTRA_TASK_CATALOG_RELATIVE_PATH", ""),
|
|
769
|
-
"referenceExpectationsPath": ctx.get(
|
|
945
|
+
"referenceExpectationsPath": ctx.get(
|
|
946
|
+
"REFERENCE_EXPECTATIONS_RELATIVE_PATH", ""
|
|
947
|
+
),
|
|
770
948
|
"runDirectoryPath": ctx.get("RUN_DIR_RELATIVE_PATH", ""),
|
|
771
949
|
"runDateTimeSegment": ctx.get("RUN_DATETIME_SEGMENT", ""),
|
|
772
950
|
"runSequencesByCategory": {
|
|
@@ -792,13 +970,26 @@ def render_run_manifest(run_manifest_path: str, ctx: dict) -> None:
|
|
|
792
970
|
"resumeCommandPath": ctx.get("CLAUDE_RESUME_COMMAND_RELATIVE_PATH", ""),
|
|
793
971
|
"workflowSnapshot": {
|
|
794
972
|
"phaseSequence": workflow.get("phaseSequence", []),
|
|
795
|
-
"currentPhase": workflow.get(
|
|
796
|
-
|
|
973
|
+
"currentPhase": workflow.get(
|
|
974
|
+
"currentPhase", ctx.get("WORKFLOW_CURRENT_PHASE", "")
|
|
975
|
+
),
|
|
976
|
+
"currentPhaseState": workflow.get(
|
|
977
|
+
"currentPhaseState", ctx.get("WORKFLOW_CURRENT_PHASE_STATE", "")
|
|
978
|
+
),
|
|
797
979
|
"phaseStates": workflow.get("phaseStates", {}),
|
|
798
|
-
"lastCompletedPhase": workflow.get(
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
"
|
|
980
|
+
"lastCompletedPhase": workflow.get(
|
|
981
|
+
"lastCompletedPhase", ctx.get("WORKFLOW_LAST_COMPLETED_PHASE", "")
|
|
982
|
+
),
|
|
983
|
+
"nextRecommendedPhase": workflow.get(
|
|
984
|
+
"nextRecommendedPhase", ctx.get("WORKFLOW_NEXT_RECOMMENDED_PHASE", "")
|
|
985
|
+
),
|
|
986
|
+
"awaitingApproval": workflow.get(
|
|
987
|
+
"awaitingApproval",
|
|
988
|
+
ctx.get("WORKFLOW_AWAITING_APPROVAL", "false") == "true",
|
|
989
|
+
),
|
|
990
|
+
"routingStatus": workflow.get(
|
|
991
|
+
"routingStatus", ctx.get("WORKFLOW_ROUTING_STATUS", "")
|
|
992
|
+
),
|
|
802
993
|
"lastSafeCheckpoint": workflow.get("lastSafeCheckpoint", {}),
|
|
803
994
|
},
|
|
804
995
|
"teamContract": {
|
|
@@ -810,7 +1001,8 @@ def render_run_manifest(run_manifest_path: str, ctx: dict) -> None:
|
|
|
810
1001
|
"finalSynthesisOwner": "Claude lead",
|
|
811
1002
|
"requiredWorkerAttempts": reviewers,
|
|
812
1003
|
"requiredWorkerRoles": required_worker_roles,
|
|
813
|
-
"requiredAgentStatusEntries": ["Claude lead"]
|
|
1004
|
+
"requiredAgentStatusEntries": ["Claude lead"]
|
|
1005
|
+
+ [catalog[item]["role"] for item in reviewers],
|
|
814
1006
|
"requireDistinctLeadFromClaudeWorker": True,
|
|
815
1007
|
"requireAllRequiredWorkerAttempts": True,
|
|
816
1008
|
"requireGeminiWorkerAttempt": "gemini" in reviewers,
|
|
@@ -867,49 +1059,61 @@ def render_timeline(timeline_path: str, ctx: dict) -> None:
|
|
|
867
1059
|
current_run_manifest_path = ctx.get("RUN_MANIFEST_FILE", "")
|
|
868
1060
|
current_run_manifest_relative_path = ctx.get("RUN_MANIFEST_RELATIVE_PATH", "")
|
|
869
1061
|
filtered = [
|
|
870
|
-
item
|
|
1062
|
+
item
|
|
1063
|
+
for item in runs
|
|
871
1064
|
if item.get("runManifestPath") != current_run_manifest_relative_path
|
|
872
1065
|
and item.get("runManifestPath") != current_run_manifest_path
|
|
873
1066
|
]
|
|
874
|
-
workflow =
|
|
1067
|
+
workflow = (
|
|
1068
|
+
task_manifest.get("workflow")
|
|
1069
|
+
if isinstance(task_manifest.get("workflow"), dict)
|
|
1070
|
+
else {}
|
|
1071
|
+
)
|
|
875
1072
|
workflow = workflow or {}
|
|
876
|
-
filtered.append(
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
"
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
"
|
|
903
|
-
|
|
904
|
-
"
|
|
905
|
-
"
|
|
906
|
-
"
|
|
907
|
-
"
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
1073
|
+
filtered.append(
|
|
1074
|
+
{
|
|
1075
|
+
"runTimestamp": ctx.get("RUN_TIMESTAMP_ISO", ""),
|
|
1076
|
+
"runDirectoryPath": ctx.get("RUN_DIR_RELATIVE_PATH", ""),
|
|
1077
|
+
"runManifestPath": ctx.get("RUN_MANIFEST_RELATIVE_PATH", ""),
|
|
1078
|
+
"runDateTimeSegment": ctx.get("RUN_DATETIME_SEGMENT", ""),
|
|
1079
|
+
"runSequencesByCategory": {
|
|
1080
|
+
"manifests": ctx.get("RUN_MANIFESTS_SEQ", ""),
|
|
1081
|
+
"prompts": ctx.get("RUN_PROMPTS_SEQ", ""),
|
|
1082
|
+
"reports": ctx.get("RUN_REPORTS_SEQ", ""),
|
|
1083
|
+
"status": ctx.get("RUN_STATUS_SEQ", ""),
|
|
1084
|
+
"state": ctx.get("RUN_STATE_SEQ", ""),
|
|
1085
|
+
"sessions": ctx.get("RUN_SESSIONS_SEQ", ""),
|
|
1086
|
+
"workerResults": ctx.get("WORKER_RESULTS_SEQ", ""),
|
|
1087
|
+
},
|
|
1088
|
+
"taskType": ctx.get("ANALYSIS_TYPE", ""),
|
|
1089
|
+
"workCategory": task_manifest.get(
|
|
1090
|
+
"workCategory", ctx.get("WORKFLOW_WORK_CATEGORY", "unknown")
|
|
1091
|
+
),
|
|
1092
|
+
"status": ctx.get("CURRENT_RUN_STATUS", ""),
|
|
1093
|
+
"taskBriefPath": ctx.get("BRIEF_RELATIVE_PATH", ""),
|
|
1094
|
+
"promptSnapshotPath": ctx.get("RUN_PROMPT_SNAPSHOT_RELATIVE_PATH", ""),
|
|
1095
|
+
"workerPromptDirectoryPath": ctx.get("RUN_PROMPTS_RELATIVE_PATH", ""),
|
|
1096
|
+
"workerPromptPathByWorkerId": {
|
|
1097
|
+
item: worker_prompt_paths[item] for item in reviewers
|
|
1098
|
+
},
|
|
1099
|
+
"reportPath": ctx.get("LATEST_REPORT_RELATIVE_PATH")
|
|
1100
|
+
or ctx.get("FINAL_REPORT_RELATIVE_PATH", ""),
|
|
1101
|
+
"teamStatePath": ctx.get("TEAM_STATE_RELATIVE_PATH", ""),
|
|
1102
|
+
"resumeCommandPath": ctx.get("CLAUDE_RESUME_COMMAND_RELATIVE_PATH", ""),
|
|
1103
|
+
"relatedTasks": json.loads(ctx.get("RELATED_TASKS_JSON", "[]")),
|
|
1104
|
+
"workflowSnapshot": {
|
|
1105
|
+
"phaseSequence": workflow.get("phaseSequence", []),
|
|
1106
|
+
"currentPhase": workflow.get("currentPhase", ""),
|
|
1107
|
+
"currentPhaseState": workflow.get("currentPhaseState", ""),
|
|
1108
|
+
"phaseStates": workflow.get("phaseStates", {}),
|
|
1109
|
+
"lastCompletedPhase": workflow.get("lastCompletedPhase", ""),
|
|
1110
|
+
"nextRecommendedPhase": workflow.get("nextRecommendedPhase", ""),
|
|
1111
|
+
"awaitingApproval": workflow.get("awaitingApproval", False),
|
|
1112
|
+
"routingStatus": workflow.get("routingStatus", ""),
|
|
1113
|
+
"lastSafeCheckpoint": workflow.get("lastSafeCheckpoint", {}),
|
|
1114
|
+
},
|
|
1115
|
+
}
|
|
1116
|
+
)
|
|
913
1117
|
payload = {
|
|
914
1118
|
"schemaVersion": "1.0",
|
|
915
1119
|
"projectId": ctx.get("PROJECT_ID", ""),
|
|
@@ -930,20 +1134,35 @@ def render_task_index(template_path: str, output_path: str, ctx: dict) -> None:
|
|
|
930
1134
|
task_manifest = json.loads(task_manifest_path.read_text(encoding="utf-8"))
|
|
931
1135
|
except Exception:
|
|
932
1136
|
task_manifest = {}
|
|
933
|
-
workflow =
|
|
934
|
-
|
|
1137
|
+
workflow = (
|
|
1138
|
+
task_manifest.get("workflow", {})
|
|
1139
|
+
if isinstance(task_manifest.get("workflow"), dict)
|
|
1140
|
+
else {}
|
|
1141
|
+
)
|
|
1142
|
+
phase_states = (
|
|
1143
|
+
workflow.get("phaseStates", {})
|
|
1144
|
+
if isinstance(workflow.get("phaseStates"), dict)
|
|
1145
|
+
else {}
|
|
1146
|
+
)
|
|
935
1147
|
phase_order = workflow.get("phaseSequence", [])
|
|
936
1148
|
if not isinstance(phase_order, list) or not phase_order:
|
|
937
1149
|
phase_order = [
|
|
938
|
-
"requirements-discovery",
|
|
939
|
-
"
|
|
1150
|
+
"requirements-discovery",
|
|
1151
|
+
"error-analysis",
|
|
1152
|
+
"implementation-planning",
|
|
1153
|
+
"implementation",
|
|
1154
|
+
"final-verification",
|
|
940
1155
|
"release-handoff",
|
|
941
1156
|
]
|
|
942
1157
|
phase_state_lines = [
|
|
943
1158
|
f"- `{phase}`: `{phase_states.get(phase, 'not-started')}`"
|
|
944
1159
|
for phase in phase_order
|
|
945
1160
|
]
|
|
946
|
-
checkpoint =
|
|
1161
|
+
checkpoint = (
|
|
1162
|
+
workflow.get("lastSafeCheckpoint", {})
|
|
1163
|
+
if isinstance(workflow.get("lastSafeCheckpoint"), dict)
|
|
1164
|
+
else {}
|
|
1165
|
+
)
|
|
947
1166
|
checkpoint_lines = [
|
|
948
1167
|
f"- Label: `{checkpoint.get('label', 'unknown')}`",
|
|
949
1168
|
f"- Run manifest: `{checkpoint.get('latestRunManifestPath', ctx.get('RUN_MANIFEST_RELATIVE_PATH', ''))}`",
|
|
@@ -951,9 +1170,21 @@ def render_task_index(template_path: str, output_path: str, ctx: dict) -> None:
|
|
|
951
1170
|
f"- Report: `{checkpoint.get('latestReportPath', task_manifest.get('latestReportPath', '--')) or '--'}`",
|
|
952
1171
|
f"- Resume command: `{checkpoint.get('latestResumeCommandPath', task_manifest.get('latestResumeCommandPath', '--')) or '--'}`",
|
|
953
1172
|
]
|
|
954
|
-
rc =
|
|
955
|
-
|
|
956
|
-
|
|
1173
|
+
rc = (
|
|
1174
|
+
task_manifest.get("resultContract")
|
|
1175
|
+
if isinstance(task_manifest.get("resultContract"), dict)
|
|
1176
|
+
else {}
|
|
1177
|
+
)
|
|
1178
|
+
cv = (
|
|
1179
|
+
task_manifest.get("contractValidation")
|
|
1180
|
+
if isinstance(task_manifest.get("contractValidation"), dict)
|
|
1181
|
+
else {}
|
|
1182
|
+
)
|
|
1183
|
+
art = (
|
|
1184
|
+
task_manifest.get("artifacts")
|
|
1185
|
+
if isinstance(task_manifest.get("artifacts"), dict)
|
|
1186
|
+
else {}
|
|
1187
|
+
)
|
|
957
1188
|
mapping = {
|
|
958
1189
|
"{{TASK_KEY}}": task_manifest.get("taskKey", ctx.get("TASK_KEY", "")),
|
|
959
1190
|
"{{TASK_TYPE}}": task_manifest.get("taskType", ctx.get("ANALYSIS_TYPE", "")),
|
|
@@ -961,44 +1192,96 @@ def render_task_index(template_path: str, output_path: str, ctx: dict) -> None:
|
|
|
961
1192
|
"{{PROJECT_ID}}": ctx.get("PROJECT_ID", ""),
|
|
962
1193
|
"{{TASK_GROUP}}": ctx.get("TASK_GROUP", ""),
|
|
963
1194
|
"{{TASK_ID}}": ctx.get("TASK_ID", ""),
|
|
964
|
-
"{{CURRENT_TASK_STATUS}}": task_manifest.get(
|
|
965
|
-
|
|
1195
|
+
"{{CURRENT_TASK_STATUS}}": task_manifest.get(
|
|
1196
|
+
"currentStatus", ctx.get("CURRENT_TASK_STATUS", "")
|
|
1197
|
+
),
|
|
1198
|
+
"{{CURRENT_RUN_STATUS}}": task_manifest.get(
|
|
1199
|
+
"latestRunStatus", ctx.get("CURRENT_RUN_STATUS", "")
|
|
1200
|
+
),
|
|
966
1201
|
"{{RELATED_TASKS_INLINE}}": ctx.get("RELATED_TASKS_INLINE", "None"),
|
|
967
|
-
"{{RECOMMENDED_ANALYSERS}}": ", ".join(
|
|
1202
|
+
"{{RECOMMENDED_ANALYSERS}}": ", ".join(
|
|
1203
|
+
task_manifest.get("recommendedWorkers", [])
|
|
1204
|
+
),
|
|
968
1205
|
"{{LEAD_MODEL}}": rc.get("leadModel", ctx.get("LEAD_MODEL_DISPLAY", "")),
|
|
969
1206
|
"{{OKSTRA_VERSION}}": ctx.get("OKSTRA_VERSION", ""),
|
|
970
|
-
"{{LATEST_RUN_RELATIVE_PATH}}": task_manifest.get(
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
"{{
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
"{{
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
"{{
|
|
1207
|
+
"{{LATEST_RUN_RELATIVE_PATH}}": task_manifest.get(
|
|
1208
|
+
"latestRunPath", ctx.get("LATEST_RUN_RELATIVE_PATH", "")
|
|
1209
|
+
),
|
|
1210
|
+
"{{LATEST_REPORT_RELATIVE_PATH}}": task_manifest.get(
|
|
1211
|
+
"latestReportPath", ctx.get("LATEST_REPORT_RELATIVE_PATH", "")
|
|
1212
|
+
),
|
|
1213
|
+
"{{TEAM_STATE_RELATIVE_PATH}}": task_manifest.get(
|
|
1214
|
+
"teamStatePath", ctx.get("TEAM_STATE_RELATIVE_PATH", "")
|
|
1215
|
+
),
|
|
1216
|
+
"{{VALIDATION_STATUS}}": cv.get(
|
|
1217
|
+
"status", ctx.get("VALIDATION_STATUS", "not-run")
|
|
1218
|
+
),
|
|
1219
|
+
"{{CLAUDE_RESUME_COMMAND_RELATIVE_PATH}}": task_manifest.get(
|
|
1220
|
+
"latestResumeCommandPath",
|
|
1221
|
+
ctx.get("CLAUDE_RESUME_COMMAND_RELATIVE_PATH", ""),
|
|
1222
|
+
),
|
|
1223
|
+
"{{MODEL_ASSIGNMENT_LINES}}": "\n".join(
|
|
1224
|
+
[
|
|
1225
|
+
f"- `Claude lead`: `{rc.get('leadModel', ctx.get('LEAD_MODEL_DISPLAY', ''))}`",
|
|
1226
|
+
f"- `Claude worker`: `{ctx.get('CLAUDE_WORKER_MODEL_DISPLAY', '')}`",
|
|
1227
|
+
f"- `Codex worker`: `{ctx.get('CODEX_WORKER_MODEL_DISPLAY', '')}`",
|
|
1228
|
+
f"- `Gemini worker`: `{ctx.get('GEMINI_WORKER_MODEL_DISPLAY', '')}`",
|
|
1229
|
+
f"- `Report writer worker`: `{ctx.get('REPORT_WRITER_MODEL_DISPLAY', '')}`",
|
|
1230
|
+
]
|
|
1231
|
+
),
|
|
1232
|
+
"{{TASK_MANIFEST_RELATIVE_PATH}}": task_manifest.get(
|
|
1233
|
+
"taskManifestPath", ctx.get("TASK_MANIFEST_RELATIVE_PATH", "")
|
|
1234
|
+
),
|
|
1235
|
+
"{{OKSTRA_LATEST_TASK_RELATIVE_PATH}}": ctx.get(
|
|
1236
|
+
"OKSTRA_LATEST_TASK_RELATIVE_PATH", ""
|
|
1237
|
+
),
|
|
1238
|
+
"{{INSTRUCTION_SET_RELATIVE_PATH}}": task_manifest.get(
|
|
1239
|
+
"instructionSetPath", ctx.get("INSTRUCTION_SET_RELATIVE_PATH", "")
|
|
1240
|
+
),
|
|
1241
|
+
"{{REFERENCE_EXPECTATIONS_RELATIVE_PATH}}": task_manifest.get(
|
|
1242
|
+
"referenceExpectationsPath",
|
|
1243
|
+
ctx.get("REFERENCE_EXPECTATIONS_RELATIVE_PATH", ""),
|
|
1244
|
+
),
|
|
1245
|
+
"{{FINAL_REPORT_TEMPLATE_RELATIVE_PATH}}": art.get(
|
|
1246
|
+
"finalReportTemplatePath",
|
|
1247
|
+
ctx.get("FINAL_REPORT_TEMPLATE_RELATIVE_PATH", ""),
|
|
1248
|
+
),
|
|
987
1249
|
"{{RUN_MANIFESTS_RELATIVE_PATH}}": ctx.get("RUN_MANIFESTS_RELATIVE_PATH", ""),
|
|
988
1250
|
"{{RUN_STATE_RELATIVE_PATH}}": ctx.get("RUN_STATE_RELATIVE_PATH", ""),
|
|
989
|
-
"{{RUN_PROMPTS_RELATIVE_PATH}}": task_manifest.get(
|
|
1251
|
+
"{{RUN_PROMPTS_RELATIVE_PATH}}": task_manifest.get(
|
|
1252
|
+
"latestRunPromptsPath", ctx.get("RUN_PROMPTS_RELATIVE_PATH", "")
|
|
1253
|
+
),
|
|
990
1254
|
"{{RUN_REPORTS_RELATIVE_PATH}}": ctx.get("RUN_REPORTS_RELATIVE_PATH", ""),
|
|
991
1255
|
"{{RUN_STATUS_RELATIVE_PATH}}": ctx.get("RUN_STATUS_RELATIVE_PATH", ""),
|
|
992
1256
|
"{{RUN_SESSIONS_RELATIVE_PATH}}": ctx.get("RUN_SESSIONS_RELATIVE_PATH", ""),
|
|
993
|
-
"{{WORKER_RESULTS_RELATIVE_PATH}}": art.get(
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
"{{
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
"{{
|
|
1000
|
-
|
|
1001
|
-
|
|
1257
|
+
"{{WORKER_RESULTS_RELATIVE_PATH}}": art.get(
|
|
1258
|
+
"workerResultsDirectoryPath", ctx.get("WORKER_RESULTS_RELATIVE_PATH", "")
|
|
1259
|
+
),
|
|
1260
|
+
"{{RUN_VALIDATOR_RELATIVE_PATH}}": cv.get(
|
|
1261
|
+
"validatorScriptPath", ctx.get("RUN_VALIDATOR_RELATIVE_PATH", "")
|
|
1262
|
+
),
|
|
1263
|
+
"{{WORK_CATEGORY}}": task_manifest.get(
|
|
1264
|
+
"workCategory", ctx.get("WORKFLOW_WORK_CATEGORY", "unknown")
|
|
1265
|
+
),
|
|
1266
|
+
"{{WORKFLOW_CURRENT_PHASE}}": workflow.get(
|
|
1267
|
+
"currentPhase", ctx.get("WORKFLOW_CURRENT_PHASE", "")
|
|
1268
|
+
),
|
|
1269
|
+
"{{WORKFLOW_CURRENT_PHASE_STATE}}": workflow.get(
|
|
1270
|
+
"currentPhaseState", ctx.get("WORKFLOW_CURRENT_PHASE_STATE", "")
|
|
1271
|
+
),
|
|
1272
|
+
"{{WORKFLOW_LAST_COMPLETED_PHASE}}": workflow.get(
|
|
1273
|
+
"lastCompletedPhase", ctx.get("WORKFLOW_LAST_COMPLETED_PHASE", "")
|
|
1274
|
+
)
|
|
1275
|
+
or "--",
|
|
1276
|
+
"{{WORKFLOW_NEXT_RECOMMENDED_PHASE}}": workflow.get(
|
|
1277
|
+
"nextRecommendedPhase", ctx.get("WORKFLOW_NEXT_RECOMMENDED_PHASE", "")
|
|
1278
|
+
),
|
|
1279
|
+
"{{WORKFLOW_AWAITING_APPROVAL}}": "yes"
|
|
1280
|
+
if workflow.get("awaitingApproval", False)
|
|
1281
|
+
else "no",
|
|
1282
|
+
"{{WORKFLOW_ROUTING_STATUS}}": workflow.get(
|
|
1283
|
+
"routingStatus", ctx.get("WORKFLOW_ROUTING_STATUS", "")
|
|
1284
|
+
),
|
|
1002
1285
|
"{{WORKFLOW_PHASE_STATE_LINES}}": "\n".join(phase_state_lines),
|
|
1003
1286
|
"{{WORKFLOW_LAST_SAFE_CHECKPOINT_LINES}}": "\n".join(checkpoint_lines),
|
|
1004
1287
|
}
|
|
@@ -1008,6 +1291,7 @@ def render_task_index(template_path: str, output_path: str, ctx: dict) -> None:
|
|
|
1008
1291
|
rendered = template
|
|
1009
1292
|
for k, v in mapping.items():
|
|
1010
1293
|
rendered = rendered.replace(k, v)
|
|
1294
|
+
rendered = _strip_phase_blocks(rendered, ctx.get("ANALYSIS_TYPE", ""))
|
|
1011
1295
|
_write_text(Path(output_path), rendered.rstrip() + "\n")
|
|
1012
1296
|
|
|
1013
1297
|
|
|
@@ -1055,7 +1339,9 @@ def build_available_mcp_servers_block(project_root: Path) -> str:
|
|
|
1055
1339
|
if description:
|
|
1056
1340
|
parts.append(description)
|
|
1057
1341
|
if isinstance(tools, list) and tools:
|
|
1058
|
-
tool_names = ", ".join(
|
|
1342
|
+
tool_names = ", ".join(
|
|
1343
|
+
f"`{str(t).strip()}`" for t in tools if str(t).strip()
|
|
1344
|
+
)
|
|
1059
1345
|
if tool_names:
|
|
1060
1346
|
parts.append(f"Tools: {tool_names}")
|
|
1061
1347
|
if notes:
|
|
@@ -1083,7 +1369,9 @@ def render_template_file(template_path: str, output_path: str, ctx: dict) -> Non
|
|
|
1083
1369
|
|
|
1084
1370
|
worker_result_lines = []
|
|
1085
1371
|
team_role_lines = [f" 1. `Claude lead` (assigned model: `{lead_model}`)"]
|
|
1086
|
-
model_assignment_lines = [
|
|
1372
|
+
model_assignment_lines = [
|
|
1373
|
+
fmt_assignment("Claude lead", lead_model, lead_model_execution)
|
|
1374
|
+
]
|
|
1087
1375
|
worker_role_labels = []
|
|
1088
1376
|
execution_status_entries = ["`Claude lead`"]
|
|
1089
1377
|
execution_status_table_lines = [
|
|
@@ -1094,24 +1382,34 @@ def render_template_file(template_path: str, output_path: str, ctx: dict) -> Non
|
|
|
1094
1382
|
for index, worker in enumerate(selected, start=2):
|
|
1095
1383
|
m = catalog[worker]
|
|
1096
1384
|
worker_result_lines.append(
|
|
1097
|
-
f"- {m['role']} result path: `{m['resultPath']}` (assigned model: `{m['model']}`)"
|
|
1098
|
-
|
|
1099
|
-
|
|
1385
|
+
f"- {m['role']} result path: `{m['resultPath']}` (assigned model: `{m['model']}`)"
|
|
1386
|
+
)
|
|
1387
|
+
team_role_lines.append(
|
|
1388
|
+
f" {index}. `{m['role']}` (assigned model: `{m['model']}`)"
|
|
1389
|
+
)
|
|
1390
|
+
model_assignment_lines.append(
|
|
1391
|
+
fmt_assignment(m["role"], m["model"], m["modelExecutionValue"])
|
|
1392
|
+
)
|
|
1100
1393
|
worker_role_labels.append(f"`{m['role']}`")
|
|
1101
1394
|
execution_status_entries.append(f"`{m['role']}`")
|
|
1102
1395
|
execution_status_table_lines.append(
|
|
1103
|
-
f"| {m['agentLabel']} | {m['role']} | {m['model']} | completed / timeout / error / not-run | {m['role']}의 핵심 발견 요약 |"
|
|
1396
|
+
f"| {m['agentLabel']} | {m['role']} | {m['model']} | completed / timeout / error / not-run | {m['role']}의 핵심 발견 요약 |"
|
|
1397
|
+
)
|
|
1104
1398
|
|
|
1105
1399
|
if worker_role_labels:
|
|
1106
1400
|
if len(worker_role_labels) == 1:
|
|
1107
|
-
worker_role_sentence =
|
|
1401
|
+
worker_role_sentence = (
|
|
1402
|
+
f"- {worker_role_labels[0]} is the required worker role."
|
|
1403
|
+
)
|
|
1108
1404
|
else:
|
|
1109
1405
|
worker_role_sentence = (
|
|
1110
1406
|
f"- {', '.join(worker_role_labels[:-1])}, "
|
|
1111
|
-
f"and {worker_role_labels[-1]} are the required worker roles."
|
|
1407
|
+
f"and {worker_role_labels[-1]} are the required worker roles."
|
|
1408
|
+
)
|
|
1112
1409
|
preferred_results_sentence = (
|
|
1113
1410
|
f"- Aim to collect completed results from all "
|
|
1114
|
-
f"{len(worker_role_labels)} required workers."
|
|
1411
|
+
f"{len(worker_role_labels)} required workers."
|
|
1412
|
+
)
|
|
1115
1413
|
else:
|
|
1116
1414
|
worker_role_sentence = "- No worker roles were selected for this run."
|
|
1117
1415
|
preferred_results_sentence = "- No worker results are expected for this run."
|
|
@@ -1144,7 +1442,7 @@ def render_template_file(template_path: str, output_path: str, ctx: dict) -> Non
|
|
|
1144
1442
|
"## Team Creation Gate (BLOCKING)\n"
|
|
1145
1443
|
"\n"
|
|
1146
1444
|
"Before any `Agent` dispatch for workers, you MUST perform Phase 3 of the\n"
|
|
1147
|
-
|
|
1445
|
+
'`okstra` skill (`agents/SKILL.md` → "Phase 3 — Team creation"). Skipping\n'
|
|
1148
1446
|
"this gate silently degrades the run to in-process background dispatch and\n"
|
|
1149
1447
|
"loses the Teams split-pane observability surface, even though worker\n"
|
|
1150
1448
|
"outputs may still appear correct on disk.\n"
|
|
@@ -1155,16 +1453,16 @@ def render_template_file(template_path: str, output_path: str, ctx: dict) -> Non
|
|
|
1155
1453
|
"\n"
|
|
1156
1454
|
"1. Invoke the `okstra-team-contract` skill and verify the selected worker\n"
|
|
1157
1455
|
" roster against `task-manifest.json`'s `resultContract.requiredWorkerRoles`.\n"
|
|
1158
|
-
f
|
|
1456
|
+
f'2. Call `TeamCreate(team_name: "okstra-{ctx.get("TASK_KEY", "")}", description: ...)`.\n'
|
|
1159
1457
|
"3. Record the outcome in team-state under\n"
|
|
1160
|
-
|
|
1458
|
+
' `teamCreate: { attempted: true, status: "ok" | "error", error?: <msg> }`\n'
|
|
1161
1459
|
" BEFORE any `Agent(...)` worker dispatch.\n"
|
|
1162
1460
|
"4. Only after `teamCreate` is persisted may you dispatch workers — with\n"
|
|
1163
1461
|
" `team_name` on success, or with `run_in_background: true` and no\n"
|
|
1164
|
-
|
|
1462
|
+
' `team_name` ONLY when `teamCreate.status == "error"` was recorded.\n'
|
|
1165
1463
|
"\n"
|
|
1166
|
-
|
|
1167
|
-
"
|
|
1464
|
+
'If the Agent tool rejects a dispatch with `"team must be created first"` /\n'
|
|
1465
|
+
'`"team을 먼저 생성하거나 team_name 없이 호출해야 합니다"`, the correct\n'
|
|
1168
1466
|
"response is to go back to step 2 — NOT to strip `team_name` and retry."
|
|
1169
1467
|
)
|
|
1170
1468
|
|
|
@@ -1178,7 +1476,9 @@ def render_template_file(template_path: str, output_path: str, ctx: dict) -> Non
|
|
|
1178
1476
|
"{{BRIEF_RELATIVE_PATH}}": ctx.get("BRIEF_RELATIVE_PATH", ""),
|
|
1179
1477
|
"{{BRIEF_FILE_PATH}}": ctx.get("BRIEF_FILE_PATH", ""),
|
|
1180
1478
|
"{{CLARIFICATION_RESPONSE_PATH}}": ctx.get("CLARIFICATION_RESPONSE_FILE", ""),
|
|
1181
|
-
"{{CLARIFICATION_RESPONSE_RELATIVE_PATH}}": ctx.get(
|
|
1479
|
+
"{{CLARIFICATION_RESPONSE_RELATIVE_PATH}}": ctx.get(
|
|
1480
|
+
"CLARIFICATION_RESPONSE_RELATIVE_PATH", ""
|
|
1481
|
+
),
|
|
1182
1482
|
"{{RUN_DIR}}": ctx.get("RUN_DIR", ""),
|
|
1183
1483
|
"{{RUN_DIR_RELATIVE_PATH}}": ctx.get("RUN_DIR_RELATIVE_PATH", ""),
|
|
1184
1484
|
"{{RUN_MANIFESTS_RELATIVE_PATH}}": ctx.get("RUN_MANIFESTS_RELATIVE_PATH", ""),
|
|
@@ -1205,31 +1505,63 @@ def render_template_file(template_path: str, output_path: str, ctx: dict) -> Non
|
|
|
1205
1505
|
"{{WORKER_RESULTS_RELATIVE_PATH}}": ctx.get("WORKER_RESULTS_RELATIVE_PATH", ""),
|
|
1206
1506
|
"{{RUN_VALIDATOR_PATH}}": ctx.get("RUN_VALIDATOR_SCRIPT", ""),
|
|
1207
1507
|
"{{RUN_VALIDATOR_RELATIVE_PATH}}": ctx.get("RUN_VALIDATOR_RELATIVE_PATH", ""),
|
|
1208
|
-
"{{CLAUDE_WORKER_RESULT_RELATIVE_PATH}}": ctx.get(
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
"{{
|
|
1508
|
+
"{{CLAUDE_WORKER_RESULT_RELATIVE_PATH}}": ctx.get(
|
|
1509
|
+
"CLAUDE_WORKER_RESULT_RELATIVE_PATH", ""
|
|
1510
|
+
),
|
|
1511
|
+
"{{CODEX_WORKER_RESULT_RELATIVE_PATH}}": ctx.get(
|
|
1512
|
+
"CODEX_WORKER_RESULT_RELATIVE_PATH", ""
|
|
1513
|
+
),
|
|
1514
|
+
"{{GEMINI_WORKER_RESULT_RELATIVE_PATH}}": ctx.get(
|
|
1515
|
+
"GEMINI_WORKER_RESULT_RELATIVE_PATH", ""
|
|
1516
|
+
),
|
|
1517
|
+
"{{REPORT_WRITER_WORKER_RESULT_RELATIVE_PATH}}": ctx.get(
|
|
1518
|
+
"REPORT_WRITER_WORKER_RESULT_RELATIVE_PATH", ""
|
|
1519
|
+
),
|
|
1212
1520
|
"{{RUN_ERRORS_LOG_PATH}}": ctx.get("RUN_ERRORS_LOG_FILE", ""),
|
|
1213
1521
|
"{{RUN_ERRORS_LOG_RELATIVE_PATH}}": ctx.get("RUN_ERRORS_LOG_RELATIVE_PATH", ""),
|
|
1214
|
-
"{{CLAUDE_WORKER_ERRORS_SIDECAR_PATH}}": ctx.get(
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
"{{
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
"{{
|
|
1221
|
-
|
|
1522
|
+
"{{CLAUDE_WORKER_ERRORS_SIDECAR_PATH}}": ctx.get(
|
|
1523
|
+
"CLAUDE_WORKER_ERRORS_SIDECAR_FILE", ""
|
|
1524
|
+
),
|
|
1525
|
+
"{{CLAUDE_WORKER_ERRORS_SIDECAR_RELATIVE_PATH}}": ctx.get(
|
|
1526
|
+
"CLAUDE_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", ""
|
|
1527
|
+
),
|
|
1528
|
+
"{{CODEX_WORKER_ERRORS_SIDECAR_PATH}}": ctx.get(
|
|
1529
|
+
"CODEX_WORKER_ERRORS_SIDECAR_FILE", ""
|
|
1530
|
+
),
|
|
1531
|
+
"{{CODEX_WORKER_ERRORS_SIDECAR_RELATIVE_PATH}}": ctx.get(
|
|
1532
|
+
"CODEX_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", ""
|
|
1533
|
+
),
|
|
1534
|
+
"{{GEMINI_WORKER_ERRORS_SIDECAR_PATH}}": ctx.get(
|
|
1535
|
+
"GEMINI_WORKER_ERRORS_SIDECAR_FILE", ""
|
|
1536
|
+
),
|
|
1537
|
+
"{{GEMINI_WORKER_ERRORS_SIDECAR_RELATIVE_PATH}}": ctx.get(
|
|
1538
|
+
"GEMINI_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", ""
|
|
1539
|
+
),
|
|
1540
|
+
"{{REPORT_WRITER_WORKER_ERRORS_SIDECAR_PATH}}": ctx.get(
|
|
1541
|
+
"REPORT_WRITER_WORKER_ERRORS_SIDECAR_FILE", ""
|
|
1542
|
+
),
|
|
1543
|
+
"{{REPORT_WRITER_WORKER_ERRORS_SIDECAR_RELATIVE_PATH}}": ctx.get(
|
|
1544
|
+
"REPORT_WRITER_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", ""
|
|
1545
|
+
),
|
|
1222
1546
|
"{{LEAD_MODEL}}": lead_model,
|
|
1223
1547
|
"{{LEAD_MODEL_EXECUTION_VALUE}}": lead_model_execution,
|
|
1224
1548
|
"{{OKSTRA_VERSION}}": ctx.get("OKSTRA_VERSION", ""),
|
|
1225
1549
|
"{{CLAUDE_WORKER_MODEL}}": ctx.get("CLAUDE_WORKER_MODEL_DISPLAY", ""),
|
|
1226
|
-
"{{CLAUDE_WORKER_MODEL_EXECUTION_VALUE}}": ctx.get(
|
|
1550
|
+
"{{CLAUDE_WORKER_MODEL_EXECUTION_VALUE}}": ctx.get(
|
|
1551
|
+
"CLAUDE_WORKER_MODEL_EXECUTION_VALUE", ""
|
|
1552
|
+
),
|
|
1227
1553
|
"{{CODEX_WORKER_MODEL}}": ctx.get("CODEX_WORKER_MODEL_DISPLAY", ""),
|
|
1228
|
-
"{{CODEX_WORKER_MODEL_EXECUTION_VALUE}}": ctx.get(
|
|
1554
|
+
"{{CODEX_WORKER_MODEL_EXECUTION_VALUE}}": ctx.get(
|
|
1555
|
+
"CODEX_WORKER_MODEL_EXECUTION_VALUE", ""
|
|
1556
|
+
),
|
|
1229
1557
|
"{{GEMINI_WORKER_MODEL}}": ctx.get("GEMINI_WORKER_MODEL_DISPLAY", ""),
|
|
1230
|
-
"{{GEMINI_WORKER_MODEL_EXECUTION_VALUE}}": ctx.get(
|
|
1558
|
+
"{{GEMINI_WORKER_MODEL_EXECUTION_VALUE}}": ctx.get(
|
|
1559
|
+
"GEMINI_WORKER_MODEL_EXECUTION_VALUE", ""
|
|
1560
|
+
),
|
|
1231
1561
|
"{{REPORT_WRITER_MODEL}}": ctx.get("REPORT_WRITER_MODEL_DISPLAY", ""),
|
|
1232
|
-
"{{REPORT_WRITER_MODEL_EXECUTION_VALUE}}": ctx.get(
|
|
1562
|
+
"{{REPORT_WRITER_MODEL_EXECUTION_VALUE}}": ctx.get(
|
|
1563
|
+
"REPORT_WRITER_MODEL_EXECUTION_VALUE", ""
|
|
1564
|
+
),
|
|
1233
1565
|
"{{WORKER_RESULT_PATH_LINES}}": "\n".join(worker_result_lines),
|
|
1234
1566
|
"{{MODEL_ASSIGNMENT_LINES}}": "\n".join(model_assignment_lines),
|
|
1235
1567
|
"{{TEAM_ROLE_LINES}}": "\n".join(team_role_lines),
|
|
@@ -1239,30 +1571,48 @@ def render_template_file(template_path: str, output_path: str, ctx: dict) -> Non
|
|
|
1239
1571
|
"{{EXECUTION_STATUS_EXACT_ENTRIES}}": ", ".join(execution_status_entries),
|
|
1240
1572
|
"{{EXECUTION_STATUS_TABLE_ROWS}}": "\n".join(execution_status_table_lines),
|
|
1241
1573
|
"{{FINAL_REPORT_TEMPLATE_PATH}}": ctx.get("FINAL_REPORT_TEMPLATE_FILE", ""),
|
|
1242
|
-
"{{FINAL_REPORT_TEMPLATE_RELATIVE_PATH}}": ctx.get(
|
|
1243
|
-
|
|
1574
|
+
"{{FINAL_REPORT_TEMPLATE_RELATIVE_PATH}}": ctx.get(
|
|
1575
|
+
"FINAL_REPORT_TEMPLATE_RELATIVE_PATH", ""
|
|
1576
|
+
),
|
|
1577
|
+
"{{REFERENCE_EXPECTATIONS_RELATIVE_PATH}}": ctx.get(
|
|
1578
|
+
"REFERENCE_EXPECTATIONS_RELATIVE_PATH", ""
|
|
1579
|
+
),
|
|
1244
1580
|
"{{CLAUDE_SESSION_ID}}": ctx.get("CLAUDE_SESSION_ID", ""),
|
|
1245
1581
|
"{{CLAUDE_RESUME_COMMAND_PATH}}": ctx.get("CLAUDE_RESUME_COMMAND_FILE", ""),
|
|
1246
|
-
"{{CLAUDE_RESUME_COMMAND_RELATIVE_PATH}}": ctx.get(
|
|
1582
|
+
"{{CLAUDE_RESUME_COMMAND_RELATIVE_PATH}}": ctx.get(
|
|
1583
|
+
"CLAUDE_RESUME_COMMAND_RELATIVE_PATH", ""
|
|
1584
|
+
),
|
|
1247
1585
|
"{{TASK_ROOT_RELATIVE_PATH}}": ctx.get("TASK_ROOT_RELATIVE_PATH", ""),
|
|
1248
1586
|
"{{TASK_MANIFEST_RELATIVE_PATH}}": ctx.get("TASK_MANIFEST_RELATIVE_PATH", ""),
|
|
1249
1587
|
"{{TASK_INDEX_RELATIVE_PATH}}": ctx.get("TASK_INDEX_RELATIVE_PATH", ""),
|
|
1250
|
-
"{{INSTRUCTION_SET_RELATIVE_PATH}}": ctx.get(
|
|
1588
|
+
"{{INSTRUCTION_SET_RELATIVE_PATH}}": ctx.get(
|
|
1589
|
+
"INSTRUCTION_SET_RELATIVE_PATH", ""
|
|
1590
|
+
),
|
|
1251
1591
|
"{{RUNS_RELATIVE_PATH}}": ctx.get("RUNS_RELATIVE_PATH", ""),
|
|
1252
1592
|
"{{HISTORY_RELATIVE_PATH}}": ctx.get("HISTORY_RELATIVE_PATH", ""),
|
|
1253
|
-
"{{OKSTRA_DISCOVERY_RELATIVE_PATH}}": ctx.get(
|
|
1254
|
-
|
|
1255
|
-
|
|
1593
|
+
"{{OKSTRA_DISCOVERY_RELATIVE_PATH}}": ctx.get(
|
|
1594
|
+
"OKSTRA_DISCOVERY_RELATIVE_PATH", ""
|
|
1595
|
+
),
|
|
1596
|
+
"{{OKSTRA_LATEST_TASK_RELATIVE_PATH}}": ctx.get(
|
|
1597
|
+
"OKSTRA_LATEST_TASK_RELATIVE_PATH", ""
|
|
1598
|
+
),
|
|
1599
|
+
"{{OKSTRA_TASK_CATALOG_RELATIVE_PATH}}": ctx.get(
|
|
1600
|
+
"OKSTRA_TASK_CATALOG_RELATIVE_PATH", ""
|
|
1601
|
+
),
|
|
1256
1602
|
"{{LATEST_RUN_RELATIVE_PATH}}": ctx.get("LATEST_RUN_RELATIVE_PATH", ""),
|
|
1257
1603
|
"{{LATEST_REPORT_RELATIVE_PATH}}": ctx.get("LATEST_REPORT_RELATIVE_PATH", ""),
|
|
1258
1604
|
"{{TIMELINE_RELATIVE_PATH}}": ctx.get("TIMELINE_RELATIVE_PATH", ""),
|
|
1259
1605
|
"{{CURRENT_TASK_STATUS}}": ctx.get("CURRENT_TASK_STATUS", ""),
|
|
1260
1606
|
"{{CURRENT_RUN_STATUS}}": ctx.get("CURRENT_RUN_STATUS", ""),
|
|
1261
1607
|
"{{VALIDATION_STATUS}}": ctx.get("VALIDATION_STATUS", "not-run"),
|
|
1262
|
-
"{{RELATED_TASKS_BULLETS}}": ctx.get(
|
|
1608
|
+
"{{RELATED_TASKS_BULLETS}}": ctx.get(
|
|
1609
|
+
"RELATED_TASKS_BULLETS", "- None recorded"
|
|
1610
|
+
),
|
|
1263
1611
|
"{{RELATED_TASKS_INLINE}}": ctx.get("RELATED_TASKS_INLINE", "None"),
|
|
1264
1612
|
"{{WORKFLOW_CURRENT_PHASE}}": ctx.get("WORKFLOW_CURRENT_PHASE", ""),
|
|
1265
|
-
"{{WORKFLOW_NEXT_RECOMMENDED_PHASE}}": ctx.get(
|
|
1613
|
+
"{{WORKFLOW_NEXT_RECOMMENDED_PHASE}}": ctx.get(
|
|
1614
|
+
"WORKFLOW_NEXT_RECOMMENDED_PHASE", ""
|
|
1615
|
+
),
|
|
1266
1616
|
"{{PHASE_ALLOWED_OUTPUTS}}": ctx.get("PHASE_ALLOWED_OUTPUTS", ""),
|
|
1267
1617
|
"{{PHASE_FORBIDDEN_ACTIONS}}": ctx.get("PHASE_FORBIDDEN_ACTIONS", ""),
|
|
1268
1618
|
"{{AVAILABLE_MCP_SERVERS}}": ctx.get(
|
|
@@ -1281,6 +1631,7 @@ def render_template_file(template_path: str, output_path: str, ctx: dict) -> Non
|
|
|
1281
1631
|
rendered = template
|
|
1282
1632
|
for k, v in mapping.items():
|
|
1283
1633
|
rendered = rendered.replace(k, v)
|
|
1634
|
+
rendered = _strip_phase_blocks(rendered, ctx.get("ANALYSIS_TYPE", ""))
|
|
1284
1635
|
_write_text(Path(output_path), rendered.rstrip() + "\n")
|
|
1285
1636
|
|
|
1286
1637
|
|