okstra 0.32.0 → 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.
- 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/workers/codex-worker.md +1 -1
- package/runtime/agents/workers/gemini-worker.md +1 -1
- package/runtime/bin/okstra-codex-exec.sh +31 -4
- package/runtime/bin/okstra-gemini-exec.sh +32 -6
- package/runtime/prompts/profiles/_common-contract.md +1 -1
- package/runtime/python/okstra_ctl/render.py +537 -227
- package/runtime/python/okstra_ctl/wizard.py +16 -5
- package/runtime/skills/okstra-team-contract/SKILL.md +3 -3
|
@@ -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 = [
|
|
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 [
|
|
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
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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(
|
|
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(
|
|
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 = (
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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(
|
|
395
|
-
|
|
396
|
-
|
|
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(
|
|
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",
|
|
512
|
-
"
|
|
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"] + [
|
|
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(
|
|
527
|
-
|
|
528
|
-
|
|
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(
|
|
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(
|
|
550
|
-
|
|
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 =
|
|
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(
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
"
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
"
|
|
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(
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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", "")
|
|
637
|
-
|
|
638
|
-
"
|
|
639
|
-
"
|
|
640
|
-
"
|
|
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(
|
|
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",
|
|
674
|
-
"
|
|
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",
|
|
679
|
-
"
|
|
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",
|
|
803
|
+
"Missing Information and Risk",
|
|
804
|
+
"Recommended Next Actions",
|
|
682
805
|
],
|
|
683
806
|
"statusLabels": [
|
|
684
|
-
"prepared",
|
|
685
|
-
"
|
|
686
|
-
"
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
796
|
-
|
|
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(
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
"
|
|
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"]
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
934
|
-
|
|
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",
|
|
939
|
-
"
|
|
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 =
|
|
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 =
|
|
955
|
-
|
|
956
|
-
|
|
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(
|
|
965
|
-
|
|
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(
|
|
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(
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
"{{
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
"{{
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
"{{
|
|
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(
|
|
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(
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
"{{
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
"{{
|
|
1000
|
-
|
|
1001
|
-
|
|
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(
|
|
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 = [
|
|
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
|
-
|
|
1099
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1422
|
+
' `team_name` ONLY when `teamCreate.status == "error"` was recorded.\n'
|
|
1165
1423
|
"\n"
|
|
1166
|
-
|
|
1167
|
-
"
|
|
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(
|
|
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(
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
"{{
|
|
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(
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
"{{
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
"{{
|
|
1221
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
1243
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
1254
|
-
|
|
1255
|
-
|
|
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(
|
|
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(
|
|
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(
|