okstra 0.75.0 → 0.76.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/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/prompts/profiles/_coding-conventions-preflight.md +21 -0
- package/runtime/prompts/profiles/_implementation-executor.md +3 -6
- package/runtime/prompts/wizard/prompts.ko.json +12 -4
- package/runtime/python/okstra_ctl/wizard.py +23 -9
package/package.json
CHANGED
package/runtime/BUILD.json
CHANGED
|
@@ -135,7 +135,7 @@ This wrapper does NOT invoke MCP tools directly. MCP availability inside the Cod
|
|
|
135
135
|
- Treat the prompt-history path as the canonical worker prompt history artifact for the current run, resolved to absolute against `Project Root` if given as relative.
|
|
136
136
|
- The assigned model execution value is canonical for CLI execution. Do not substitute a different Codex model unless the task bundle explicitly changes it.
|
|
137
137
|
- Pass the prompt received from Lead directly to codex after persisting the exact prompt to the assigned path.
|
|
138
|
-
- **Executor preflight forwarding check (implementation runs only).** When the lead prompt assigns this dispatch the `Executor` role for an `implementation` run, the persisted prompt body MUST contain the literal heading `Coding-conventions preflight` (
|
|
138
|
+
- **Executor preflight forwarding check (implementation runs only).** When the lead prompt assigns this dispatch the `Executor` role for an `implementation` run, the persisted prompt body MUST contain the literal heading `Coding-conventions preflight` (the lead appends the body of `prompts/profiles/_coding-conventions-preflight.md` into the dispatch prompt — see `prompts/profiles/_implementation-executor.md` → "Pre-implementation context exploration") — the Codex CLI does not share the lead's context, so an unforwarded gate never reaches the process that writes the code. If the heading is absent, return `CODEX_PREFLIGHT_MISSING: executor dispatch prompt lacks the coding-conventions preflight block` instead of invoking the CLI; the lead is responsible for re-dispatching with the block included. This check does NOT apply to verifier or analysis dispatches.
|
|
139
139
|
- Include context (code, diff, file paths) if provided.
|
|
140
140
|
- For long prompts, dispatch through the wrapper with literal absolute paths (plus the worktree path for implementation phase):
|
|
141
141
|
```bash
|
|
@@ -135,7 +135,7 @@ This wrapper does NOT invoke MCP tools directly. MCP availability inside the Gem
|
|
|
135
135
|
- Treat the prompt-history path as the canonical worker prompt history artifact for the current run, resolved to absolute against `Project Root` if given as relative.
|
|
136
136
|
- The assigned model execution value is canonical for CLI execution. Do not substitute a different Gemini model unless the task bundle explicitly changes it.
|
|
137
137
|
- Pass the prompt received from Lead directly to gemini after persisting the exact prompt to the assigned path.
|
|
138
|
-
- **Executor preflight forwarding check (implementation runs only).** When the lead prompt assigns this dispatch the `Executor` role for an `implementation` run, the persisted prompt body MUST contain the literal heading `Coding-conventions preflight` (
|
|
138
|
+
- **Executor preflight forwarding check (implementation runs only).** When the lead prompt assigns this dispatch the `Executor` role for an `implementation` run, the persisted prompt body MUST contain the literal heading `Coding-conventions preflight` (the lead appends the body of `prompts/profiles/_coding-conventions-preflight.md` into the dispatch prompt — see `prompts/profiles/_implementation-executor.md` → "Pre-implementation context exploration") — the Gemini CLI does not share the lead's context, so an unforwarded gate never reaches the process that writes the code. If the heading is absent, return `GEMINI_PREFLIGHT_MISSING: executor dispatch prompt lacks the coding-conventions preflight block` instead of invoking the CLI; the lead is responsible for re-dispatching with the block included. This check does NOT apply to verifier or analysis dispatches.
|
|
139
139
|
- Include context (code, diff, file paths) if provided.
|
|
140
140
|
- For long prompts, dispatch through the wrapper with literal absolute paths (plus the worktree path for implementation phase):
|
|
141
141
|
```bash
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Single source for the executor's Coding-conventions preflight gate. Two delivery
|
|
3
|
+
paths converge here (see _implementation-executor.md "Pre-implementation context
|
|
4
|
+
exploration"):
|
|
5
|
+
- Claude executor reads this file directly before its first Edit / Write.
|
|
6
|
+
- codex / gemini executor cannot read this path (it sits outside the CLI
|
|
7
|
+
sandbox and the CLI only sees its stdin prompt), so the lead appends this
|
|
8
|
+
file's body into the persisted executor prompt at dispatch time.
|
|
9
|
+
The `Coding-conventions preflight` heading below is the literal string the CLI
|
|
10
|
+
wrapper's "Executor preflight forwarding check" greps for in the persisted
|
|
11
|
+
prompt (agents/workers/_cli-wrapper-template.md → Prompt Composition).
|
|
12
|
+
-->
|
|
13
|
+
|
|
14
|
+
# Coding-conventions preflight (BLOCKING — runs before the first `Edit` / `Write`, and binds the TDD loop)
|
|
15
|
+
|
|
16
|
+
Load the applicable coding conventions for every language the diff will touch, then state in ONE line which conventions apply (e.g. `Applying TS + hexagonal overlay; domain at src/domains/*/domain/`). Lint/test green is necessary but NOT sufficient — self-mocked tests, interaction-only assertions, and untruthful names all pass a green pipeline; this gate is what keeps them out of the diff.
|
|
17
|
+
|
|
18
|
+
- **Language-specific rules load per situation — never inline them here.** Detect each touched file's language (extension / project manifest) and load the matching reference by reading okstra's installed coding-conventions files directly at `~/.claude/skills/okstra-coding-preflight/` (placed there by `okstra install`): read `languages/<lang>.md` (mock/spy API, idioms, test framework) + `clean-code.md` + any `architecture/*` overlay via the Read tool by absolute path. The skill is `user-invocable: false`, so do NOT rely on Skill-tool auto-invocation — read the files directly. For a ports-and-adapters / NestJS-hex layout (`domain/` + `ports/` + `adapters/`, `*.port.*`), load the hexagonal overlay too. This per-language split is the skill's job — the executor does not carry a multi-language block in context.
|
|
19
|
+
- **Project review rule packs:** also look for project-local review skills in `<PROJECT_ROOT>/skills/*review*`, `<PROJECT_ROOT>/.claude/skills/*review*`, and up to two parent directories' `skills/*review*/SKILL.md`. Read the relevant `SKILL.md` plus referenced `references/*.md` files and apply their rules during implementation. This is a prevention pass, not a PR-comment generation workflow: do not dispatch reviewer subagents from the executor. For Fonts Ninja-style PR review packs, the executor must avoid newly introduced duplicate helper stacks, tautological tests that merely re-call the delegated helper, self-mocking, domain rules in adapters/ports, domain objects outside `domain/`, dead APIs, weak public names, and functions that fail the plain-English read.
|
|
20
|
+
- **Language-agnostic principles that ALWAYS bind (the TDD loop MUST satisfy them):** (1) no self-mocking of the SUT — stub/spy only injected collaborators, never the subject's own methods; (2) behavioral assertions on outcomes (return value, state, persisted rows, events, boundary calls) — never `toHaveBeenCalled*` on an internal helper as the only/primary assertion; (3) truthful names — a `get*` / `find*` that writes/inserts, or a name encoding the caller's use-case (`*ForInit`) or hiding a domain rule (`findValid*`), is a defect; (4) single-purpose functions ≤50 effective lines, plain-English readability.
|
|
21
|
+
- **Graceful degradation (codex / gemini executor runtimes, or any runtime where the `~/.claude/skills/okstra-coding-preflight/` files are absent or unreadable):** do NOT skip the gate — apply the agnostic principles above plus the project's own `CLAUDE.md` / `CONTRIBUTING` / formatter+lint config, and record `coding-conventions: skill-unavailable → applied <project rules + agnostic principles>` in the final report. Never claim a skill read that did not happen.
|
|
@@ -19,12 +19,9 @@ until Phase 5 ends, then drop from active context for Phase 6/7.
|
|
|
19
19
|
|
|
20
20
|
## Pre-implementation context exploration (executor before first edit)
|
|
21
21
|
|
|
22
|
-
- **Coding-conventions preflight (BLOCKING — runs before the first `Edit` / `Write`, and binds the TDD loop below)
|
|
23
|
-
- **
|
|
24
|
-
- **
|
|
25
|
-
- **Language-agnostic principles that ALWAYS bind (the TDD loop below MUST satisfy them):** (1) no self-mocking of the SUT — stub/spy only injected collaborators, never the subject's own methods; (2) behavioral assertions on outcomes (return value, state, persisted rows, events, boundary calls) — never `toHaveBeenCalled*` on an internal helper as the only/primary assertion; (3) truthful names — a `get*` / `find*` that writes/inserts, or a name encoding the caller's use-case (`*ForInit`) or hiding a domain rule (`findValid*`), is a defect; (4) single-purpose functions ≤50 effective lines, plain-English readability.
|
|
26
|
-
- **Graceful degradation (codex / gemini executor runtimes, or any runtime where the `~/.claude/skills/okstra-coding-preflight/` files are absent or unreadable):** do NOT skip the gate — apply the agnostic principles above plus the project's own `CLAUDE.md` / `CONTRIBUTING` / formatter+lint config, and record `coding-conventions: skill-unavailable → applied <project rules + agnostic principles>` in the final report. Never claim a skill read that did not happen.
|
|
27
|
-
- **CLI executor transcription (BLOCKING when the executor provider is `codex` or `gemini`):** the executor CLI process does NOT share the lead's context — a gate that stays in lead memory never reaches it. The lead MUST copy this entire "Coding-conventions preflight" bullet tree (file-read instructions, project review rule packs, agnostic principles, graceful degradation) verbatim into the dispatched executor prompt body. Enforcement: the CLI wrapper agents refuse an implementation-Executor dispatch whose persisted prompt lacks the literal heading `Coding-conventions preflight`, returning `<SENTINEL_PREFIX>_PREFLIGHT_MISSING` (see `agents/workers/_cli-wrapper-template.md` → Prompt Composition).
|
|
22
|
+
- **Coding-conventions preflight (BLOCKING — runs before the first `Edit` / `Write`, and binds the TDD loop below).** The gate body is a single source at `prompts/profiles/_coding-conventions-preflight.md` (sibling of this sidecar): skill-file pointers (`~/.claude/skills/okstra-coding-preflight/`), project review rule packs, the always-binding language-agnostic principles, and graceful degradation. Do NOT re-type that content from memory — deliver it by file so it cannot drift or be dropped:
|
|
23
|
+
- **Claude executor:** Read `_coding-conventions-preflight.md` end-to-end before the first `Edit` / `Write`, then state in ONE line which conventions apply (e.g. `Applying TS + hexagonal overlay; domain at src/domains/*/domain/`).
|
|
24
|
+
- **CLI executor (BLOCKING when the executor provider is `codex` or `gemini`):** the executor CLI process does NOT share the lead's context, and it cannot Read this sidecar's directory — that path sits outside the CLI sandbox and the CLI only sees its stdin prompt, so a file reference never reaches it. The lead MUST physically append the **body** of `_coding-conventions-preflight.md` into the persisted executor prompt at dispatch time: Read the file from the same absolute directory you read this sidecar from, then `Write`/`cat` its body into the persisted prompt. Never hand-retype it. Enforcement: the CLI wrapper agents refuse an implementation-Executor dispatch whose persisted prompt lacks the literal heading `Coding-conventions preflight`, returning `<SENTINEL_PREFIX>_PREFLIGHT_MISSING` (see `agents/workers/_cli-wrapper-template.md` → Prompt Composition).
|
|
28
25
|
- **Stage discipline transcription (when a preceding stage is `done`):** the lead MUST transcribe the `Stage discipline` rule (from this run's rendered profile — the INCLUDEd `_stage-discipline.md` body) verbatim into the dispatched CLI executor prompt, so a codex/gemini executor honors the prior-stage behavior-freeze. Declaration-level — no wrapper sentinel.
|
|
29
26
|
- **Non-interactive auto-execution (BLOCKING when the executor provider is `codex` or `gemini`).** A CLI executor runs head-less (`codex exec` / gemini equivalent) — there is no human at the keyboard. Skills loaded during the run (tdd, coding-preflight, and others) contain "get user approval", "state your plan to the user and wait", or "ask before proceeding" gates written for interactive sessions; in this run those gates are **already satisfied** by the upstream `implementation-planning` approval (the plan this stage executes was human-approved). The executor MUST NOT stop to request approval, MUST NOT end its turn after only producing a plan, and MUST carry the stage through end-to-end — RED → GREEN → refactor → per-step commits → `### Stage Carry Evidence`. The ONLY skill step to skip is the interactive user-approval prompt itself; every other skill rule (TDD discipline, conventions, real-IO isolation) still binds. The lead MUST transcribe this bullet verbatim into the dispatched CLI executor prompt (same reason as the preflight transcription rule above — the CLI process does not share lead context). Stopping early for approval in a head-less run is the observed empty-exit failure (exit 0, no diff): treat it as `contract-violated`.
|
|
30
27
|
- **Mandatory TDD loop**: BEFORE the first `Edit` or `Write` call, the executor MUST apply a red-green-refactor loop for every code change in this run. This is required; skipping it is a `contract-violated` outcome. This governs HOW each step is executed (failing test first → minimal implementation → refactor); it does not override the approved plan's WHAT/file scope.
|
|
@@ -196,6 +196,7 @@
|
|
|
196
196
|
},
|
|
197
197
|
"approved_plan_pick": {
|
|
198
198
|
"label": "approved final-report 경로 (기본: {default})",
|
|
199
|
+
"label_final_verification": "검증 기준이 될 승인된 plan(approved final-report) 을 선택하세요 (기본: {default})",
|
|
199
200
|
"echo_template": "approved-plan(pick): {value}",
|
|
200
201
|
"options": {
|
|
201
202
|
"__use_default__": "기본 경로 사용: {default}",
|
|
@@ -216,12 +217,17 @@
|
|
|
216
217
|
"echo_template": "approved-plan: {value}"
|
|
217
218
|
},
|
|
218
219
|
"approve_plan_confirm": {
|
|
219
|
-
"label": "이 플랜으로 진행할까요?\n {path}\n· 예 —
|
|
220
|
+
"label": "이 플랜으로 구현을 진행할까요?\n {path}\n· 예 — 이 plan 대로 구현을 실행합니다. 플랜이 아직 승인 전이면 지금 data.json(정본) + 리포트를 함께 approved 로 처리한 뒤 진행합니다. (markdown 만 손으로 고치면 일관성 검증에서 거부되므로 이 경로로 승인하세요.)\n· 아니오 — 진행하지 않습니다.",
|
|
221
|
+
"label_final_verification": "이 plan 을 검증 기준으로 삼아 진행할까요?\n {path}\n· 예 — 선택한 stage 의 구현 결과를 위 plan 기준으로 검증합니다. (plan 이 아직 승인 전이면 data.json(정본) + 리포트를 함께 approved 로 처리한 뒤 검증합니다.)\n· 아니오 — 진행하지 않습니다.",
|
|
220
222
|
"html_approval_note": "\nHTML 승인 기록 발견: {sidecar}\n 선택 옵션: {option}\n· 예(기록 적용) — 승인과 함께 위 옵션을 frontmatter 에 기록합니다.",
|
|
221
223
|
"html_approval_note_default_option": "(추천 옵션 그대로)",
|
|
222
224
|
"echo_template": "approve-plan: {value}",
|
|
223
225
|
"options": {
|
|
224
|
-
"yes": "예 — 승인하고 진행",
|
|
226
|
+
"yes": "예 — 승인하고 구현 진행",
|
|
227
|
+
"no": "아니오 — 진행하지 않음"
|
|
228
|
+
},
|
|
229
|
+
"options_final_verification": {
|
|
230
|
+
"yes": "예 — 이 plan 기준으로 검증 진행",
|
|
225
231
|
"no": "아니오 — 진행하지 않음"
|
|
226
232
|
},
|
|
227
233
|
"options_html_approval": {
|
|
@@ -230,8 +236,10 @@
|
|
|
230
236
|
"no": "아니오 — 진행하지 않음"
|
|
231
237
|
},
|
|
232
238
|
"echo_variants": {
|
|
233
|
-
"selected": "plan 선택: {path} — 다음 단계에서
|
|
234
|
-
"
|
|
239
|
+
"selected": "plan 선택: {path} — 다음 단계에서 승인·구현 진행 여부를 확인합니다",
|
|
240
|
+
"selected_final_verification": "검증 기준 plan 선택: {path} — 다음 단계에서 검증 진행 여부를 확인합니다",
|
|
241
|
+
"approved": "approved-plan: {path} (승인·구현 진행 확인됨)",
|
|
242
|
+
"approved_final_verification": "검증 기준 plan 확정: {path} (검증 진행 확인됨)",
|
|
235
243
|
"approved_with_option": "approved-plan: {path} (승인 + 옵션 `{option}` 적용됨)"
|
|
236
244
|
},
|
|
237
245
|
"errors": {
|
|
@@ -635,7 +635,12 @@ def _stage_plan_for_confirmation(
|
|
|
635
635
|
state.html_approval_sidecar = str(sidecar_path)
|
|
636
636
|
state.html_approval_option = record.implementation_option
|
|
637
637
|
t = _p(state.workspace_root, "approve_plan_confirm", path=str(p))
|
|
638
|
-
|
|
638
|
+
variants = t["echo_variants"]
|
|
639
|
+
key = ("selected_final_verification"
|
|
640
|
+
if state.task_type == "final-verification"
|
|
641
|
+
and variants.get("selected_final_verification")
|
|
642
|
+
else "selected")
|
|
643
|
+
msg = variants[key].format(path=p)
|
|
639
644
|
return f"{msg} {suffix}".rstrip() if suffix else msg
|
|
640
645
|
|
|
641
646
|
|
|
@@ -770,8 +775,10 @@ def _p(workspace_root: str, step_id: str, **vars: str) -> dict:
|
|
|
770
775
|
if raw is None:
|
|
771
776
|
raise WizardError(f"unknown wizard step_id: {step_id!r}")
|
|
772
777
|
label_template = raw.get("label", "")
|
|
778
|
+
fv_label_template = raw.get("label_final_verification", "")
|
|
773
779
|
try:
|
|
774
780
|
label = label_template.format(**vars)
|
|
781
|
+
label_final_verification = fv_label_template.format(**vars)
|
|
775
782
|
except KeyError as exc:
|
|
776
783
|
missing = exc.args[0] if exc.args else "<unknown>"
|
|
777
784
|
raise WizardError(
|
|
@@ -779,8 +786,10 @@ def _p(workspace_root: str, step_id: str, **vars: str) -> dict:
|
|
|
779
786
|
) from exc
|
|
780
787
|
return {
|
|
781
788
|
"label": label,
|
|
789
|
+
"label_final_verification": label_final_verification,
|
|
782
790
|
"echo_template": raw.get("echo_template", ""),
|
|
783
791
|
"options": raw.get("options", {}),
|
|
792
|
+
"options_final_verification": raw.get("options_final_verification", {}),
|
|
784
793
|
"options_html_approval": raw.get("options_html_approval", {}),
|
|
785
794
|
"html_approval_note": raw.get("html_approval_note", ""),
|
|
786
795
|
"html_approval_note_default_option": raw.get(
|
|
@@ -1626,6 +1635,8 @@ def _build_approved_plan_pick(state: WizardState) -> Prompt:
|
|
|
1626
1635
|
default = reports[0] if reports else None
|
|
1627
1636
|
t = _p(state.workspace_root, "approved_plan_pick",
|
|
1628
1637
|
default=str(default) if default is not None else "")
|
|
1638
|
+
is_fv = state.task_type == "final-verification"
|
|
1639
|
+
label = (t["label_final_verification"] or t["label"]) if is_fv else t["label"]
|
|
1629
1640
|
other_report_label = t["labels"]["other_report"]
|
|
1630
1641
|
options: list[Option] = []
|
|
1631
1642
|
if default is not None:
|
|
@@ -1637,7 +1648,7 @@ def _build_approved_plan_pick(state: WizardState) -> Prompt:
|
|
|
1637
1648
|
options.append(_opt(PICK_OTHER, t["options"][PICK_OTHER]))
|
|
1638
1649
|
return Prompt(
|
|
1639
1650
|
step=S_APPROVED_PLAN_PICK, kind="pick",
|
|
1640
|
-
label=
|
|
1651
|
+
label=label, options=options,
|
|
1641
1652
|
echo_template=t["echo_template"],
|
|
1642
1653
|
)
|
|
1643
1654
|
|
|
@@ -1679,8 +1690,9 @@ def _submit_approved_plan(state: WizardState, value: str) -> Optional[str]:
|
|
|
1679
1690
|
def _build_approve_plan_confirm(state: WizardState) -> Prompt:
|
|
1680
1691
|
t = _p(state.workspace_root, "approve_plan_confirm",
|
|
1681
1692
|
path=state.approve_plan_candidate)
|
|
1682
|
-
|
|
1683
|
-
|
|
1693
|
+
is_fv = state.task_type == "final-verification"
|
|
1694
|
+
label = (t["label_final_verification"] or t["label"]) if is_fv else t["label"]
|
|
1695
|
+
options_map = (t["options_final_verification"] or t["options"]) if is_fv else t["options"]
|
|
1684
1696
|
if state.html_approval_sidecar:
|
|
1685
1697
|
label += t["html_approval_note"].format(
|
|
1686
1698
|
sidecar=state.html_approval_sidecar,
|
|
@@ -1728,7 +1740,12 @@ def _submit_approve_plan_confirm(state: WizardState, value: str) -> Optional[str
|
|
|
1728
1740
|
if not fully_approved:
|
|
1729
1741
|
raise WizardError(
|
|
1730
1742
|
t["errors"]["still_unapproved"].format(path=resolved))
|
|
1731
|
-
|
|
1743
|
+
variants = t["echo_variants"]
|
|
1744
|
+
approved_key = ("approved_final_verification"
|
|
1745
|
+
if state.task_type == "final-verification"
|
|
1746
|
+
and variants.get("approved_final_verification")
|
|
1747
|
+
else "approved")
|
|
1748
|
+
echo = variants[approved_key].format(path=resolved)
|
|
1732
1749
|
if apply_option:
|
|
1733
1750
|
_apply_cli_implementation_option(
|
|
1734
1751
|
str(resolved), state.html_approval_option)
|
|
@@ -1748,10 +1765,7 @@ def _build_stage_pick(state: WizardState) -> Prompt:
|
|
|
1748
1765
|
t = _p(state.workspace_root, "stage_pick")
|
|
1749
1766
|
stages = _parse_stage_objects(state)
|
|
1750
1767
|
is_fv = state.task_type == "final-verification"
|
|
1751
|
-
label = (
|
|
1752
|
-
t.get("label_final_verification", t["label"])
|
|
1753
|
-
if is_fv else t["label"]
|
|
1754
|
-
)
|
|
1768
|
+
label = (t["label_final_verification"] or t["label"]) if is_fv else t["label"]
|
|
1755
1769
|
done = _done_stage_numbers(state) if is_fv else set()
|
|
1756
1770
|
options = []
|
|
1757
1771
|
if _stage_auto_allowed(state):
|