okstra 0.71.2 → 0.72.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 +14 -1
- package/docs/kr/cli.md +7 -1
- package/docs/superpowers/plans/2026-06-11-fix-cycle.md +1290 -0
- package/docs/superpowers/specs/2026-06-11-fix-cycle-design.md +94 -0
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/bin/lib/okstra/cli.sh +5 -1
- package/runtime/bin/lib/okstra/globals.sh +1 -0
- package/runtime/bin/lib/okstra/usage.sh +6 -1
- package/runtime/bin/okstra.sh +1 -0
- package/runtime/prompts/wizard/prompts.ko.json +9 -0
- package/runtime/python/okstra_ctl/analysis_packet.py +17 -0
- package/runtime/python/okstra_ctl/fix_cycles.py +172 -0
- package/runtime/python/okstra_ctl/render.py +28 -4
- package/runtime/python/okstra_ctl/run.py +93 -0
- package/runtime/python/okstra_ctl/run_context.py +15 -9
- package/runtime/python/okstra_ctl/wizard.py +64 -4
- package/runtime/schemas/final-report-v1.0.schema.json +25 -0
- package/runtime/skills/okstra-brief/SKILL.md +8 -0
- package/runtime/skills/okstra-report-writer/SKILL.md +2 -0
- package/runtime/skills/okstra-run/SKILL.md +2 -1
- package/runtime/templates/project-docs/task-index.template.md +1 -0
- package/runtime/templates/reports/final-report.template.md +14 -0
- package/runtime/validators/validate-run.py +41 -0
- package/src/render-bundle.mjs +4 -1
|
@@ -103,15 +103,14 @@ def task_mutex(task_key: str) -> Iterator[None]:
|
|
|
103
103
|
|
|
104
104
|
|
|
105
105
|
@contextmanager
|
|
106
|
-
def
|
|
107
|
-
"""
|
|
108
|
-
|
|
109
|
-
lock 은
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
path = plan_run_root / ".consumers.lock"
|
|
106
|
+
def dir_flock(dir_path: Path, lock_filename: str) -> Iterator[None]:
|
|
107
|
+
"""dir_path 아래 lock_filename 파일 기반 exclusive flock.
|
|
108
|
+
|
|
109
|
+
lock 은 보호 대상 파일과 같은 디렉토리에 두어 디렉토리마다 1:1 로
|
|
110
|
+
격리한다 (마지막 세그먼트만 키로 쓰면 다른 task 의 동일 seq 가 같은
|
|
111
|
+
lock 을 공유하므로 금지)."""
|
|
112
|
+
dir_path.mkdir(parents=True, exist_ok=True)
|
|
113
|
+
path = dir_path / lock_filename
|
|
115
114
|
path.touch(exist_ok=True)
|
|
116
115
|
with path.open("r+") as f:
|
|
117
116
|
fcntl.flock(f.fileno(), fcntl.LOCK_EX)
|
|
@@ -121,6 +120,13 @@ def consumers_mutex(plan_run_root: Path) -> Iterator[None]:
|
|
|
121
120
|
fcntl.flock(f.fileno(), fcntl.LOCK_UN)
|
|
122
121
|
|
|
123
122
|
|
|
123
|
+
@contextmanager
|
|
124
|
+
def consumers_mutex(plan_run_root: Path) -> Iterator[None]:
|
|
125
|
+
"""plan run-root 별 consumers.jsonl append mutex."""
|
|
126
|
+
with dir_flock(plan_run_root, ".consumers.lock"):
|
|
127
|
+
yield
|
|
128
|
+
|
|
129
|
+
|
|
124
130
|
def _atomic_write_json(path: Path, payload: dict) -> None:
|
|
125
131
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
126
132
|
tmp = path.with_suffix(path.suffix + ".tmp")
|
|
@@ -50,7 +50,7 @@ from okstra_ctl.workers import (
|
|
|
50
50
|
validate_workers_against_profile,
|
|
51
51
|
)
|
|
52
52
|
from okstra_ctl.workflow import PHASE_SEQUENCE
|
|
53
|
-
from okstra_ctl import worktree_registry
|
|
53
|
+
from okstra_ctl import fix_cycles, worktree_registry
|
|
54
54
|
from okstra_ctl.worktree import (
|
|
55
55
|
compute_worktree_path,
|
|
56
56
|
is_git_work_tree,
|
|
@@ -58,7 +58,7 @@ from okstra_ctl.worktree import (
|
|
|
58
58
|
preview_stage_worktree_decision,
|
|
59
59
|
preview_worktree_decision,
|
|
60
60
|
)
|
|
61
|
-
from okstra_ctl.paths import task_runs_dir
|
|
61
|
+
from okstra_ctl.paths import task_dir, task_runs_dir
|
|
62
62
|
from okstra_ctl.run_context import latest_run_inputs
|
|
63
63
|
from okstra_project.dirs import project_json_path
|
|
64
64
|
from okstra_project.state import (
|
|
@@ -268,6 +268,7 @@ S_CLARIFICATION = "clarification"
|
|
|
268
268
|
S_PR_TEMPLATE_PICK = "pr_template_pick"
|
|
269
269
|
S_PR_TEMPLATE = "pr_template"
|
|
270
270
|
S_PR_TEMPLATE_SCOPE = "pr_template_scope"
|
|
271
|
+
S_FIX_CYCLE_CONFIRM = "fix_cycle_confirm"
|
|
271
272
|
S_BRANCH_CONFIRM = "branch_confirm"
|
|
272
273
|
S_CONFIRM = "confirm"
|
|
273
274
|
S_EDIT_TARGET = "edit_target"
|
|
@@ -376,6 +377,8 @@ class WizardState:
|
|
|
376
377
|
last_pr_template_cached: str = ""
|
|
377
378
|
|
|
378
379
|
# confirm / edit
|
|
380
|
+
# "" | "yes" | "no" — done(release-handoff) task 재진입의 fix-cycle 기록 여부
|
|
381
|
+
fix_cycle: str = ""
|
|
379
382
|
branch_confirmed: Optional[bool] = None
|
|
380
383
|
confirmed: Optional[bool] = None
|
|
381
384
|
edit_target: str = ""
|
|
@@ -796,6 +799,26 @@ def _branch_confirm_required(state: WizardState) -> bool:
|
|
|
796
799
|
return state.task_type != "final-verification"
|
|
797
800
|
|
|
798
801
|
|
|
802
|
+
def _fix_cycle_confirm_required(state: WizardState) -> bool:
|
|
803
|
+
"""완료(release-handoff) task 에 entry phase 로 재진입하고, 아직 열린 fix
|
|
804
|
+
cycle 이 없을 때만 묻는다."""
|
|
805
|
+
if state.task_type not in fix_cycles.FIX_CYCLE_ENTRY_PHASES:
|
|
806
|
+
return False
|
|
807
|
+
task_root = task_dir(Path(state.project_root),
|
|
808
|
+
state.task_group, state.task_id)
|
|
809
|
+
manifest = task_root / "task-manifest.json"
|
|
810
|
+
if not manifest.is_file():
|
|
811
|
+
return False
|
|
812
|
+
try:
|
|
813
|
+
data = json.loads(manifest.read_text(encoding="utf-8"))
|
|
814
|
+
except (OSError, json.JSONDecodeError):
|
|
815
|
+
return False
|
|
816
|
+
workflow = data.get("workflow") or {}
|
|
817
|
+
if workflow.get("lastCompletedPhase") != "release-handoff":
|
|
818
|
+
return False
|
|
819
|
+
return fix_cycles.open_cycle(fix_cycles.read_rows(task_root)) is None
|
|
820
|
+
|
|
821
|
+
|
|
799
822
|
def _stage_auto_allowed(state: WizardState) -> bool:
|
|
800
823
|
return state.task_type == "implementation"
|
|
801
824
|
|
|
@@ -2424,6 +2447,30 @@ def _submit_pr_template_scope(state: WizardState, value: str) -> Optional[str]:
|
|
|
2424
2447
|
return f"pr-template-scope: {value}"
|
|
2425
2448
|
|
|
2426
2449
|
|
|
2450
|
+
def _build_fix_cycle_confirm(state: WizardState) -> Prompt:
|
|
2451
|
+
t = _p(state.workspace_root, "fix_cycle_confirm")
|
|
2452
|
+
opts = t["options"]
|
|
2453
|
+
return Prompt(
|
|
2454
|
+
step=S_FIX_CYCLE_CONFIRM, kind="pick", label=t["label"],
|
|
2455
|
+
options=[
|
|
2456
|
+
_opt("yes", opts["yes"]),
|
|
2457
|
+
_opt("no", opts["no"]),
|
|
2458
|
+
_opt("abort", opts["abort"]),
|
|
2459
|
+
],
|
|
2460
|
+
echo_template=t["echo_template"])
|
|
2461
|
+
|
|
2462
|
+
|
|
2463
|
+
def _submit_fix_cycle_confirm(state: WizardState, value: str) -> Optional[str]:
|
|
2464
|
+
v = value.strip().lower()
|
|
2465
|
+
if v == "abort":
|
|
2466
|
+
state.aborted = True
|
|
2467
|
+
return "fix-cycle: abort"
|
|
2468
|
+
if v not in ("yes", "no"):
|
|
2469
|
+
raise WizardError(f"expected 'yes' / 'no' / 'abort', got: {value!r}")
|
|
2470
|
+
state.fix_cycle = v
|
|
2471
|
+
return f"fix-cycle: {v}"
|
|
2472
|
+
|
|
2473
|
+
|
|
2427
2474
|
def _build_branch_confirm(state: WizardState) -> Prompt:
|
|
2428
2475
|
if state.task_type == "implementation":
|
|
2429
2476
|
return _build_branch_confirm_impl_stage(state)
|
|
@@ -2854,14 +2901,24 @@ STEPS: list[Step] = [
|
|
|
2854
2901
|
and S_PR_TEMPLATE_SCOPE not in s.answered),
|
|
2855
2902
|
build=_build_pr_template_scope, submit=_submit_pr_template_scope,
|
|
2856
2903
|
owns=("pr_template_scope",)),
|
|
2904
|
+
Step(S_FIX_CYCLE_CONFIRM,
|
|
2905
|
+
applies=lambda s: (_ready_for_confirm(s)
|
|
2906
|
+
and _fix_cycle_confirm_required(s)
|
|
2907
|
+
and not s.fix_cycle),
|
|
2908
|
+
build=_build_fix_cycle_confirm, submit=_submit_fix_cycle_confirm,
|
|
2909
|
+
owns=("fix_cycle",)),
|
|
2857
2910
|
Step(S_BRANCH_CONFIRM,
|
|
2858
2911
|
applies=lambda s: (_ready_for_confirm(s)
|
|
2912
|
+
and (not _fix_cycle_confirm_required(s)
|
|
2913
|
+
or bool(s.fix_cycle))
|
|
2859
2914
|
and _branch_confirm_required(s)
|
|
2860
2915
|
and s.branch_confirmed is None),
|
|
2861
2916
|
build=_build_branch_confirm, submit=_submit_branch_confirm,
|
|
2862
2917
|
owns=("branch_confirmed",)),
|
|
2863
2918
|
Step(S_CONFIRM,
|
|
2864
2919
|
applies=lambda s: (_ready_for_confirm(s)
|
|
2920
|
+
and (not _fix_cycle_confirm_required(s)
|
|
2921
|
+
or bool(s.fix_cycle))
|
|
2865
2922
|
and (not _branch_confirm_required(s)
|
|
2866
2923
|
or s.branch_confirmed is True)
|
|
2867
2924
|
and s.confirmed is None),
|
|
@@ -2944,9 +3001,10 @@ _FIELD_DEFAULTS: dict[str, Any] = {
|
|
|
2944
3001
|
"task_group_pending_text": False, "task_id_pending_text": False,
|
|
2945
3002
|
"profile_workers": [], "profile_optional_workers": [],
|
|
2946
3003
|
"keep_existing_brief": None,
|
|
2947
|
-
"brief_path": "", "
|
|
3004
|
+
"brief_path": "", "brief_path_pending_text": False,
|
|
3005
|
+
"reuse_worktree": None, "base_ref": "",
|
|
2948
3006
|
"base_ref_pending_text": False, "approved_plan_path": "",
|
|
2949
|
-
"approved_plan_pending_text": False,
|
|
3007
|
+
"approved_plan_pending_text": False, "approve_plan_candidate": "",
|
|
2950
3008
|
"selected_stage": "auto",
|
|
2951
3009
|
"handoff_mode": "", "handoff_stages": "",
|
|
2952
3010
|
"executor": "", "critic": "", "critic_pending_text": False,
|
|
@@ -2959,6 +3017,7 @@ _FIELD_DEFAULTS: dict[str, Any] = {
|
|
|
2959
3017
|
"clarification_response_path": "", "clarification_pending_text": False,
|
|
2960
3018
|
"pr_template_path": "", "pr_template_pending_text": False,
|
|
2961
3019
|
"pr_template_scope": "",
|
|
3020
|
+
"fix_cycle": "",
|
|
2962
3021
|
"branch_confirmed": None, "confirmed": None, "edit_target": "",
|
|
2963
3022
|
}
|
|
2964
3023
|
|
|
@@ -3126,6 +3185,7 @@ def render_args(state: WizardState) -> dict[str, str]:
|
|
|
3126
3185
|
"related-tasks": state.related_tasks_raw,
|
|
3127
3186
|
"clarification-response": state.clarification_response_path,
|
|
3128
3187
|
"pr-template-path": pr_template,
|
|
3188
|
+
"fix-cycle": state.fix_cycle,
|
|
3129
3189
|
}
|
|
3130
3190
|
|
|
3131
3191
|
|
|
@@ -625,6 +625,31 @@
|
|
|
625
625
|
}
|
|
626
626
|
},
|
|
627
627
|
|
|
628
|
+
"fixCycle": {
|
|
629
|
+
"type": "object",
|
|
630
|
+
"description": "RENDER_IF fixCycle present — the post-release bug-fix cycle this run belongs to.",
|
|
631
|
+
"required": ["cycle", "targetReport", "symptom"],
|
|
632
|
+
"additionalProperties": false,
|
|
633
|
+
"properties": {
|
|
634
|
+
"cycle": { "type": "string", "pattern": "^fc-[0-9]{2,}$" },
|
|
635
|
+
"targetReport": { "type": "string" },
|
|
636
|
+
"symptom": { "type": "string" },
|
|
637
|
+
"runs": {
|
|
638
|
+
"type": "array",
|
|
639
|
+
"items": {
|
|
640
|
+
"type": "object",
|
|
641
|
+
"required": ["taskType", "runSeq"],
|
|
642
|
+
"additionalProperties": false,
|
|
643
|
+
"properties": {
|
|
644
|
+
"taskType": { "type": "string" },
|
|
645
|
+
"runSeq": { "type": "integer" },
|
|
646
|
+
"runManifest": { "type": "string" }
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
},
|
|
652
|
+
|
|
628
653
|
"clarificationItems": {
|
|
629
654
|
"type": "array",
|
|
630
655
|
"items": { "$ref": "#/$defs/ClarificationRow" }
|
|
@@ -431,6 +431,9 @@ never error:
|
|
|
431
431
|
1. **okstra-internal (authoritative)** — always check first:
|
|
432
432
|
- `<PROJECT_ROOT>/.okstra/glossary.md` if present
|
|
433
433
|
- `<PROJECT_ROOT>/.okstra/decisions/` titles if present
|
|
434
|
+
- Fix history (when the brief targets an existing task):
|
|
435
|
+
`<PROJECT_ROOT>/.okstra/tasks/<task-group>/<task-id>/history/fix-cycles.jsonl`
|
|
436
|
+
— read every `opened`/`closed` row.
|
|
434
437
|
2. **Explicit source material only** — if the reporter cited a path outside
|
|
435
438
|
okstra's subtree, read it as source evidence only; do not treat it as
|
|
436
439
|
okstra memory.
|
|
@@ -602,6 +605,11 @@ Required sections:
|
|
|
602
605
|
- **Related Artifacts** — files, URLs, issues, prior task-keys.
|
|
603
606
|
- **Open Questions** — anything the user already flagged as undecided
|
|
604
607
|
(becomes raw material for `requirements-discovery`).
|
|
608
|
+
- **Task Continuity Notes** — if the target task's
|
|
609
|
+
`history/fix-cycles.jsonl` exists, cite each cycle here as one line —
|
|
610
|
+
`fix-cycle fc-NN (open|closed): <symptom> (target: <target_report>)`.
|
|
611
|
+
An open cycle means the new brief continues a bug-fix in progress; say
|
|
612
|
+
so explicitly.
|
|
605
613
|
|
|
606
614
|
Sections **deliberately omitted** (do NOT add them, do NOT prompt for them):
|
|
607
615
|
|
|
@@ -279,6 +279,8 @@ Section numbering follows `templates/reports/final-report.template.md` exactly
|
|
|
279
279
|
6. **Recommended Next Steps** — prioritized actions. After Phase 7's follow-up spawner runs, append a row per newly created task-key (see "Phase 6 → Phase 7 execution sequence" above).
|
|
280
280
|
7. **Follow-up Tasks** — auto-spawn-eligible table. Each row drives `okstra-spawn-followups.py`; see template §7 for the row schema.
|
|
281
281
|
|
|
282
|
+
**§5.10 Fix History (data-presence gated).** When the run-manifest carries a `fixCycleId`, fill the data.json `fixCycle` block (`cycle` / `targetReport` / `symptom` / `runs`). Read the values from the task root's `history/fix-cycles.jsonl`: `cycle` MUST equal `fixCycleId`, `targetReport` / `symptom` come from that cycle's `opened` row, and `runs` lists its attached `run` rows (`taskType` / `runSeq` / `runManifest`). The validator (`validators/validate-run.py` → `_validate_fix_cycle`) fails the run when the block is missing or `fixCycle.cycle` does not match `fixCycleId`. When the run-manifest has no `fixCycleId`, OMIT the `fixCycle` block entirely — the renderer omits §5.10.
|
|
283
|
+
|
|
282
284
|
### Writing Guidelines
|
|
283
285
|
|
|
284
286
|
- Write in Markdown. **Prefer tables over prose bullet lists** for any section that enumerates multiple items with the same shape (evidence rows, risks, options, dependencies, rollback steps, follow-ups, open questions). Bullets are reserved for short, single-line standalone statements (e.g., "- 추가 정보 요청 없음."). When the template provides a table form, do NOT degrade it back to bullets in the rendered report.
|
|
@@ -178,7 +178,8 @@ okstra render-bundle \
|
|
|
178
178
|
--report-writer-model "<args.report-writer-model>" \
|
|
179
179
|
--related-tasks "<args.related-tasks>" \
|
|
180
180
|
--clarification-response "<args.clarification-response>" \
|
|
181
|
-
--pr-template-path "<args.pr-template-path>"
|
|
181
|
+
--pr-template-path "<args.pr-template-path>" \
|
|
182
|
+
--fix-cycle "<args.fix-cycle>"
|
|
182
183
|
```
|
|
183
184
|
|
|
184
185
|
`render-bundle` auto-supplies `--workspace-root` and forces `--render-only`. Stdout prints `okstra task root:`, `okstra instruction-set:`, and the full rendered lead prompt. Parse the labelled lines for `TASK_ROOT` and `INSTRUCTION_SET_PATH`. Also watch for an optional `okstra concurrent-run stages:` label line — present only when a concurrent run is detected (see "동시-run 감지 분기" below).
|
|
@@ -37,6 +37,7 @@ taskType: "{{FM_TASK_TYPE}}"
|
|
|
37
37
|
- Next Recommended Phase: `{{WORKFLOW_NEXT_RECOMMENDED_PHASE}}`
|
|
38
38
|
- Awaiting Approval: `{{WORKFLOW_AWAITING_APPROVAL}}`
|
|
39
39
|
- Routing Status: `{{WORKFLOW_ROUTING_STATUS}}`
|
|
40
|
+
- Fix cycles: {{FIX_CYCLES_SUMMARY}}
|
|
40
41
|
|
|
41
42
|
## Phase States
|
|
42
43
|
|
|
@@ -556,6 +556,20 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
556
556
|
| Cand ID | Lens | Title | Scope | Severity | Effort | Consensus | Source workers | Recommended next-phase | Evidence |
|
|
557
557
|
|---------|------|-------|-------|----------|--------|-----------|----------------|------------------------|----------|
|
|
558
558
|
|
|
559
|
+
{% endif %}
|
|
560
|
+
{% if fixCycle and fixCycle.cycle %}
|
|
561
|
+
## 5.10 Fix History
|
|
562
|
+
|
|
563
|
+
> This run belongs to a post-release bug-fix cycle registered in `history/fix-cycles.jsonl`.
|
|
564
|
+
|
|
565
|
+
- Cycle: `{{ fixCycle.cycle }}` — {{ fixCycle.symptom }}
|
|
566
|
+
- Target report: `{{ fixCycle.targetReport }}`
|
|
567
|
+
{% if fixCycle.runs %}
|
|
568
|
+
- Runs in this cycle so far:
|
|
569
|
+
{% for r in fixCycle.runs %}
|
|
570
|
+
- {{ r.taskType }} seq {{ r.runSeq }}{% if r.runManifest %} (`{{ r.runManifest }}`){% endif %}
|
|
571
|
+
{% endfor %}
|
|
572
|
+
{% endif %}
|
|
559
573
|
{% endif %}
|
|
560
574
|
## 6. Cross Verification Results
|
|
561
575
|
|
|
@@ -1369,6 +1369,19 @@ def _data_path_for(report_path: Path) -> Path:
|
|
|
1369
1369
|
return report_path.with_suffix(".data.json")
|
|
1370
1370
|
|
|
1371
1371
|
|
|
1372
|
+
def _load_final_report_data(report_path: Path) -> dict:
|
|
1373
|
+
"""Best-effort parse of the final-report data.json sibling. Returns {} when
|
|
1374
|
+
absent or unparseable — those conditions are already surfaced as failures by
|
|
1375
|
+
validate_final_report_data; this loader only feeds cross-field checks."""
|
|
1376
|
+
data_path = _data_path_for(report_path)
|
|
1377
|
+
if not data_path.is_file():
|
|
1378
|
+
return {}
|
|
1379
|
+
try:
|
|
1380
|
+
return json.loads(data_path.read_text(encoding="utf-8"))
|
|
1381
|
+
except (OSError, json.JSONDecodeError):
|
|
1382
|
+
return {}
|
|
1383
|
+
|
|
1384
|
+
|
|
1372
1385
|
def validate_final_report_data(report_path: Path, failures: list[str]) -> None:
|
|
1373
1386
|
"""Validate the final-report data.json against the v1.0 schema.
|
|
1374
1387
|
|
|
@@ -1748,6 +1761,31 @@ def _validate_improvement_discovery(
|
|
|
1748
1761
|
failures.append(f"improvement-discovery: {err}")
|
|
1749
1762
|
|
|
1750
1763
|
|
|
1764
|
+
def _validate_fix_cycle(run_manifest: dict, data: dict, failures: list[str]) -> None:
|
|
1765
|
+
"""Enforce: when the run-manifest carries a fixCycleId, the final-report
|
|
1766
|
+
data.json MUST contain a fixCycle block whose ``cycle`` matches it.
|
|
1767
|
+
|
|
1768
|
+
Direction is one-way: a fixCycle block present without a run-manifest
|
|
1769
|
+
fixCycleId is NOT rejected — same posture as the schema-optional block.
|
|
1770
|
+
This check owns only the run→report direction (no missing/mismatched
|
|
1771
|
+
block when the run is attached), never the reverse."""
|
|
1772
|
+
cycle_id = (run_manifest or {}).get("fixCycleId", "")
|
|
1773
|
+
if not cycle_id:
|
|
1774
|
+
return
|
|
1775
|
+
block = (data or {}).get("fixCycle")
|
|
1776
|
+
if not isinstance(block, dict):
|
|
1777
|
+
failures.append(
|
|
1778
|
+
f"fix-cycle: run-manifest fixCycleId={cycle_id} but data.json has "
|
|
1779
|
+
"no fixCycle block"
|
|
1780
|
+
)
|
|
1781
|
+
return
|
|
1782
|
+
if block.get("cycle") != cycle_id:
|
|
1783
|
+
failures.append(
|
|
1784
|
+
f"fix-cycle: data.json fixCycle.cycle={block.get('cycle')!r} does "
|
|
1785
|
+
f"not match run-manifest fixCycleId={cycle_id!r}"
|
|
1786
|
+
)
|
|
1787
|
+
|
|
1788
|
+
|
|
1751
1789
|
def _validate_session_conformance(
|
|
1752
1790
|
team_state: dict,
|
|
1753
1791
|
team_state_path: Path,
|
|
@@ -2157,6 +2195,9 @@ def main() -> int:
|
|
|
2157
2195
|
# safety net for hand-edited or pre-v1.0 reports.
|
|
2158
2196
|
task_type = effective_run_task_type(run_manifest, task_manifest)
|
|
2159
2197
|
validate_final_report_data(report_path, failures)
|
|
2198
|
+
_validate_fix_cycle(
|
|
2199
|
+
run_manifest, _load_final_report_data(report_path), failures
|
|
2200
|
+
)
|
|
2160
2201
|
validate_report(report_path, contract["required_agent_status_entries"], failures)
|
|
2161
2202
|
validate_team_state_usage(team_state, failures)
|
|
2162
2203
|
|
package/src/render-bundle.mjs
CHANGED
|
@@ -18,11 +18,14 @@ Usage:
|
|
|
18
18
|
[--gemini-model <m>] [--report-writer-model <m>] \\
|
|
19
19
|
[--related-tasks <list>] [--base-ref <ref>] \\
|
|
20
20
|
[--clarification-response <path>] [--work-category <cat>] \\
|
|
21
|
-
[--stage <auto|N>] [--stages <csv>] [--pr-template-path <path>]
|
|
21
|
+
[--stage <auto|N>] [--stages <csv>] [--pr-template-path <path>] \\
|
|
22
|
+
[--fix-cycle <yes|no>]
|
|
22
23
|
|
|
23
24
|
--stage implementation / final-verification only
|
|
24
25
|
--stages release-handoff only: PR stage bundle csv (empty = whole-task)
|
|
25
26
|
--pr-template-path release-handoff only
|
|
27
|
+
--fix-cycle entry phase only: record this re-entry of a done task as a
|
|
28
|
+
bug-fix cycle (yes opens/continues the cycle)
|
|
26
29
|
|
|
27
30
|
release-handoff takes NO --task-brief (briefs belong to entry phases) —
|
|
28
31
|
prepare generates the run's input document from the cited verification
|