okstra 0.219.0 → 0.219.1
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/README.md +5 -5
- package/dist/cli-registry.mjs +0 -10
- package/dist/cli-registry.mjs.map +1 -1
- package/dist/commands/lifecycle/config.mjs +2 -3
- package/dist/commands/lifecycle/config.mjs.map +1 -1
- package/dist/lib/runtime-payload.mjs +1 -2
- package/dist/lib/runtime-payload.mjs.map +1 -1
- package/docs/architecture/storage-model.md +1 -1
- package/docs/architecture.md +9 -7
- package/docs/cli.md +2 -3
- package/docs/project-structure-overview.md +3 -4
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/prompts/launch.template.md +1 -1
- package/runtime/prompts/lead/okstra-lead-contract.md +1 -3
- package/runtime/prompts/lead/report-writer.md +10 -17
- package/runtime/prompts/lead/team-contract.md +1 -2
- package/runtime/prompts/wizard/prompts.ko.json +0 -1
- package/runtime/python/okstra_ctl/adapters/hosts/codex/relay.md +1 -1
- package/runtime/python/okstra_ctl/adapters/providers/antigravity/adapter.py +1 -1
- package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +0 -1
- package/runtime/python/okstra_ctl/adapters/providers/codex/adapter.py +1 -1
- package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +0 -1
- package/runtime/python/okstra_ctl/adapters/providers/kimi/adapter.py +0 -1
- package/runtime/python/okstra_ctl/agent/invocation.py +1 -2
- package/runtime/python/okstra_ctl/agent/prompt_cli/cli.py +2 -2
- package/runtime/python/okstra_ctl/agent/prompt_cli/jobs.py +0 -2
- package/runtime/python/okstra_ctl/agent/prompt_cli/materialize.py +2 -27
- package/runtime/python/okstra_ctl/agent/prompt_cli/run_identity.py +0 -4
- package/runtime/python/okstra_ctl/dispatch_core.py +3 -349
- package/runtime/python/okstra_ctl/dispatch_state.py +0 -13
- package/runtime/python/okstra_ctl/domain/provider.py +1 -1
- package/runtime/python/okstra_ctl/domain/role.py +0 -6
- package/runtime/python/okstra_ctl/final_report_paths.py +0 -16
- package/runtime/python/okstra_ctl/i18n.py +2 -2
- package/runtime/python/okstra_ctl/path_hints.py +0 -4
- package/runtime/python/okstra_ctl/paths.py +0 -3
- package/runtime/python/okstra_ctl/phases/final_verification/profile.md +1 -0
- package/runtime/python/okstra_ctl/phases/final_verification/spec.md +2 -0
- package/runtime/python/okstra_ctl/phases/final_verification/target.py +17 -1
- package/runtime/python/okstra_ctl/phases/implementation/entry.py +12 -0
- package/runtime/python/okstra_ctl/phases/implementation/instructions/_implementation-deliverable.md +1 -0
- package/runtime/python/okstra_ctl/phases/implementation/instructions/_implementation-executor.md +1 -0
- package/runtime/python/okstra_ctl/phases/implementation/spec.md +4 -0
- package/runtime/python/okstra_ctl/phases/release_handoff/boundary.json +1 -1
- package/runtime/python/okstra_ctl/phases/release_handoff/operations.py +24 -3
- package/runtime/python/okstra_ctl/phases/release_handoff/profile.md +9 -9
- package/runtime/python/okstra_ctl/phases/release_handoff/spec.md +4 -2
- package/runtime/python/okstra_ctl/render.py +0 -10
- package/runtime/python/okstra_ctl/render_final_report.py +4 -4
- package/runtime/python/okstra_ctl/report_finalize.py +37 -76
- package/runtime/python/okstra_ctl/report_html/render.py +4 -44
- package/runtime/python/okstra_ctl/report_language.py +3 -3
- package/runtime/python/okstra_ctl/report_prose_keys.py +153 -0
- package/runtime/python/okstra_ctl/report_synthesis_packet.py +1 -1
- package/runtime/python/okstra_ctl/run.py +1 -21
- package/runtime/python/okstra_ctl/stage_integrate.py +37 -2
- package/runtime/python/okstra_ctl/stage_rebase.py +189 -0
- package/runtime/python/okstra_ctl/stage_targets.py +29 -62
- package/runtime/python/okstra_ctl/team.py +1 -6
- package/runtime/python/okstra_ctl/wizard/confirmation.py +0 -8
- package/runtime/python/okstra_ctl/wizard/registry.py +2 -3
- package/runtime/python/okstra_ctl/wizard/sources.py +10 -0
- package/runtime/python/okstra_ctl/worker_prompt_policy.py +3 -12
- package/runtime/python/okstra_ctl/worktree/__init__.py +1 -1
- package/runtime/python/okstra_ctl/worktree/decisions.py +3 -4
- package/runtime/python/okstra_ctl/worktree/provision.py +27 -0
- package/runtime/python/okstra_ctl/worktree/rebase.py +108 -0
- package/runtime/python/okstra_ctl/worktree_registry.py +14 -0
- package/runtime/skills/_fragments/wizard-render-bundle.md +1 -1
- package/runtime/skills/okstra-discussion/SKILL.md +1 -1
- package/runtime/skills/okstra-run/SKILL.md +1 -1
- package/runtime/skills/okstra-setup/SKILL.md +2 -2
- package/runtime/skills/okstra-setup/references/project-config.md +2 -2
- package/runtime/templates/report-writer-prompt-preamble.md +1 -1
- package/runtime/templates/reports/html/i18n/en.json +0 -1
- package/runtime/templates/reports/html/i18n/ko.json +0 -1
- package/runtime/templates/worker-prompt-preamble.md +1 -1
- package/runtime/validators/validate-run.py +1 -28
- package/runtime/agents/operations/report-translation.json +0 -6
- package/runtime/agents/roles/translator.json +0 -19
- package/runtime/bin/okstra-report-translate.py +0 -404
- package/runtime/prompts/duties/translator.json +0 -32
- package/runtime/python/okstra_ctl/report_translation.py +0 -509
- package/runtime/python/okstra_ctl/report_translation_dispatch.py +0 -356
- package/runtime/templates/translator-prompt-preamble.md +0 -36
|
@@ -6,7 +6,6 @@ from pathlib import Path
|
|
|
6
6
|
|
|
7
7
|
DATA_JSON_SUFFIX = ".data.json"
|
|
8
8
|
MARKDOWN_SUFFIX = ".md"
|
|
9
|
-
TRANSLATION_SOURCE_SUFFIX = ".translation-source.json"
|
|
10
9
|
|
|
11
10
|
|
|
12
11
|
def _stem(data_path: Path) -> str:
|
|
@@ -106,21 +105,6 @@ def timeline_report_record_rel(run: dict) -> str:
|
|
|
106
105
|
return report_record_rel_from_legacy_pointer(str(run.get("reportPath") or ""))
|
|
107
106
|
|
|
108
107
|
|
|
109
|
-
def translation_source_path(data_path: Path) -> Path:
|
|
110
|
-
"""Return the translator's work list for a final-report data.json path."""
|
|
111
|
-
return data_path.with_name(_stem(data_path) + TRANSLATION_SOURCE_SUFFIX)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
def translation_sidecar_path(data_path: Path, lang: str) -> Path:
|
|
115
|
-
"""Return the filled translation sidecar the HTML renderer overlays.
|
|
116
|
-
|
|
117
|
-
One file per language beside the report it translates, so the renderer
|
|
118
|
-
finds it from the data.json alone and a run for another language does not
|
|
119
|
-
overwrite it.
|
|
120
|
-
"""
|
|
121
|
-
return data_path.with_name(f"{_stem(data_path)}.i18n.{lang}.json")
|
|
122
|
-
|
|
123
|
-
|
|
124
108
|
def sidecar_source_rel(path: Path) -> str:
|
|
125
109
|
"""사이드카 프론트매터가 `source-report` / `source-data` 에 적는 경로.
|
|
126
110
|
|
|
@@ -47,8 +47,8 @@ def load_dictionary(lang: str, rel: tuple[str, ...] = DICTIONARY_REL) -> dict[st
|
|
|
47
47
|
"""`<lang>.json` 이 있으면 그것, 없으면 영어 사전.
|
|
48
48
|
|
|
49
49
|
사전은 페이지 크롬(표 머리글·빈 상태·enum 라벨)의 고정 문자열이고 본문은
|
|
50
|
-
|
|
51
|
-
영어로 나온다 — 사전이 없다고 렌더를 거절하면 독자는 아무것도 못 받는다.
|
|
50
|
+
report-writer 가 리포트 언어로 쓴다. 사전이 없는 언어의 리포트는 본문은 그
|
|
51
|
+
언어, 크롬은 영어로 나온다 — 사전이 없다고 렌더를 거절하면 독자는 아무것도 못 받는다.
|
|
52
52
|
"""
|
|
53
53
|
directory = _i18n_dir(rel)
|
|
54
54
|
path = directory / f"{lang}.json"
|
|
@@ -119,7 +119,6 @@ def hydrate_active_run_context(payload: Mapping[str, Any]) -> dict[str, Any]:
|
|
|
119
119
|
"IMPLEMENTATION_WORKER_PREAMBLE_PATH", ""
|
|
120
120
|
),
|
|
121
121
|
"report-writer": ctx.get("REPORT_WRITER_PREAMBLE_PATH", ""),
|
|
122
|
-
"translator": ctx.get("TRANSLATOR_PREAMBLE_PATH", ""),
|
|
123
122
|
},
|
|
124
123
|
"workerErrorContractPath": ctx.get("WORKER_ERROR_CONTRACT_PATH", ""),
|
|
125
124
|
"hostSessionContractPath": (
|
|
@@ -515,9 +514,6 @@ def _runtime_fields(paths: Mapping[str, Any]) -> dict[str, str]:
|
|
|
515
514
|
"REPORT_WRITER_PREAMBLE_PATH": str(
|
|
516
515
|
runtime_home / "templates" / "report-writer-prompt-preamble.md"
|
|
517
516
|
),
|
|
518
|
-
"TRANSLATOR_PREAMBLE_PATH": str(
|
|
519
|
-
runtime_home / "templates" / "translator-prompt-preamble.md"
|
|
520
|
-
),
|
|
521
517
|
"WORKER_ERROR_CONTRACT_PATH": str(
|
|
522
518
|
runtime_home / "templates" / "worker-error-contract.md"
|
|
523
519
|
),
|
|
@@ -663,9 +663,6 @@ def compute_run_paths(
|
|
|
663
663
|
"REPORT_WRITER_PREAMBLE_PATH": str(
|
|
664
664
|
runtime_home / "templates" / "report-writer-prompt-preamble.md"
|
|
665
665
|
),
|
|
666
|
-
"TRANSLATOR_PREAMBLE_PATH": str(
|
|
667
|
-
runtime_home / "templates" / "translator-prompt-preamble.md"
|
|
668
|
-
),
|
|
669
666
|
"WORKER_ERROR_CONTRACT_PATH": str(
|
|
670
667
|
runtime_home / "templates" / "worker-error-contract.md"
|
|
671
668
|
),
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
- **whole-task mutates only when it has to.** When one stage branch already contains every other done stage's commit — the usual shape of a linear plan, since each stage branches from its predecessor's done commit — that branch IS the whole task, and verification runs in that stage's worktree with no merge at all (`stage_targets.containing_stage`). Only when the stage graph has several tips does entry auto-merge (with `--no-ff`) the done stages into the task branch to create an integration commit; that case is the mutating one. The stage worktrees are NOT removed on entry: they are reclaimed after the verdict, by the Phase 7 `teardown-stages` step, and only when the verdict clears the work for release (`accepted`, or `conditional-accept` with no condition blocking release). A `blocked` verdict therefore leaves every stage worktree in place, so the rework it routes to can start immediately. The stage branches are kept as the reviewable stack (the target of `okstra handoff local-checkout --stage <N>`). If a merge conflict occurs it reports the conflicting files and aborts (the user resolves them manually and retries). A stage worktree with uncommitted changes remaining is preserved. Therefore the "fully-merged, clean target" the entry gate above refers to is the state after this auto-integration step completes, and whole-task final-verification must be treated as a mutating phase that creates the integration commit.
|
|
27
27
|
- **single-stage scope** (`--stage N`): prep verified stage N is `status:done` and its isolated stage worktree exists and is clean. Other stages' state is irrelevant. A single-stage run is a partial verification of one stage, and that is exactly the unit release-handoff ships: an `accepted` verdict here makes the stage PR-eligible, so `release-handoff` is the routing target. It says nothing about any other stage.
|
|
28
28
|
- the lead still captures `git status --short` from the injected worktree to confirm the analysis ran against the delivered work-tree state; an unexpected divergence (dirty tree outside `.okstra/`, missing worktree) is a `tool-failure`, not a silent proceed.
|
|
29
|
+
- **Stage worktree lifecycle:** implementation finalization may already have removed a completed stage's directory. Preparation restores the preserved stage branch for single-stage verification, or the containing tip branch for a linear whole-task target. After successful report validation, a release-ready single-stage verdict reclaims that stage's clean worktree; a whole-task verdict reclaims completed stages without any cleanup-time merge. Blocked, active, dirty, nested, or changed stages remain available for rework. The branch and registry base survive cleanup. After finalization use the main project root or absolute artifact paths because the verification cwd may have been removed. Enforcement: `report_finalize._teardown_stage_worktrees`, `stage_targets.teardown_completed_stage_worktrees`, and `tests/worktree/test_stage_rebase.py`.
|
|
29
30
|
- Worker verification procedure:
|
|
30
31
|
- **Target confirmation:** analyse the injected target and nothing else. Read `verification-target.md` for the stage/report mapping and the complete diff stat. Prepare fixed that target and `phases/final_verification/validation.py` `validate_verification_target_match` (called by `validators/validate-run.py`) re-checks the report against its digest, so the procedure to follow here is simply: if the worktree you can see does not match the injected target, record a `tool-failure` — never reselect a target.
|
|
31
32
|
- **Evidence:** attach file:line, exact command + exit code, log excerpt, or MCP SELECT evidence to every finding. Mark a requirement as covered only when the cited artifact demonstrates it.
|
|
@@ -184,6 +184,8 @@ flowchart TD
|
|
|
184
184
|
|
|
185
185
|
The stage merge of whole-task mode is a runtime-owned integration step that prepare performs; the matching teardown is a runtime-owned step too, but it runs after the verdict (Phase 7 `teardown-stages`) and only when the verdict clears the work for release. After that, lead verification is read-only. Source edit, follow-up fix, and scope expansion are all forbidden. When a defect is found, it is not fixed within the current run. The lead chooses the next phase from `prompts/lead/phase-routing.md` using the blocker facts this phase recorded.
|
|
186
186
|
|
|
187
|
+
Implementation finalization may already have reclaimed completed stage worktrees. Target acquisition restores a missing single-stage worktree, or the containing tip-stage worktree for a linear whole-task target, from its preserved branch. Restoration does not merge into the task branch. After a release-ready single-stage verdict, `report-finalize` also reclaims that stage's worktree using the captured verification head; a blocked verdict keeps the restored worktree for rework. Cleanup never merges stages: `stage_targets.teardown_completed_stage_worktrees` only reclaims completed, inactive worktrees whose tips match the selected completion commits. Tests: `tests/worktree/test_stage_rebase.py`.
|
|
188
|
+
|
|
187
189
|
### 8. Verified code
|
|
188
190
|
|
|
189
191
|
- `scripts/okstra_ctl/phases/final_verification/profile.md`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""final-verification 이 검증할 대상을 고른다.
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
검증 대상 고정 전에 이동한 의존 기준을 재배치한다. 전체 작업 검증은 완료된
|
|
4
4
|
stage 전부를 담은 stage 브랜치가 있으면 그것을 쓰고, 없으면 task 브랜치에 통합한
|
|
5
5
|
결과를 쓴다. 원장·registry·Git·통합은 공통 모듈이 제공하고, 여기서는 어느 것을
|
|
6
6
|
언제 부를지만 정한다. 전체 과정은 task 키 잠금 하나 안에서 돈다.
|
|
@@ -115,6 +115,14 @@ def _read_final_verification_done_rows(
|
|
|
115
115
|
),
|
|
116
116
|
plan_run_root,
|
|
117
117
|
)
|
|
118
|
+
from okstra_ctl.stage_rebase import synchronize_stage_stack
|
|
119
|
+
from okstra_ctl.stage_targets import StageWorktreeIdentity
|
|
120
|
+
|
|
121
|
+
synchronize_stage_stack(
|
|
122
|
+
project_root=request.project_root, plan_run_root=plan_run_root,
|
|
123
|
+
stage_map=list(request.stage_map),
|
|
124
|
+
identity=StageWorktreeIdentity(project_id, task_group, task_id, request.work_category),
|
|
125
|
+
)
|
|
118
126
|
return read_stage_consumer_state(plan_run_root).done_rows
|
|
119
127
|
|
|
120
128
|
|
|
@@ -141,6 +149,10 @@ def _acquire_single_stage_target(
|
|
|
141
149
|
assert stage is not None
|
|
142
150
|
row = worktree_registry.get_stage_row(*registry_coordinates, stage)
|
|
143
151
|
worktree_path = (row or {}).get("worktree_path", "")
|
|
152
|
+
if worktree_path and not Path(worktree_path).exists():
|
|
153
|
+
from okstra_ctl.worktree.provision import restore_stage_worktree
|
|
154
|
+
|
|
155
|
+
restore_stage_worktree(request.project_root, worktree_path, row["branch"])
|
|
144
156
|
head = ""
|
|
145
157
|
if worktree_path and Path(worktree_path).is_dir():
|
|
146
158
|
head_result = _git(worktree_path, "rev-parse", "HEAD")
|
|
@@ -183,6 +195,10 @@ def _acquire_tip_stage_target(
|
|
|
183
195
|
return None
|
|
184
196
|
row = worktree_registry.get_stage_row(*registry_coordinates, tip) or {}
|
|
185
197
|
worktree_path = row.get("worktree_path", "")
|
|
198
|
+
if worktree_path and not Path(worktree_path).exists():
|
|
199
|
+
from okstra_ctl.worktree.provision import restore_stage_worktree
|
|
200
|
+
|
|
201
|
+
restore_stage_worktree(request.project_root, worktree_path, row["branch"])
|
|
186
202
|
if not worktree_path or not Path(worktree_path).is_dir():
|
|
187
203
|
return None
|
|
188
204
|
head_result = _git(worktree_path, "rev-parse", "HEAD")
|
|
@@ -95,6 +95,18 @@ def claim_implementation_stage_run(
|
|
|
95
95
|
plan_run_root = Path(inp.approved_plan_path).resolve().parents[1]
|
|
96
96
|
backfill_done_from_carry(plan_run_root)
|
|
97
97
|
auto_reconcile_best_effort(inp, plan_run_root)
|
|
98
|
+
from okstra_ctl.stage_rebase import synchronize_stage_stack
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
synchronize_stage_stack(
|
|
102
|
+
project_root=Path(inp.project_root), plan_run_root=plan_run_root,
|
|
103
|
+
stage_map=ctx_stage_map,
|
|
104
|
+
identity=stage_targets.StageWorktreeIdentity(
|
|
105
|
+
inp.project_id, task_group_segment, task_id_segment, inp.work_category,
|
|
106
|
+
),
|
|
107
|
+
)
|
|
108
|
+
except RuntimeError as exc:
|
|
109
|
+
raise ImplementationStageError(str(exc)) from exc
|
|
98
110
|
reserved_stages = _reg.list_active_stage_numbers(
|
|
99
111
|
inp.project_id,
|
|
100
112
|
inp.task_group,
|
package/runtime/python/okstra_ctl/phases/implementation/instructions/_implementation-deliverable.md
CHANGED
|
@@ -94,6 +94,7 @@ A finding that needs no code change (convergence settled it as a verifier misrea
|
|
|
94
94
|
- **A run that ends with no verifier result still closes.** When the stage ends before any verifier returned a verdict — the executor stopped without a change and the user chose not to verify, or every dispatched verifier ended without a result — append the `failed` row as above, record each roster verifier `not-run` with its reason (`okstra worker-state transition --team-state <path> --worker <worker-id> --status not-run --reason <text>`; a worker the dispatch skipped already carries one), and write the convergence state with `okstra convergence skip --run-manifest <path> --reason <text>` before dispatching the report writer. Without that state the report-writer packet refuses with `convergence … required source is missing`. The command refuses while any analyser, verifier, or critic attempt returned a result or is still running; such a run converges normally. In the report, write each verifier's `verifierResults[]` row as `verdict: not-run`, and state the reason under `Stage sidecar evidence` as `withheld`.
|
|
95
95
|
- On a non-`FAIL` verdict, for this run's single stage: write its JSON verbatim to `runs/<impl-task-key>/carry/stage-<N>.json` — after a passing lead minor fix, with the head, files, and note that §"Lead minor fix" adds. Refuse to overwrite an existing file (one stage = one sidecar; a fix run re-entering after a `failed` row writes the first one, because a withheld stage never wrote it). A stage reopened after a `done` row (see "Reopening a settled stage" below) still has the sidecar of the run that first settled it, naming a head the stage has since moved past: move it unchanged to `runs/<impl-task-key>/carry/superseded/stage-<N>-from-<that run's task-type>-<seq>.json` before writing the new one, and name both paths in `Stage sidecar evidence`. Do not rename it inside `carry/`: the readers glob `carry/stage-*.json` (`okstra_ctl.stage_map`, `okstra_ctl.consumers.backfill_done_from_carry`), so a renamed file there is read again as a live sidecar. A reopened stage whose run ends `FAIL` writes nothing and leaves the old file where it is.
|
|
96
96
|
- On a non-`FAIL` verdict, for this run's single stage: append a `status:"done"` row to `runs/<plan-task-key>/consumers.jsonl` with `completed_at`, `carry_path`, `report_path` (this run's final-report path relative to the run root), and the SHA of HEAD. Append it with `okstra_ctl.consumers.append_consumer` (NOT a raw filesystem write) — that call honours the consumers lock AND releases this stage's worktree-registry occupancy, so later runs stop seeing a finished stage as a concurrent run. `report_path` lets `final-verification` cite each stage's originating report when assembling its Source Implementation Report list.
|
|
97
|
+
- **Automatic stage worktree cleanup:** after the report passes finalization, `report-finalize` reclaims this completed stage's clean worktree. It compares the report's final commit with the completion ledger and branch tip, preserves active or unfinished stages and dirty or nested worktrees, and keeps the branch and registry base for dependency rebases and later re-entry. Never remove the directory by hand before finalization. A later implementation or final-verification entry restores it from the preserved branch. After finalization, use the main project root or absolute artifact paths: this run's stage cwd may no longer exist. Enforcement: `report_finalize._teardown_stage_worktrees`, `stage_targets.teardown_completed_stage_worktrees`, and the cleanup/restoration tests in `tests/worktree/test_stage_rebase.py`.
|
|
97
98
|
- **Reopening a settled stage.** Appending a `status:"failed"` row after a `done` one withdraws that stage: `--stage N` accepts it again, its dependents stop resolving a base from the withdrawn head, and whole-task final-verification blocks until it is settled again. Write it with `okstra stage-reopen <task-key> --stage <N> --reason "<why>" [--evidence <report>]`, which records the reason and the report that found the defect, and re-run the stage rather than repairing the tree outside okstra — a stage reverted outside the ledger leaves the recorded `head_commit` and the carry sidecar naming a tree that no longer exists, and the next stage branches from it.
|
|
98
99
|
- The verifier round, Phase 5.5 convergence, and this Phase 6 report run **once per run** over this stage's diff — NOT per step.
|
|
99
100
|
- Record this stage's new contents in the final report's `Stage sidecar evidence` deliverable section: the sidecar by path with its one-line summary, and the new consumers row quoted by itself.
|
package/runtime/python/okstra_ctl/phases/implementation/instructions/_implementation-executor.md
CHANGED
|
@@ -9,6 +9,7 @@ until Phase 5 ends, then drop from active context for Phase 6/7.
|
|
|
9
9
|
|
|
10
10
|
## Executor role binding (carried over from the thin core)
|
|
11
11
|
|
|
12
|
+
- **Moving dependency bases:** never run `git merge <predecessor>` or merge an old origin branch into this stage to update its base. Runtime preparation rebases downstream stages in dependency order through `okstra_ctl.stage_rebase`; a predecessor moving during execution requires stopping and re-entering preparation after the current stage claim is released. Do not rewrite another active stage or repair registry/ledger SHAs by hand. Enforcement of preparation ordering and conflict recovery is covered by `tests/worktree/test_stage_rebase.py`.
|
|
12
13
|
- The `Executor` (bound in `implementation.md` thin core) is the **only worker permitted to mutate project files**. All other workers run read-only. A `runner=native-session` executor uses the selected host adapter's native edit and command primitives. A `runner=cli-wrapper` executor mutates files inside its provider CLI's auto-edit mode. The safety rules in this sidecar apply identically to both runners.
|
|
13
14
|
- When the thin core's Task worktree block resolves status to `created` or `reused`, the Executor MUST run every Edit / Write / build / test / commit command with the worktree path as cwd. Treat it as `project_root` for the duration of this run. Do NOT mutate the caller's original checkout. Do NOT `cd` out of the worktree to reach files. If a file outside the worktree is genuinely needed, treat it as a planning gap: record it in `Out-of-plan edits` and continue.
|
|
14
15
|
- **How to set the working directory**: every command and native edit MUST target `{{EXECUTOR_WORKTREE_PATH}}`, never the lead session's original project directory. The selected runtime adapter owns the exact native command syntax. Provider CLI wrappers inject the worktree at the CLI layer. For tools that accept an explicit working-directory flag (`git -C <path>`, `cargo --manifest-path`, `pytest --rootdir`), prefer that form.
|
|
@@ -172,6 +172,10 @@ Stage selection is `auto` or a number. If `--stage` comes from another task-type
|
|
|
172
172
|
|
|
173
173
|
The stage worktree base is decided by dependency shape. An independent stage uses the task-key worktree HEAD fixed at first implementation entry as its anchor, and a single-dependency stage branches from the predecessor stage's done `head_commit`. A multi-dependency stage branches from a commit holding exactly its predecessors: the predecessor commit that already contains the others, or else an integration branch merging them (`<work-category-namespace>/<task-id-segment>-g<n1>-<n2>`). okstra creates that branch itself, so a predecessor that has not been merged into the task branch no longer blocks the stage; release-handoff later reuses the same branch as that stage's PR base.
|
|
174
174
|
|
|
175
|
+
When a predecessor advances, implementation entry synchronizes existing completed stage branches in dependency order through `okstra_ctl.stage_rebase.synchronize_stage_stack`. Each downstream stage is rebased with `git rebase --onto <new-base> <recorded-old-base>`; merging a moving predecessor into that stage is not an update strategy. Re-entering an unfinished stage also rebases its existing worktree when the computed base changes. Old heads are preserved under `refs/okstra/rebase-backups/`, registry bases and completion records are updated, and previous verification no longer qualifies. Conflicts abort the current rebase and stop the chain; active runs and dirty worktrees are not rewritten. Tests: `tests/worktree/test_stage_rebase.py`.
|
|
176
|
+
|
|
177
|
+
Successful implementation report finalization automatically removes that stage's clean worktree after its `done` record is persisted. The report's last commit, the completion ledger, and the current branch tip must agree for cleanup (`stage_integrate.reclaim_completed_stage`). Failed, active, dirty, nested, or moved stages are preserved with a reason in the finalization step result. Cleanup retains the branch and registry base; implementation re-entry and final-verification restore the worktree from that branch. The task worktree and other stages are outside this scoped cleanup. Tests: `tests/worktree/test_stage_rebase.py` and `tests/report/test_report_finalize.py`.
|
|
178
|
+
|
|
175
179
|
## 6. Deliverables
|
|
176
180
|
|
|
177
181
|
```mermaid
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"allowed": " - entering this phase only when the cited final-verification report's verdict is release-ready — `Verdict Token` exactly `accepted`, or exactly `conditional-accept` with every Conditional Acceptance Condition row declaring `blocksReleaseHandoff: false` (`okstra_ctl.release_gate.release_handoff_allowed`)\n - asking the user (via `AskUserQuestion` / interactive prompt) which delivery action to take: `local checkout`, `push + PR`, or `skip` (end the run)\n - asking the user to pick a PR base branch from `staging` | `preprod` | `prod` | `main` | `dev` | a user-supplied branch name\n - drafting PR title and PR body **inline as the Okstra lead** (no drafter worker and no delegated dispatch); the lead reviews its own draft with the user before any mutating git / gh command runs\n - read-only git inspection: `git status`, `git diff`, `git log`, `git rev-parse`\n - pushing the current feature branch to its origin remote via `git push -u origin <current-branch>` (the feature branch only — NEVER the base branch)\n - creating a pull request via `gh pr create --base <chosen-base> --head <current-branch>`; if a PR with the same head already exists, surface its URL and skip creation\n - the lead writes the final report directly (no `Report writer worker` dispatch); the report still conforms to the standard final-report template",
|
|
3
3
|
"forbidden": [
|
|
4
4
|
"entering this phase when the cited final-verification `Verdict Token` is `conditional-accept` or `blocked`, or when no final-verification report is cited",
|
|
5
|
-
"local commit commands of any kind (`git add`, `git commit`, `git restore --staged`, `git stash`), and any direct `git merge` / `git rebase` / `git rebase --onto` / `git cherry-pick` / `git commit --amend` run by the lead.
|
|
5
|
+
"local commit commands of any kind (`git add`, `git commit`, `git restore --staged`, `git stash`), and any direct `git merge` / `git rebase` / `git rebase --onto` / `git cherry-pick` / `git commit --amend` run by the lead. Runtime exceptions are dependency-ordered rebases by `okstra_ctl.stage_rebase` (which invalidate verification and stop PR delivery), and merge commits on a dedicated multi-predecessor `merge-base` branch. Never merge a moving predecessor or origin branch into a downstream stage to update its base.",
|
|
6
6
|
"any git push variant that rewrites remote history, regardless of intent or whether the user said \"force it\": `git push --force`, `git push --force-with-lease`, `git push -f`, `git push +<refspec>`, or any other history-rewriting invocation",
|
|
7
7
|
"pushing directly to a release base branch — i.e. `git push origin <branch>` where `<branch>` is `main`, `master`, `prod`, `preprod`, `staging`, `dev`, or the branch the user chose as the release base in this run. The only permitted push targets are the branches `okstra handoff pr-plan` listed for this run (each stage `head_branch`, and any `merge-base` branch).",
|
|
8
8
|
"bypassing repo safeguards: `--no-verify` / `-n` on `git push`, bypassing GPG signing, disabling safeguards via equivalent flags, or any hook bypass.",
|
|
@@ -5,9 +5,14 @@ import subprocess
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from typing import Any, Dict, List, Optional
|
|
7
7
|
|
|
8
|
+
from okstra_project.dirs import okstra_home
|
|
9
|
+
|
|
8
10
|
from okstra_ctl import consumers, stage_targets, worktree_registry
|
|
9
11
|
from okstra_ctl.handoff_verification import verification_row_is_current
|
|
10
12
|
from okstra_ctl.handoff_error import HandoffError
|
|
13
|
+
from okstra_ctl.locks import worktree_provision_mutex
|
|
14
|
+
from okstra_ctl.stage_rebase import synchronize_stage_stack
|
|
15
|
+
from okstra_ctl.stage_targets import StageWorktreeIdentity
|
|
11
16
|
from okstra_ctl.stage_integrate import (
|
|
12
17
|
IntegrateError, MergeBranchConflict, ensure_stage_merge_branch,
|
|
13
18
|
)
|
|
@@ -306,15 +311,31 @@ def _reconcile_with_origin(
|
|
|
306
311
|
def pr_plan(*, project_root, plan_run_root, stage_map, stages, base_branch,
|
|
307
312
|
work_category, project_id, task_group, task_id) -> Dict[str, Any]:
|
|
308
313
|
"""선택한 stage 각각의 PR head/base 를 정하고, 필요한 선행 머지 브랜치를
|
|
309
|
-
만든다.
|
|
310
|
-
|
|
311
|
-
|
|
314
|
+
만든다. 이동한 의존 기준은 먼저 재배치하고 재검증 전에는 전달하지 않는다."""
|
|
315
|
+
try:
|
|
316
|
+
with worktree_provision_mutex(okstra_home(), project_id, task_group, task_id):
|
|
317
|
+
rebased = synchronize_stage_stack(
|
|
318
|
+
project_root=Path(project_root), plan_run_root=Path(plan_run_root),
|
|
319
|
+
stage_map=stage_map,
|
|
320
|
+
identity=StageWorktreeIdentity(project_id, task_group, task_id, work_category),
|
|
321
|
+
)
|
|
322
|
+
except RuntimeError as exc:
|
|
323
|
+
raise HandoffError(str(exc)) from exc
|
|
324
|
+
if rebased:
|
|
325
|
+
raise HandoffError(
|
|
326
|
+
f"stages {rebased} were rebased onto updated dependencies; "
|
|
327
|
+
"re-run final-verification before release-handoff"
|
|
328
|
+
)
|
|
312
329
|
rows = consumers.read_consumers(Path(plan_run_root))
|
|
313
330
|
_require_eligible(stage_map, rows, stages)
|
|
314
331
|
_run_git(["fetch", "origin", base_branch], project_root)
|
|
315
332
|
done = consumers.latest_done_by_stage(rows)
|
|
316
333
|
pr_covered = consumers.pr_covered_stages(rows)
|
|
317
334
|
by_n = {s["stage_number"]: s for s in stage_map}
|
|
335
|
+
stages = stage_targets.order_stage_closure(
|
|
336
|
+
[(n, s.get("depends_on") or []) for n, s in by_n.items()],
|
|
337
|
+
set(stages), set(by_n) - set(stages),
|
|
338
|
+
)
|
|
318
339
|
anchor_base = worktree_registry.get_implementation_base(
|
|
319
340
|
project_id, task_group, task_id) or ""
|
|
320
341
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
- Record the handoff shape in `releaseHandoff.handoffScope`: the `stages` this run delivered and
|
|
4
4
|
the `releaseBase` the user picked. The report is the only place a reader learns which stages
|
|
5
5
|
shipped and in what order their PRs must be merged — the branch names do not say.
|
|
6
|
-
- Purpose: take a release-ready single-stage final-verification verdict for each already-committed implementation stage and turn it into a delivered push and/or pull request, with explicit user selection at every
|
|
6
|
+
- Purpose: take a release-ready single-stage final-verification verdict for each already-committed implementation stage and turn it into a delivered push and/or pull request, with explicit user selection at every delivery step. **One stage is one PR.** The PR head is that stage's stack branch (`<prefix>-<task-id>-s<N>`); nothing is squashed or collected into a bundle branch. Runtime dependency rebases happen before delivery and invalidate the prior verification.
|
|
7
7
|
- **Execution model: single-lead, no worker dispatch.** This phase is a thin orchestrator over `git` / `gh`; it does NOT dispatch teammates, does NOT dispatch analysis or drafter sub-agents, and does NOT run convergence. The host-native Okstra lead performs every step inline (drafting PR text, asking the user, running git / gh, writing the final report) — see "Lead-only contract" below.
|
|
8
8
|
- Worker roster: none — `profile.json` declares no roles; the run is executed entirely by the Okstra lead.
|
|
9
9
|
- Lead-only contract (replaces the shared team contract for this phase):
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
- the lead MUST capture `git status --short` and confirm the working tree is clean. Dirty state aborts the run; release-handoff packages the commits produced by `implementation`, it does not stage or commit changes.
|
|
21
21
|
- the lead MUST capture `git rev-parse --abbrev-ref HEAD` and record it as the run's current branch. It is evidence, not a PR head: every PR head comes from `okstra handoff pr-plan`. If the current branch is `main`, `master`, `prod`, `preprod`, `staging`, or `dev`, record it and do not push it — release-handoff never pushes a base branch.
|
|
22
22
|
- before pushing, run `okstra handoff pr-plan` again if anything mutated since entry, and confirm each selected stage is still present in its output. `pr-plan` re-checks eligibility and each stage's recorded verified commit. Record the pr-plan output in Executed Commands. These pre-push comparisons are lead instructions; the automated entry checks are enforced by `okstra_ctl.handoff_verification` and the handoff tests.
|
|
23
|
-
- **
|
|
23
|
+
- **Dependency updates use rebase.** When a predecessor advances, the runtime replays each dependent stage's own commits onto the new base in dependency order with `git rebase --onto`; never merge a moving predecessor into a downstream stage. `okstra_ctl.stage_rebase.synchronize_stage_stack` enforces this at implementation entry, verification target acquisition, and PR planning. It preserves the old head under `refs/okstra/rebase-backups/`, updates the registry base and completion ledger, and invalidates prior verification. Conflicts abort the current rebase and stop the remaining stages. The lead does not rebase or merge by hand. A rebase during `pr-plan` stops delivery and requires fresh final-verification. Remote divergence stops delivery for explicit user instructions; never repair it by merging origin back into the rebased stage. Merge PRs in dependency order, using merge commits or rebase-merge rather than squash. State that order and rule in the final report and closeout, not in individual PR bodies.
|
|
24
24
|
- User interaction protocol (Okstra lead — performed in order, using the selected runtime adapter's interactive prompt):
|
|
25
25
|
1. **Action selection** — present three choices and capture exactly one:
|
|
26
26
|
- `local checkout` — bring one stage's branch into the MAIN worktree for local testing (no push, no PR). See step 1c.
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
`okstra handoff pr-plan --plan-run-root <...> --approved-plan <...> --project-root <project root> --project-id <id> --task-group <g> --task-id <t> --work-category <c> --stages <HANDOFF_STAGES> --base <chosen-base>`.
|
|
40
40
|
It returns one row per stage with `head_branch`, `head_commit`, `base_kind` (`release-base` | `stage` | `merge-base`), `base_branch` and `base_commit`. Exit 2 means the predecessors of a multi-dependency stage conflict with each other: show the `conflicts` paths and stop (route: resolve manually, then retry). Exit 1 means an eligibility or dependency violation — most often a stage whose predecessor is neither in this run nor already PR'd nor merged into the release base: show the error verbatim and re-ask the action selection. On success these rows are authoritative for every subsequent step; the lead does not recompute a base by hand.
|
|
41
41
|
A `merge-base` row means `pr-plan` created a branch merging that stage's predecessors. That branch is a PR base only — it is never a PR head — and it MUST be pushed before the PR that targets it.
|
|
42
|
-
The output also carries `push_plan` and `warnings`. `push_plan` is every branch the PRs sit on, already in push order (each `merge-base` first, then the stage heads in
|
|
43
|
-
2b. **Pre-merge conflict probe** (only when the user picked `push + PR`) — before pushing, for each planned stage in
|
|
42
|
+
The output also carries `push_plan` and `warnings`. `push_plan` is every branch the PRs sit on, already in push order (each `merge-base` first, then the stage heads in dependency order), with the `commit` it must reach and an `action` of `create` (origin does not have it), `fast-forward` (origin is behind) or `up-to-date` (origin already has that commit — do not push it). GitHub resolves a PR's head and base by name on origin, so this list, not the local branch set, is what makes the stack real. A `warnings` row means `pr-plan` fast-forwarded a local branch to origin because origin was ahead: the commits origin adds are outside what this run verified. Quote every `warnings` row verbatim in Executed Commands and say so in the branch/PR explanation — the user is deciding whether to release commits this run did not verify. `pr-plan` refuses outright when a branch moved outside okstra or diverged from origin, since only a force push could deliver it.
|
|
43
|
+
2b. **Pre-merge conflict probe** (only when the user picked `push + PR`) — before pushing, for each planned stage in dependency order:
|
|
44
44
|
- run `git fetch origin <chosen-base>` once (read-only on the local working tree).
|
|
45
45
|
- run `git merge-tree --write-tree <head_branch> <base_branch>` for that stage, using the `base_branch` from the pr-plan row (`origin/<chosen-base>` for a `release-base` row). Exit 0 means clean. Exit 1 with a result-tree object ID on the first stdout line means a merge conflict. A missing result tree, or any other exit code, is an execution error: stop and report it, without offering permission to proceed as if it were a content conflict. An invalid ref can also return exit 1, so the exit code alone is insufficient.
|
|
46
46
|
- If no stage conflicts, proceed silently to Q3 (do NOT add a confirmation prompt — keep the happy path frictionless).
|
|
@@ -63,8 +63,8 @@
|
|
|
63
63
|
- Allowed actions during the run (Okstra lead only):
|
|
64
64
|
- read-only inspection: `git status`, `git status --short`, `git diff`, `git log`, `git rev-parse`, `git ls-remote --heads origin <name>`, `gh pr list --head <branch>`, `gh pr view <url>`, and the drafting bundle `okstra pr gen --base <base_commit> --head <head_commit> --template-file <PR_TEMPLATE_PATH>`, and the body check `okstra pr check --body-file <draft file>`.
|
|
65
65
|
- merge-conflict probe (only when the user picked `push + PR`): `git fetch origin <chosen-base>` and the exact `git merge-tree` commands from step 2b. Both preserve the working tree and branch tips.
|
|
66
|
-
- branch push (only when the user picked `push + PR`): `git push -u origin <branch>` for each `push_plan` row whose `action` is `create` or `fast-forward`, in the order `push_plan` lists them (each `merge-base` branch before the PR that targets it, then the stage heads in
|
|
67
|
-
- PR creation, one per stage (only when the user picked `push + PR` AND no PR with the same head already exists on origin): `gh pr create --base <row base_branch> --head <row head_branch> --title "<title>" --body "<body>"`. The title and body are the user-confirmed PR draft for that stage. Open them in
|
|
66
|
+
- branch push (only when the user picked `push + PR`): `git push -u origin <branch>` for each `push_plan` row whose `action` is `create` or `fast-forward`, in the order `push_plan` lists them (each `merge-base` branch before the PR that targets it, then the stage heads in dependency order). Skip an `up-to-date` row — origin already holds that commit. The pushed ref MUST come from a `push_plan` row — never the release base branch, and never a ref that list does not name.
|
|
67
|
+
- PR creation, one per stage (only when the user picked `push + PR` AND no PR with the same head already exists on origin): `gh pr create --base <row base_branch> --head <row head_branch> --title "<title>" --body "<body>"`. The title and body are the user-confirmed PR draft for that stage. Open them in dependency order so each base branch already exists on origin.
|
|
68
68
|
- PR reuse: if `gh pr list --head <branch> --state open --json url --jq '.[0].url'` returns a URL, treat that PR as already existing — record the URL in the final report and SKIP `gh pr create` for that stage.
|
|
69
69
|
- after each `gh pr create` succeeds (or an existing PR is reused), the lead MUST run `okstra handoff record-pr --plan-run-root <...> --stage <N> --branch <head branch> --base <base branch> --url <pr url>` and quote the command + exit code in the final report. One record-pr call per stage — the consumers ledger is what stops the same stage being PR'd twice.
|
|
70
70
|
- stage helpers: `okstra handoff eligible`, `okstra handoff pr-plan`, `okstra handoff record-pr`, `okstra handoff local-checkout`. `pr-plan` is the ONLY path that may create commits in this phase (merge commits on a `merge-base` branch, and only for a multi-dependency stage).
|
|
@@ -73,8 +73,8 @@
|
|
|
73
73
|
- Required deliverable shape (final report, in addition to the standard sections):
|
|
74
74
|
- **Source Verification Reports**: one row per selected stage — relative path of that stage's originating `final-verification` final-report plus the literal quoted `Verdict Token` row, and — when that token is `conditional-accept` — every Conditional Acceptance Condition row quoted with its `blocksReleaseHandoff` value.
|
|
75
75
|
- **Feature Branch & Working-Tree State**: the branch from `git rev-parse --abbrev-ref HEAD` at run start and the output of `git status --short` at run start.
|
|
76
|
-
- **Stage PR Plan**: the pr-plan rows — stage, head branch, head commit, base kind, base branch, base commit — plus, for every `merge-base` row, which predecessor stages it merges and the merge commits it created. This is the table a reader uses to merge the stack in the right order, so it carries the merge rule with it: merge in
|
|
77
|
-
- **User narrative `nextAction`**: when this run opened or reused any PR, it names the merge order (
|
|
76
|
+
- **Stage PR Plan**: the pr-plan rows — stage, head branch, head commit, base kind, base branch, base commit — plus, for every `merge-base` row, which predecessor stages it merges and the merge commits it created. This is the table a reader uses to merge the stack in the right order, so it carries the merge rule with it: merge in dependency order, with a merge commit or rebase-merge, never a squash.
|
|
77
|
+
- **User narrative `nextAction`**: when this run opened or reused any PR, it names the merge order (dependency order) and the merge rule (merge commit or rebase-merge, never squash), and says why — squashing one PR in the stack invalidates the next PR's base. This is where the operator reads the rule; the PR bodies do not carry it.
|
|
78
78
|
- **User Selections**: a block recording each prompt and the user's verbatim answer.
|
|
79
79
|
- Q1 action: `local checkout` | `push + PR` | `skip`.
|
|
80
80
|
- Q1c checkout stage (only when Q1 is `local checkout`): the `--stage <N>` value this answer produced. Record it in the `User Selections` row `H1c` (`userSelections.h1c`); omit the row entirely when Q1 was not `local checkout`.
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
7. **Merge-conflict probe audit** — for any `push + PR` run, confirm the report's `Merge Conflict Probe` section is present and either records `Clean` or records `Conflicts detected` with the user's verbatim choice. A missing or unparseable probe entry on a `push + PR` run is a contract violation.
|
|
109
109
|
- Non-goals:
|
|
110
110
|
- re-litigating a final-verification verdict — release-handoff trusts the cited release-ready verdicts and does not reopen acceptance checks. Restating a conditional-accept's conditions in the PR body is reporting, not verification: the lead does not check whether a condition has been satisfied.
|
|
111
|
-
-
|
|
111
|
+
- manual commit creation, amendment, squash, or rebase by the lead. Runtime dependency rebases are owned by `okstra_ctl.stage_rebase`; `pr-plan` stops delivery after any such change until final-verification is repeated. Combining several independent predecessors on a dedicated `merge-base` branch remains separate from updating a downstream stage; never merge that base into the downstream stage.
|
|
112
112
|
- bundling several stages into one PR, or building a branch that collects every stage. One stage is one PR.
|
|
113
113
|
- opening releases or deployments beyond the per-stage PRs the user chose to create.
|
|
114
114
|
- merging the PRs. Merging is a separate, manual step performed by the user (or by repo automation) after release-handoff ends; the lead MUST NOT call `gh pr merge`.
|
|
@@ -27,9 +27,11 @@ Report shapes remain in `schemas/final-report-v2.0.schema.json` and `schemas/fin
|
|
|
27
27
|
|
|
28
28
|
## 1. Purpose
|
|
29
29
|
|
|
30
|
-
`release-handoff` is the terminal phase that pushes already-committed implementation stages with a release-ready verdict, or hands them off as pull requests. **One stage is one PR.** The PR head is that stage's stack branch; the base comes from its `depends-on` — the release base for a stage with no live dependency, the predecessor's branch for a stage with one (a stacked PR), and a branch merging the predecessors for a stage with several.
|
|
30
|
+
`release-handoff` is the terminal phase that pushes already-committed implementation stages with a release-ready verdict, or hands them off as pull requests. **One stage is one PR.** The PR head is that stage's stack branch; the base comes from its `depends-on` — the release base for a stage with no live dependency, the predecessor's branch for a stage with one (a stacked PR), and a branch merging the predecessors for a stage with several.
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
When a predecessor advances, `okstra_ctl.stage_rebase.synchronize_stage_stack` rebases downstream stages in dependency order rather than merging the predecessor into them. It records the new bases and completion commits, preserving prior heads under `refs/okstra/rebase-backups/`. Implementation entry and verification target acquisition use the same operation. PR planning stops after a stack change and requires fresh final-verification; the new completion rows invalidate earlier verification through `consumers.verified_accepted_stages`. Dirty or active stages, rewritten unproven predecessors, and rebase conflicts stop synchronization. The tests in `tests/worktree/test_stage_rebase.py` cover ordering, preservation, and verification invalidation. Origin divergence remains a delivery error, never a reason to merge old origin history into the rebased branch.
|
|
33
|
+
|
|
34
|
+
Because the PRs are a stack, they must be merged in dependency order with a merge commit or a rebase-merge. A squash replaces the commits the next PR's base points at, so the stack breaks — every PR body states this, and the lead never merges.
|
|
33
35
|
|
|
34
36
|
This phase has no worker dispatch. It does not use a provider or report-writer roster; the host-native Okstra lead performs git/gh inspection, user questions, the PR draft, and the final report inline.
|
|
35
37
|
|
|
@@ -697,8 +697,6 @@ def _active_runtime_resources(ctx: dict) -> dict:
|
|
|
697
697
|
or str(runtime_home / "templates" / "implementation-worker-preamble.md"),
|
|
698
698
|
"report-writer": ctx.get("REPORT_WRITER_PREAMBLE_PATH")
|
|
699
699
|
or str(runtime_home / "templates" / "report-writer-prompt-preamble.md"),
|
|
700
|
-
"translator": ctx.get("TRANSLATOR_PREAMBLE_PATH")
|
|
701
|
-
or str(runtime_home / "templates" / "translator-prompt-preamble.md"),
|
|
702
700
|
},
|
|
703
701
|
"workerErrorContractPath": ctx.get("WORKER_ERROR_CONTRACT_PATH")
|
|
704
702
|
or str(runtime_home / "templates" / "worker-error-contract.md"),
|
|
@@ -2638,9 +2636,6 @@ def inject_lead_prompt_computed_tokens(ctx: dict) -> None:
|
|
|
2638
2636
|
report_writer_preamble = ctx.get("REPORT_WRITER_PREAMBLE_PATH") or str(
|
|
2639
2637
|
runtime_templates / "report-writer-prompt-preamble.md"
|
|
2640
2638
|
)
|
|
2641
|
-
translator_preamble = ctx.get("TRANSLATOR_PREAMBLE_PATH") or str(
|
|
2642
|
-
runtime_templates / "translator-prompt-preamble.md"
|
|
2643
|
-
)
|
|
2644
2639
|
worker_error_contract = ctx.get("WORKER_ERROR_CONTRACT_PATH") or str(
|
|
2645
2640
|
runtime_templates / "worker-error-contract.md"
|
|
2646
2641
|
)
|
|
@@ -2663,7 +2658,6 @@ def inject_lead_prompt_computed_tokens(ctx: dict) -> None:
|
|
|
2663
2658
|
f"- Analysis worker preamble: `{analysis_preamble}`\n"
|
|
2664
2659
|
f"- Implementation worker preamble: `{implementation_preamble}`\n"
|
|
2665
2660
|
f"- Report writer preamble: `{report_writer_preamble}`\n"
|
|
2666
|
-
f"- Translator preamble: `{translator_preamble}`\n"
|
|
2667
2661
|
|
|
2668
2662
|
f"- Shared worker error contract: `{worker_error_contract}`"
|
|
2669
2663
|
)
|
|
@@ -2886,10 +2880,6 @@ def apply_lead_prompt_defaults(ctx: dict) -> None:
|
|
|
2886
2880
|
"REPORT_WRITER_PREAMBLE_PATH",
|
|
2887
2881
|
str(runtime_home / "templates" / "report-writer-prompt-preamble.md"),
|
|
2888
2882
|
)
|
|
2889
|
-
ctx.setdefault(
|
|
2890
|
-
"TRANSLATOR_PREAMBLE_PATH",
|
|
2891
|
-
str(runtime_home / "templates" / "translator-prompt-preamble.md"),
|
|
2892
|
-
)
|
|
2893
2883
|
ctx.setdefault(
|
|
2894
2884
|
"WORKER_ERROR_CONTRACT_PATH",
|
|
2895
2885
|
str(runtime_home / "templates" / "worker-error-contract.md"),
|
|
@@ -588,10 +588,10 @@ def _markdown_task_template(task_type: str) -> str:
|
|
|
588
588
|
return ""
|
|
589
589
|
|
|
590
590
|
|
|
591
|
-
# The Markdown is the AI handoff sibling of the data.json
|
|
592
|
-
#
|
|
593
|
-
#
|
|
594
|
-
#
|
|
591
|
+
# The Markdown is the AI handoff sibling of the data.json. Its prose is the
|
|
592
|
+
# record's own, already in the report language; only its fixed labels come from
|
|
593
|
+
# the English dictionary. A malformed tag is still rejected here, before the
|
|
594
|
+
# HTML render meets it at the very end of the run.
|
|
595
595
|
MARKDOWN_LANG = "en"
|
|
596
596
|
|
|
597
597
|
|