okstra 0.56.0 → 0.57.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 +2 -2
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/prompts/profiles/_common-contract.md +4 -4
- package/runtime/prompts/profiles/_implementation-deliverable.md +4 -4
- package/runtime/prompts/profiles/_implementation-executor.md +1 -4
- package/runtime/prompts/profiles/final-verification.md +1 -1
- package/runtime/prompts/profiles/implementation-planning.md +5 -5
- package/runtime/prompts/profiles/implementation.md +6 -6
- package/runtime/prompts/profiles/improvement-discovery.md +1 -0
- package/runtime/prompts/profiles/release-handoff.md +4 -4
- package/runtime/prompts/wizard/prompts.ko.json +4 -1
- package/runtime/python/okstra_ctl/wizard.py +95 -30
- package/runtime/schemas/final-report-v1.0.schema.json +127 -10
- package/runtime/skills/okstra-coding-preflight/SKILL.md +8 -0
- package/runtime/skills/okstra-coding-preflight/clean-code.md +6 -0
- package/runtime/skills/okstra-run/SKILL.md +1 -1
- package/runtime/skills/okstra-run/templates/pr-body.template.md +12 -12
- package/runtime/templates/reports/final-report.template.md +63 -19
- package/runtime/templates/reports/i18n/en.json +1 -1
- package/runtime/templates/reports/i18n/ko.json +1 -1
- package/runtime/templates/reports/implementation-input.template.md +1 -1
- package/runtime/templates/reports/implementation-planning-input.template.md +3 -3
- package/runtime/validators/validate-run.py +2 -0
package/docs/kr/architecture.md
CHANGED
|
@@ -343,7 +343,7 @@ okstra phase 는 PRD / issue file 을 직접 쓰지 않습니다. 동등한 결
|
|
|
343
343
|
|---|---|---|---|---|
|
|
344
344
|
| `requirements-discovery` | 요청을 bugfix/feature/refactor/ops/improvement 중 하나로 분류하고 안전한 다음 phase로 라우팅 | work category, routing decision, missing-input list, clarification requests | `pending-routing-decision` (사용자 답변 후 결정) | 금지 |
|
|
345
345
|
| `error-analysis` | 보고된 에러/사고의 증상·원인·재현 갭을 증거 기반으로 분석 | symptom/trigger 정리, root-cause 가설, reproduction gap, validation 경로 | `implementation-planning` | 금지 |
|
|
346
|
-
| `implementation-planning` | 코딩 시작 전 안전한 구현 방향과 옵션을 평가 | 최소 2개 구현 옵션, 영향 파일 목록, trade-off,
|
|
346
|
+
| `implementation-planning` | 코딩 시작 전 안전한 구현 방향과 옵션을 평가 | 최소 2개 구현 옵션, 영향 파일 목록, trade-off, validation/rollback, YAML frontmatter `approved: false` / `implementation-option:`, **§5.5.9 Plan Body Verification** (Phase 6 워커 사후 검증 라운드 — 합성된 plan 의 내적 일관성을 워커가 `AGREE` / `DISAGREE(a-e)` / `SUPPLEMENT` 로 cross-verify; gate 결과가 `passed` / `passed-with-dissent` 일 때만 사용자가 frontmatter 를 `approved: true` 로 뒤집을 수 있고, `blocked-by-disagreement` / `aborted-non-result` 일 때는 majority DISAGREE 항목이 `## 1. Clarification Items` 의 `Blocks=approval` row 로 변환됨). **산출 구조**: 항상 `## 5.5 Stage Map` + N 개의 `## 5.5.<i> Stage <i>` 섹션. 각 stage 의 effective step ≤ 6. `depends-on (none)` 인 stage 들은 별도 `implementation` run 으로 병렬 실행 가능 | `implementation` (사용자 승인 후) | 금지 |
|
|
347
347
|
| `implementation` | 승인된 `implementation-planning` final report의 단계대로 소스 코드를 수정. **한 run 에 한 stage 만 실행** (`--stage <auto\|N>` 인수로 stage 선택) | commit list, diff summary, out-of-plan edits 블록, validation/TDD evidence, rollback 검증, verifier 결과(Gemini/Codex/Claude), `carry/stage-<N>.json` evidence sidecar | `final-verification` | 허용 (승인된 plan의 파일 목록 한정, `git push`/publish/deploy/실제 migration 금지) |
|
|
348
348
|
| `final-verification` | 완료된 작업의 잔존 결함·회귀 위험을 점검하고 release 판단 | acceptance verdict, residual risk, follow-up 라우팅(`error-analysis`/`implementation-planning`/`release-handoff`) | `pending-release-handoff` (verdict 가 `accepted` 일 때만 `release-handoff` 로 진입; 그 외에는 `error-analysis` 또는 `implementation-planning` 으로 리라우팅) | 금지 (read-only 테스트만 허용) |
|
|
349
349
|
| `release-handoff` | `accepted` 받은 변경을 사용자가 선택한 방식대로 커밋·푸시·PR 로 전달 | 사용자 메뉴 응답(H1 action / H2 PR base / H3 message handling) 기록, 실행한 git/gh 명령 로그, commit SHA 목록, PR URL | `done-or-follow-up` | 허용 — 단 **사용자가 메뉴로 선택한 mutating 명령만** 실행. `git push --force*`, base 브랜치 직접 push, `--no-verify`, `gh release`, publish/deploy 는 금지. source code 자체는 수정 금지(이전 `implementation` 의 diff 를 그대로 패키징). |
|
|
@@ -355,7 +355,7 @@ okstra phase 는 PRD / issue file 을 직접 쓰지 않습니다. 동등한 결
|
|
|
355
355
|
- **implementation stage 격리 worktree (동시 병렬)**: 위 task-key 단위 worktree 는 `requirements-discovery`~`implementation-planning` 의 모델입니다. `implementation` task 는 **stage 격리** 로 동작합니다 (spec `docs/superpowers/specs/2026-06-06-stage-worktree-isolation-design.md`) — **한 run = 한 stage**, 각 run 이 `.../<task-id-segment>/stage-<N>/` (브랜치 `<prefix>-<task-id-segment>-s<N>`) 격리 worktree 를 발급받습니다. registry 가 task-key 와 **stage-key** (`<task-key>#stage-<N>`) 를 함께 flock 예약하고, `_resolve_effective_stages` 가 `consumers.jsonl` 의 `started` + registry 예약 stage 를 ready 집합에서 제외하므로(점유 SSOT = registry), 사용자가 두 `implementation` run 을 동시에 띄우면 서로 다른 독립 stage 를 충돌 없이 진행합니다. base 결정: 독립 = 공통 anchor(첫 stage 진입 HEAD 고정), 단일 의존 = 선행 done commit, 다중 의존 = 선행이 모두 ancestor 인 task worktree HEAD(`git merge-base --is-ancestor`; 미머지 시 `PrepareError`). cost-aware-design 의 ready-set batch 는 stage 마다 격리 branch 가 필요해 의미를 잃으므로(같은 branch 에 두 stage-key reserve 시 branch-uniqueness 충돌) 폐기되었고, 순차 진행은 stage done 후 다음 run, 동시 진행은 별도 run 으로 — cost 등가. `--stage <auto|N>` 또는 wizard `stage_pick` 으로 stage 를 선택합니다.
|
|
356
356
|
- `implementation` 과 `release-handoff` 를 제외한 모든 phase 는 source code edit, build, migration, deployment, 그 밖의 state-mutating 명령을 금지합니다 (`final-verification` 은 read-only 테스트 명령만 허용). `implementation` 은 승인된 plan 의 파일 목록 안에서만 edit/commit 이 허용되며, `git push`·publish·deploy·실제 migration·third-party write API 는 여전히 금지됩니다. `release-handoff` 는 source code 자체는 수정하지 않고, 사용자가 메뉴로 선택한 commit / push / PR 명령만 실행합니다 (force push, base 브랜치 직접 push, hook bypass, release publish 는 여전히 금지).
|
|
357
357
|
- 사용자가 "다음 단계 진행해" 같은 표현을 보내도, 그 발화만으로 다음 phase가 자동 시작되지 않습니다. 다음 phase는 새 `okstra.sh` 실행으로만 시작합니다.
|
|
358
|
-
- **Authority & permissions assumption (모든 task-type 및 `okstra-schedule` 공통)**: 사용자(및 팀)는 예상되는 모든 작업에 대해 완전한 권한·승인 권한을 보유한다고 가정합니다. 외부 승인, 서드파티 액세스, 역할/IAM 권한, 조직적 sign-off, 법무·보안 검토, 벤더 협의, "권한 보유 여부 확인" 같은 항목을 routing 결정·missing inputs·clarification questions·risk·dependency·open questions·effort/day 추정에 포함하지 않습니다. okstra 내부 phase 핸드오프(`
|
|
358
|
+
- **Authority & permissions assumption (모든 task-type 및 `okstra-schedule` 공통)**: 사용자(및 팀)는 예상되는 모든 작업에 대해 완전한 권한·승인 권한을 보유한다고 가정합니다. 외부 승인, 서드파티 액세스, 역할/IAM 권한, 조직적 sign-off, 법무·보안 검토, 벤더 협의, "권한 보유 여부 확인" 같은 항목을 routing 결정·missing inputs·clarification questions·risk·dependency·open questions·effort/day 추정에 포함하지 않습니다. okstra 내부 phase 핸드오프(`implementation-planning`의 `approved:` frontmatter 등)는 사용자 본인이 즉시 승인 가능한 내부 게이트이므로 영향 없으며, `implementation`의 forbidden actions(`git push`, prod deploy, shared-DB migration 등)도 권한 사유가 아닌 **안전 사유**로 계속 적용됩니다.
|
|
359
359
|
- Phase별 상세 규칙은 `prompts/profiles/<task-type>.md`에 정의되어 있고, 그 본문이 그대로 `instruction-set/analysis-profile.md`로 렌더링됩니다.
|
|
360
360
|
|
|
361
361
|
### Phase 간 정보 전달
|
package/package.json
CHANGED
package/runtime/BUILD.json
CHANGED
|
@@ -25,7 +25,7 @@ profile document.
|
|
|
25
25
|
- Authority & permissions assumption (applies to every okstra task-type):
|
|
26
26
|
- **Assume the user (and their team) holds full authority and every permission required for the anticipated, in-flight, or follow-up work.** Treat external approvals, third-party access grants, role/IAM permissions, organisational sign-off, legal/compliance review, vendor coordination, and "verify access exists" steps as already satisfied unless the task brief explicitly states otherwise.
|
|
27
27
|
- Do NOT add such items to routing decisions, missing-materials lists, clarification questions, option trade-offs, dependency/migration risk, validation checklists, rollout plans, acceptance blockers, residual risks, release recommendations, the `## 1. Clarification Items` table, or any day/effort estimate. They are not legitimate sources of schedule extension.
|
|
28
|
-
- Internal okstra phase handoffs (e.g. the `
|
|
28
|
+
- Internal okstra phase handoffs (e.g. the `approved:` frontmatter gate in `implementation-planning`) are unaffected — those are the user themselves approving and proceed without external coordination.
|
|
29
29
|
- This rule does NOT relax any phase-specific Forbidden actions list; safety rules in the per-profile document remain in force regardless of the user's authority.
|
|
30
30
|
- Anti-escalation rule (shared):
|
|
31
31
|
- treating "다음 단계 진행해" or equivalent user phrases as authorisation to start a *different* lifecycle phase is forbidden. The next phase begins only in a separate okstra run launched with the new `--task-type`. Per-profile documents may further restrict this within their own scope.
|
|
@@ -61,7 +61,7 @@ profile document.
|
|
|
61
61
|
- **Reporter confirmation precondition (BLOCKING)**: the brief's frontmatter carries `reporter-confirmations: <complete | partial | pending | skipped>` set by `okstra-brief` Step 6.5. Every phase that consumes the brief MUST read this field before doing analysis. The handling matrix is:
|
|
62
62
|
- `complete` → proceed normally.
|
|
63
63
|
- `partial` → proceed; treat still-unmarked `intent-check:` / `conversion-block:` rows as the `skipped` branch.
|
|
64
|
-
- `skipped` → do NOT silently infer the missing answers. Promote each unmarked `intent-check:` / `conversion-block:` row into this run's `## 1. Clarification Items` as `Kind=decision`. Use `Blocks=approval` in `implementation-planning`, where the row gates the
|
|
64
|
+
- `skipped` → do NOT silently infer the missing answers. Promote each unmarked `intent-check:` / `conversion-block:` row into this run's `## 1. Clarification Items` as `Kind=decision`. Use `Blocks=approval` in `implementation-planning`, where the row gates the `approved:` frontmatter flip; otherwise use `Blocks=next-phase`. The recommended answer is drawn from the brief's matching content and clearly labelled `보고자 직접 확인 권장`.
|
|
65
65
|
- `pending` (or field missing) → ABORT analysis; render the Verdict Card with `Verdict Token = blocked` + `Direction = hold` and write a single `## Reporter Confirmation Required` block (no leading number) summarising which rows are pending. The `## 1. Clarification Items` table carries one row per pending item with `Blocks=approval` in `implementation-planning`, otherwise `Blocks=next-phase`. The operator must rerun `okstra-brief` Step 6.5. Do NOT emit `## 0.` for this case — Section 0 is reserved for clarification-response carry-in only.
|
|
66
66
|
`[CONFIRMED <YYYY-MM-DD> → RC-N]` markers on `Open Questions` rows are the per-row signal that the reporter has answered; their answers live verbatim under `## Reporter Confirmations` in the brief.
|
|
67
67
|
- `Source Material` is reporter-verbatim. Do NOT paraphrase, summarize, reorder, or restructure it. Quote it directly when needed.
|
|
@@ -81,9 +81,9 @@ profile document.
|
|
|
81
81
|
- **Canonical column schema (SSOT — must match `templates/reports/final-report.template.md` §1 exactly):** every `## 1. Clarification Items` table has exactly these 8 columns, in this order:
|
|
82
82
|
`| ID | Ticket ID | Kind | Statement | Expected form | Blocks | Status | User input |`.
|
|
83
83
|
Profile-specific addenda may tighten cell content but MUST NOT add, remove, rename, or reorder columns. The `ID` cell uses `C-NNN` (3-digit zero-padded), the `Status` cell ∈ `{open, answered, resolved, obsolete}`, and the `Kind` / `Blocks` legal values are listed below.
|
|
84
|
-
- section 1 is a **single unified table** per `final-report-template.md`. Every clarification item — whether the user must attach a file, choose between options, or supply a single number/path — is one row of that table. Do not split it into sub-sections (`1.1 추가 자료 요청` / `1.2 사용자 확인 질문` / `5.5.9 Open Questions` are removed and the validator fails reports that reintroduce them), do not create a parallel table elsewhere in the report, and do not duplicate the same item into
|
|
84
|
+
- section 1 is a **single unified table** per `final-report-template.md`. Every clarification item — whether the user must attach a file, choose between options, or supply a single number/path — is one row of that table. Do not split it into sub-sections (`1.1 추가 자료 요청` / `1.2 사용자 확인 질문` / `5.5.9 Open Questions` are removed and the validator fails reports that reintroduce them), do not create a parallel table elsewhere in the report, and do not duplicate the same item into an approval block or any other section.
|
|
85
85
|
- each row's `Kind` column picks one of `{material, decision, data-point}`: `material` for files / snapshots / logs / screenshots the user must attach (the `User input` cell will hold a path or URL); `decision` for choices and yes/no confirmations only the user can make; `data-point` for a single number, ID, date, or short string the user can answer inline. Items that mix "yes/no + file path if yes" are one row of `Kind=material` with the combined expectation written into `Expected form`.
|
|
86
|
-
- each row's `Blocks` column picks one of `{approval, next-phase, none}`. `approval` is reserved for items that gate an approval action, especially the `implementation-planning`
|
|
86
|
+
- each row's `Blocks` column picks one of `{approval, next-phase, none}`. `approval` is reserved for items that gate an approval action, especially the `implementation-planning` `approved:` frontmatter flip; outside `implementation-planning`, unresolved brief reporter-confirmation rows use `next-phase` instead. `next-phase` blocks the next run from starting cleanly. `none` is informational/audit-only.
|
|
87
87
|
- write every entry in full, descriptive sentences that a non-developer can act on without further context. Avoid abbreviations and internal jargon. The `Statement` cell must state *what* is needed, *why* the answer / attachment changes the next step, and (for `material`) *where* the user can find it and *where* to place it. The `Expected form` cell must state the answer shape (예/아니오, 보기 중 하나, 숫자/날짜, 파일 경로, 짧은 서술 등); supply concrete option choices when applicable.
|
|
88
88
|
- if a phase requires a recommended answer, alternatives, or an evidence-check note, encode it inside the existing 8-column schema: put evidence notes in `Statement` as `Evidence checked: <path:line>` or `Evidence checked: none — <human-only reason>`, and put recommendations/options in `Expected form` as `Recommended: <answer> — <rationale>; Alternatives: <options>`. Do not add `Recommended`, `Evidence`, `Alternatives`, or `evidence-checked` columns.
|
|
89
89
|
- the same `final-report.md` file is the canonical artifact carried into the next run; the user appends answers inline before rerunning. The preferred turn-around is `scripts/okstra.sh --resume-clarification --task-key <project-id>:<task-group>:<task-id>` (opens the latest report in `$EDITOR`, then auto-reruns the same phase with `--clarification-response` carry-in). The lower-level form `--clarification-response <path>` remains available for scripted runs.
|
|
@@ -48,7 +48,7 @@ are collected and convergence finished. Phase 1-5 do not need it.
|
|
|
48
48
|
## Lead post-stage persistence (BLOCKING — runs after the Executor emits `### Stage Carry Evidence`)
|
|
49
49
|
|
|
50
50
|
- Parse the executor's `### Stage Carry Evidence` JSON block. If absent or unparsable, end with status `contract-violated` and route to a follow-up `error-analysis`.
|
|
51
|
-
- For
|
|
52
|
-
- For
|
|
53
|
-
- The verifier round, Phase 5.5 convergence, and this Phase 6 report run **once per run** over
|
|
54
|
-
- Quote
|
|
51
|
+
- For this run's single stage: write its JSON verbatim to `runs/<impl-task-key>/carry/stage-<N>.json`. Refuse to overwrite an existing file (one stage = one sidecar; re-runs are out of scope for this version).
|
|
52
|
+
- 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. Use the okstra runtime's `consumers_mutex` helper (NOT a raw filesystem write) to honour the lock. `report_path` lets `final-verification` cite each stage's originating report when assembling its Source Implementation Report list.
|
|
53
|
+
- The verifier round, Phase 5.5 convergence, and this Phase 6 report run **once per run** over this stage's diff — NOT per step.
|
|
54
|
+
- Quote this stage's new contents (the sidecar JSON in full and the new consumers row by itself) in the final report's `Stage sidecar evidence` deliverable section.
|
|
@@ -48,10 +48,7 @@ until Phase 5 ends, then drop from active context for Phase 6/7.
|
|
|
48
48
|
|
|
49
49
|
- **Sidecar evidence writer (BLOCKING).** When this stage's Stage Validation `post` commands all succeed, the Executor MUST emit a JSON object matching the schema in `docs/superpowers/specs/2026-05-20-implementation-planning-multi-stage-design.md` §3.2 and the lead MUST persist it to `runs/<impl-task-key>/carry/stage-<N>.json`. The file MUST NOT exist before the run starts (overwrite is refused — see `--force-stage` non-goal).
|
|
50
50
|
- **Reverse link (BLOCKING).** The runtime already appended a `status:"started"` row for this stage before the run began. On completion, append a `status:"done"` row with `carry_path` populated for this stage number.
|
|
51
|
-
- **
|
|
52
|
-
- `## Stage <N>` — number, title (from Stage Map row), touched files, and validation result.
|
|
53
|
-
- `## Carry-In summary` — depends-on list + cited identifiers/SHAs from each loaded sidecar (omit when depends-on is empty).
|
|
54
|
-
- `## Previous run` / `## Next run` — links so a reviewer can navigate the run chain.
|
|
51
|
+
- **No PR / push in this phase.** This run produces local commits, carry sidecar evidence, verifier results, and the implementation final report only. Push and PR creation belong exclusively to the later `release-handoff` phase after `final-verification` returns `accepted`.
|
|
55
52
|
|
|
56
53
|
## Allowed actions during the run
|
|
57
54
|
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
- Clarification request policy (phase-specific addendum — shared policy is in `_common-contract.md`):
|
|
42
42
|
- populate `## 1. Clarification Items` only when a blocker hinges on information only the user can supply (deployment intent, intended target environment, business-rule interpretation); use `Blocks=next-phase` for items that gate continuing to release-handoff
|
|
43
43
|
- Self-review pass before finalising the report (`Claude lead` runs this; do not delegate to a generic subagent):
|
|
44
|
-
1. **Verdict precision** — section
|
|
44
|
+
1. **Verdict precision** — section 7 (`Final Verdict`) includes `Verdict Token` with one of the three allowed verdict tokens; `conditional-accept` lists every condition as an actionable item.
|
|
45
45
|
2. **Blocker traceability** — every blocker cites a concrete artifact (file:line, log excerpt, test exit code, MCP SELECT). Blockers without evidence are demoted to residual risk or removed.
|
|
46
46
|
3. **Coverage check** — every requirement in the originating plan/task brief is either marked covered (with artifact) or listed as a blocker. No silent omissions.
|
|
47
47
|
4. **Verifier dissent preserved** — if workers reach different verdicts, the disagreement is visible in section 1.2; synthesis hides nothing.
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
- gemini — when added to the roster it joins the analyser set; omitted by default
|
|
10
10
|
{{INCLUDE:_common-contract.md}}
|
|
11
11
|
- Brief consumption (phase-specific addendum — shared rules live in `_common-contract.md` under "Brief handoff contract"):
|
|
12
|
-
- Apply the shared reporter-confirmation precondition exactly as written. In this phase, unresolved `intent-check:` / `conversion-block:` rows use `Blocks=approval`; the operator cannot
|
|
12
|
+
- Apply the shared reporter-confirmation precondition exactly as written. In this phase, unresolved `intent-check:` / `conversion-block:` rows use `Blocks=approval`; the operator cannot flip the approval frontmatter to `approved: true` until those rows are resolved.
|
|
13
13
|
- never plan around an unconfirmed `intent-inference` augmentation as if it were a settled requirement. After the precondition runs, a `[CONFIRMED …]` marker on the matching `intent-check:` row is the signal that the inference can be treated as settled; otherwise it remains a `Blocks=approval` clarification item per the precondition's `skipped` branch.
|
|
14
14
|
- `conversion-block:` rows are handled by the precondition; planning around an untranslated reporter phrase is forbidden until it is resolved.
|
|
15
15
|
- Pre-planning context exploration (mandatory before option drafting):
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
- Section heading contract (BLOCKING — validator scans for these literal English substrings):
|
|
56
56
|
- The final report MUST include section headings containing each of the following exact strings: `Option Candidates`, `Trade-off`, `Recommended Option`, `Stage Map`, `Stage Exit Contract`, `Stage Validation`, `Dependency`, `Validation Checklist`, `Rollback`, `Requirement Coverage`. (Approval is no longer a body section — it is the YAML frontmatter `approved` field.)
|
|
57
57
|
- Korean translations are allowed in parentheses (e.g. `### Recommended Option (권장 옵션)`), but the English keyword must be present verbatim in the heading line.
|
|
58
|
-
- The shape and ordering follow `final-report-template.md` section
|
|
58
|
+
- The shape and ordering follow `final-report-template.md` section 5.5 (`Implementation Plan Deliverables` + `Stage Map`). Do NOT translate the heading keywords — `validators/validate-run.py` does substring matching on the raw report text and missing English strings are a real, repeatedly observed failure mode (root cause: writer translated the headings to Korean).
|
|
59
59
|
- Beyond substring matching, when the Plan Body Verification gate result is `passed` / `passed-with-dissent`, `validators/validate-run.py` runs the **structural** Stage Map validator (`validators/validate-implementation-plan-stages.py`) at the planning boundary — the exact `## 5.5 Stage Map` heading, each `## 5.5.<i> Stage <i>:` section with its four required subsections, the per-stage effective step count (≤6), the `depends-on` DAG, and the per-stage vertical-slice contract (S10) are all enforced here, not deferred to the `implementation` entry gate. S10 scans for the literal in-section strings `Slice value:`, `Acceptance:`, and the Stepwise `action`-cell prefixes `RED:` / `GREEN:` (or a `TDD exemption:` line) — keep these tokens verbatim for the same reason as the heading keywords above.
|
|
60
60
|
- Required deliverable shape (final report, in addition to the standard sections):
|
|
61
61
|
- at least two implementation options. **Each option must include**:
|
|
@@ -88,8 +88,8 @@
|
|
|
88
88
|
- the YAML frontmatter MUST include the line `approved: false` (report-writer always emits the unflipped value). The user authorises the next `implementation` run by flipping it to `approved: true` (manual edit or `--approve` CLI). Do NOT recreate any `User Approval Request` body block — the validator fails reports that contain one (see `validators/validate-run.py` deprecated patterns).
|
|
89
89
|
- the YAML frontmatter MUST include the line `implementation-option:` directly under `approved:` (report-writer always emits it with an **empty value**). The user selects which Option Candidate the next `implementation` run executes by filling this line with that option's name (manual edit or `--implementation-option <name>` CLI). When left empty, the `implementation` run falls back to the `Recommended Option`.
|
|
90
90
|
- **the frontmatter `approved: false` line is rendered unconditionally; if the plan-body verification gate (§5.5.9) returns `blocked-by-disagreement` or `aborted-non-result`, the writer MUST keep `approved: false` and the validator refuses any report that ships with `approved: true` under such a gate result.**
|
|
91
|
-
- every ambiguity flagged during pre-planning that the user must resolve before approval registered as a `Blocks=approval` row in the `## 1. Clarification Items` table (do NOT create a separate `Open Questions` block under
|
|
92
|
-
- **§5.5.9 Plan Body Verification (BLOCKING).** After report-writer finishes the draft, the lead MUST run a worker peer-review round on the consolidated plan body (sections
|
|
91
|
+
- every ambiguity flagged during pre-planning that the user must resolve before approval registered as a `Blocks=approval` row in the `## 1. Clarification Items` table (do NOT create a separate `Open Questions` block under the implementation plan body — the unified table is the single home)
|
|
92
|
+
- **§5.5.9 Plan Body Verification (BLOCKING).** After report-writer finishes the draft, the lead MUST run a worker peer-review round on the consolidated plan body (Option Candidates / Trade-off Matrix / Recommended Option / Stage Map and per-stage sections / Dependency / Validation Checklist / Rollback / Requirement Coverage) and populate `### 5.5.9 Plan Body Verification` in the final report. The round protocol, plan-item ID scheme (`P-Opt-*` / `P-Step-*` / `P-Dep-*` / `P-Val-*` / `P-Rb-*`), verdict semantics, gate-result classification, and dissent log format are defined in `skills/okstra-convergence/SKILL.md` "Plan-body verification mode". The four gate-result values are `passed`, `passed-with-dissent`, `blocked-by-disagreement`, `aborted-non-result`. When the gate would have been `blocked-by-disagreement` or `aborted-non-result`, the lead MUST NOT silently flip it to one of the passing values to "unblock" the run — that is a contract violation. When `convergence.adversarial=true` (the default for this phase), this round uses the adversarial posture — verifiers confirm cited paths/commands and the burden of proof is on the plan — but the gate threshold stays `majority-disagree` (see that skill's §"Adversarial plan-body posture").
|
|
93
93
|
- **Decision-record evaluation (sole owner)**: this phase is the **single owner** of decision-record evaluation in the okstra lifecycle. The brief never evaluates or drafts decision records — it only forwards `adr-candidate:*` signals. Every `adr-candidate:*` entry inherited from the brief's `Open Questions` is a mandatory evaluation target. In addition, evaluate every decision the recommended option introduces against the three criteria:
|
|
94
94
|
1. **Hard to reverse** — would changing the decision later cost meaningfully more than deciding now?
|
|
95
95
|
2. **Surprising without context** — would a future reader, seeing only the code, wonder "why was it built this way?"?
|
|
@@ -109,5 +109,5 @@
|
|
|
109
109
|
4. **Ambiguity check** — any requirement that could be read two ways must be made explicit or moved to the `## 1. Clarification Items` table as a `Blocks=approval` row.
|
|
110
110
|
5. **Scope check** — if the recommended plan now spans multiple independent subsystems, recommend splitting into separate planning runs rather than shipping an oversized plan.
|
|
111
111
|
6. **Review-rule preflight check** — if a project review rule pack exists, map each relevant rule to the recommended option. Reject the draft if it knowingly creates a violation that the later PR reviewer would flag, unless the plan records a specific rationale and follow-up. In particular, scan for repeated helper stacks across planned files, tests that assert delegation to the same calculator/helper they exercise, public names that hide side effects, domain rules placed in repositories/adapters, and APIs made dead by this change.
|
|
112
|
-
7. **Plan-body verification reconciliation (BLOCKING for implementation-planning).** Inspect the `### 5.5.9 Plan Body Verification` verdict table. For every plan-item row classified as `majority-disagree → C-<N>`, the corresponding `C-<N>` row MUST exist in `## 1. Clarification Items` with `Kind` chosen per the standard policy and `Blocks=approval`. Do NOT create a parallel
|
|
112
|
+
7. **Plan-body verification reconciliation (BLOCKING for implementation-planning).** Inspect the `### 5.5.9 Plan Body Verification` verdict table. For every plan-item row classified as `majority-disagree → C-<N>`, the corresponding `C-<N>` row MUST exist in `## 1. Clarification Items` with `Kind` chosen per the standard policy and `Blocks=approval`. Do NOT create a parallel `Open Questions` block under the implementation plan body — the unified table is the single home. Conversely, the `Classification` column's `C-<N>` reference and the `## 1. Clarification Items` `ID` column MUST match 1:1; an orphan on either side is a contract violation. For `partial-consensus` and `worker-unique` plan-items, the dissenting opinion lives in §5.5.9 `Dissent log` and is NOT promoted to §5.
|
|
113
113
|
8. **Stage Map self-check** — for every stage, count the effective rows of its `Stepwise Execution Order` table by hand; reject the draft if any stage exceeds 6. Confirm each stage declares a non-empty `Slice value:` and `Acceptance:` line, and that its first step `action` starts with `RED:` with a later `GREEN:` (or carries a `TDD exemption:` line) — this is what validator S10 enforces. Walk the `depends-on` graph and confirm it is a DAG (no cycle, no self-reference). For each `depends-on` link, confirm it encodes a real data/contract dependency — do NOT add links to serialise unrelated work, and do NOT split a stage merely to create more parallel stages. **Parallel-safety:** for every pair of `depends-on (none)` stages, confirm their `Stage Exit Contract` predicted file sets are disjoint; if they share a file, merge them or add a `depends-on` link (validator S9 rejects overlap).
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Implementation Profile
|
|
2
2
|
|
|
3
3
|
- Purpose: realise the approved `implementation-planning` deliverable as actual source changes, with cross-model verification, while keeping the run reversible
|
|
4
|
-
- **Run-level fixed cost:** the verifier set, Phase 5.5 convergence, and the Phase 6 report-writer run exactly once per run, over
|
|
4
|
+
- **Run-level fixed cost:** the verifier set, Phase 5.5 convergence, and the Phase 6 report-writer run exactly once per implementation run, over this run's single stage diff — never once per step.
|
|
5
5
|
- Required workers:
|
|
6
6
|
- claude
|
|
7
7
|
- codex
|
|
@@ -20,14 +20,14 @@
|
|
|
20
20
|
- that file's YAML frontmatter MUST carry `approved: true`. report-writer emits `approved: false` by default; the user flips it to `true` to authorise this run. Free-form approvals such as "lgtm" / "go ahead" / paraphrased confirmations are NOT accepted; re-edit the plan file's frontmatter to `approved: true` before invoking implementation, or pass `--approve` so the CLI flips it on the user's behalf (`okstra_ctl.run._apply_cli_approval`).
|
|
21
21
|
- The `--approve` flag is meaningful ONLY with `--task-type implementation` and `--approved-plan <path>`; any other use raises `PrepareError`. Idempotent — re-running with `approved: true` already set appends an audit line but does NOT re-toggle.
|
|
22
22
|
- the authoritative scope for this run is the Option Candidate named by the YAML frontmatter `implementation-option:` field. **If `implementation-option:` is empty, fall back to the plan's `Recommended Option`** (this is a soft fallback, not a hard block). The chosen option's bite-sized step list becomes the authoritative scope; deviations must be justified in the final report and routed back to a new `implementation-planning` run rather than silently expanded. If the chosen option name does not match any heading under `Option Candidates`, record it as a deviation.
|
|
23
|
-
-
|
|
23
|
+
- Stage worktree (provisioned by `okstra-ctl` at this implementation run's prep time):
|
|
24
24
|
- Status: `{{EXECUTOR_WORKTREE_STATUS}}` (one of: `created` | `reused` | `skipped-in-worktree` | `skipped-not-git`)
|
|
25
|
-
- Working tree path: `{{EXECUTOR_WORKTREE_PATH}}` — when status is `created` or `reused`, this is
|
|
26
|
-
- Branch: `{{EXECUTOR_WORKTREE_BRANCH}}` — empty when status is `skipped-*`. Branch name = `<work-category-prefix>-<task-id-segment>`, globally unique via `~/.okstra/worktrees/registry.json`.
|
|
27
|
-
- Base ref: `{{EXECUTOR_WORKTREE_BASE_REF}}` — canonical `<base>` for every `git diff` / `git log` in this run.
|
|
25
|
+
- Working tree path: `{{EXECUTOR_WORKTREE_PATH}}` — when status is `created` or `reused`, this is this run's isolated stage worktree rooted at `~/.okstra/worktrees/<project>/<task-group>/<task-id>/stage-<N>/`. When skipped, this is the caller's `project_root`.
|
|
26
|
+
- Branch: `{{EXECUTOR_WORKTREE_BRANCH}}` — empty when status is `skipped-*`. Branch name = `<work-category-prefix>-<task-id-segment>-s<N>`, globally unique via `~/.okstra/worktrees/registry.json`.
|
|
27
|
+
- Base ref: `{{EXECUTOR_WORKTREE_BASE_REF}}` — canonical `<base>` for every `git diff` / `git log` in this run. Independent stages start from the task anchor; dependent stages start from the predecessor done commit or the verified merged task worktree head.
|
|
28
28
|
- Provisioning note: `{{EXECUTOR_WORKTREE_NOTE}}`
|
|
29
29
|
- Treat the working-tree path as `project_root` for the duration of this run. Do NOT mutate the caller's original checkout. cwd-sensitive Bash commands MUST be prefixed `cd {{EXECUTOR_WORKTREE_PATH}} && ` in the same Bash invocation (never `bash -lc "..."` wrappers — see executor sidecar for full rules).
|
|
30
|
-
- Lifecycle: kept after the run completes
|
|
30
|
+
- Lifecycle: kept after the run completes as this stage's evidence worktree. Later stages get their own stage worktrees. Manual cleanup: `git worktree remove <path>` → `git branch -D <branch>` → drop the stage-key registry entry (`<task-key>#stage-<N>`).
|
|
31
31
|
- Approval gate (phase-specific addendum to shared authority rule):
|
|
32
32
|
- the pre-implementation gate's recorded user approval marker is the only authorised approval gate at this phase — proceed once it is satisfied without further external coordination.
|
|
33
33
|
- Forbidden actions — universal (any occurrence → terminal status `contract-violated`):
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
- When candidates branch on a structural question (e.g. "is module X meant to own this responsibility?"), resolve via `Read` / `Grep` first. Only escalate to the user inside the Phase 1.5 budget.
|
|
31
31
|
- Expected output emphasis:
|
|
32
32
|
- the `## 5.9 Improvement Candidates` table populated with rows that obey the 10-column schema from `validators/validate-improvement-report.py` (Cand ID `I-NNN`, Lens from whitelist, Title, Scope ⊆ scan-scope, Severity, Effort, Consensus, Source workers `<worker>:<id>` from {claude, codex, gemini}, Recommended next-phase ∈ {requirements-discovery, implementation-planning, error-analysis}, Evidence as path:line list)
|
|
33
|
+
- `Consensus` cells in `## 5.9 Improvement Candidates` use the table enum exactly: `full`, `partial`, `contested`, `worker-unique`. Map convergence's `full-consensus` / `partial-consensus` labels to `full` / `partial` before writing the table.
|
|
33
34
|
- `## 7. Final Verdict` Verdict Token ∈ {`candidates-ready`, `no-candidates`, `blocked`}; Direction `routing`; Next Step "사용자에게 후보 K개 선택 의뢰 (## 5.9 표 참조)"
|
|
34
35
|
- `## 3. Recommended Next Steps` first entry summarises per-candidate routing and proposes new task-key names of the form `<task-group>/imp-<Cand-ID>`
|
|
35
36
|
- this report is authored free-form (improvement-discovery is not in the data.json schema enum); after the markdown is written, the report-writer runs `scripts/okstra-inject-report-index.py <report.md> --report-language <en|ko>` to add the top-of-report Index + `I-NNN`/`C-NNN` scroll anchors. The run validator fails the report when the Index anchor is missing.
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
- Purpose: take an `accepted` final-verification verdict for an already-committed implementation branch and turn it into a delivered push and/or pull request, with explicit user selection at every mutating step
|
|
4
4
|
- **Execution model: single-lead, no worker dispatch.** This phase is a thin orchestrator over `git` / `gh`; it does NOT run team-mode, does NOT call `TeamCreate`, does NOT dispatch analysis or drafter sub-agents, and does NOT run convergence. The Claude lead performs every step inline (drafting PR text, asking the user, running git / gh, writing the final report) — see "Lead-only contract" below.
|
|
5
|
-
-
|
|
5
|
+
- Worker roster: none — this profile intentionally has no `- Required workers:` block; the run is executed entirely by the Claude lead.
|
|
6
6
|
- Lead-only contract (replaces the shared team contract for this phase):
|
|
7
7
|
- The Claude lead is the sole agent for this run. No `Agent(...)` worker dispatch, no `TeamCreate`, no parallel sub-agents, no convergence loop.
|
|
8
8
|
- The lead drafts the PR title and PR body **inline** by reading the run brief, the cited final-verification report, `git log --oneline <base>..HEAD`, and `git diff <base>..HEAD --stat`. No drafter worker is dispatched.
|
|
9
|
-
- The lead authors the final-report file directly (no `Report writer worker` dispatch). The report still conforms to the standard `
|
|
9
|
+
- The lead authors the final-report file directly (no `Report writer worker` dispatch). The report still conforms to the standard `templates/reports/final-report.template.md` structure, including the `## 5.6 Release Handoff Deliverables` section.
|
|
10
10
|
- The shared anti-escalation rule from the common contract still applies: do not start any other lifecycle phase from inside this run.
|
|
11
11
|
- The shared "authority & permissions assumption" rule from the common contract still applies: assume the user holds every permission needed; do not block on hypothetical approvals.
|
|
12
12
|
- The shared "MCP read-only" rule still applies if the brief lists MCP servers, though most release-handoff runs do not use MCP.
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
- `push + PR` — push the feature branch, then open or reuse a pull request.
|
|
23
23
|
- `skip` — record the verified state and end the run without any git command.
|
|
24
24
|
If the user picks `skip`, route directly to the final-report self-review pass.
|
|
25
|
-
2. **PR base branch** (only when the user picked `push + PR`) — present
|
|
25
|
+
2. **PR base branch** (only when the user picked `push + PR`) — present four options and capture exactly one:
|
|
26
26
|
- `staging`
|
|
27
27
|
- `preprod`
|
|
28
28
|
- `main`
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
- **User Selections**: a block recording each prompt and the user's verbatim answer.
|
|
76
76
|
- Q1 action: `local only` | `push + PR` | `skip`.
|
|
77
77
|
- Q2 PR base (if applicable): the chosen branch and how it was selected (menu pick vs free-form input).
|
|
78
|
-
- Q2b merge-conflict probe (if applicable): `clean` (no conflict, no prompt shown) | `proceed anyway` | `change base branch` | `cancel`. When a conflict was detected, list the conflicting paths.
|
|
78
|
+
- Q2b merge-conflict probe (if applicable): `not-run` | `clean` (no conflict, no prompt shown) | `proceed anyway` | `change base branch` | `cancel`. Record it in both the `User Selections` row `H2b` and the `Merge Conflict Probe` deliverable. When a conflict was detected, list the conflicting paths.
|
|
79
79
|
- Q3 title/body: `use as-is` | `edit then proceed` (with a diff between the lead's draft and the final text) | `cancel`.
|
|
80
80
|
- **Executed Commands**: every git / gh command the lead actually ran, with its exit code and a one-line stdout/stderr summary. Read-only inspection commands MAY be summarised; mutating commands MUST be listed verbatim.
|
|
81
81
|
- **Commit List**: each existing implementation commit in `git log <base>..HEAD`, with short/full SHA, subject line, and touched files. Release-handoff MUST NOT create new commits.
|
|
@@ -95,13 +95,16 @@
|
|
|
95
95
|
}
|
|
96
96
|
},
|
|
97
97
|
"brief_path_pick": {
|
|
98
|
-
"label": "task brief markdown
|
|
98
|
+
"label": "task brief markdown 을 선택해주세요",
|
|
99
99
|
"echo_template": "brief(pick): {value}",
|
|
100
100
|
"options": {
|
|
101
101
|
"__existing__": "기존 brief 사용: {existing}",
|
|
102
102
|
"__standard__": "표준 경로 사용: {standard}",
|
|
103
103
|
"__free_input__": "직접 입력"
|
|
104
104
|
},
|
|
105
|
+
"labels": {
|
|
106
|
+
"brief_candidate": "같은 task-group brief: {path}"
|
|
107
|
+
},
|
|
105
108
|
"errors": {
|
|
106
109
|
"existing_missing": "기존 brief 가 없습니다. 다른 옵션을 선택하세요."
|
|
107
110
|
}
|
|
@@ -99,6 +99,7 @@ PICK_USE_SUGGESTED = "__use_suggested__"
|
|
|
99
99
|
PICK_TYPE_CUSTOM = "__free_input__"
|
|
100
100
|
_RECENT_PREFIX = "__recent:"
|
|
101
101
|
_REPORT_PREFIX = "__report:"
|
|
102
|
+
_BRIEF_PREFIX = "__brief:"
|
|
102
103
|
|
|
103
104
|
# Lines of `key: value` we pull from a brief markdown frontmatter. The
|
|
104
105
|
# parser is intentionally lightweight (no yaml dep) and tolerant — a
|
|
@@ -176,6 +177,38 @@ def _brief_suggestions(path: Path) -> tuple[str, str]:
|
|
|
176
177
|
return tg, tid
|
|
177
178
|
|
|
178
179
|
|
|
180
|
+
def _project_relative_path(path: Path, project_root: Path) -> str:
|
|
181
|
+
try:
|
|
182
|
+
return str(path.relative_to(project_root))
|
|
183
|
+
except ValueError:
|
|
184
|
+
return str(path)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _accept_brief_path(state: WizardState, path: Path) -> None:
|
|
188
|
+
"""Record a validated brief and derive safe identity suggestions.
|
|
189
|
+
|
|
190
|
+
New-task runs now ask for ``task_group`` before the brief so the wizard
|
|
191
|
+
can offer same-group brief candidates. If the selected brief carries a
|
|
192
|
+
conflicting frontmatter ``task-group``, fail fast; otherwise keep using
|
|
193
|
+
the brief's ``brief-id`` as the task-id suggestion.
|
|
194
|
+
"""
|
|
195
|
+
tg_suggestion, tid_suggestion = _brief_suggestions(path)
|
|
196
|
+
if state.task_group and tg_suggestion:
|
|
197
|
+
suggested_group = _slug_or_die(tg_suggestion, "task_group")
|
|
198
|
+
if suggested_group != state.task_group:
|
|
199
|
+
raise WizardError(
|
|
200
|
+
"brief task-group does not match selected task-group: "
|
|
201
|
+
f"{tg_suggestion!r} != {state.task_group!r} ({path})"
|
|
202
|
+
)
|
|
203
|
+
state.brief_path = str(path)
|
|
204
|
+
state.brief_path_pending_text = False
|
|
205
|
+
if state.is_new_task:
|
|
206
|
+
if not state.task_group:
|
|
207
|
+
state.task_group_suggestion = tg_suggestion
|
|
208
|
+
if not state.task_id:
|
|
209
|
+
state.task_id_suggestion = tid_suggestion
|
|
210
|
+
|
|
211
|
+
|
|
179
212
|
# ---- Step IDs ------------------------------------------------------------
|
|
180
213
|
|
|
181
214
|
S_TASK_PICK = "task_pick"
|
|
@@ -771,6 +804,39 @@ def _suggest_recent_task_ids(state: WizardState, limit: int = 2) -> list[str]:
|
|
|
771
804
|
return seen
|
|
772
805
|
|
|
773
806
|
|
|
807
|
+
def _suggest_group_briefs(state: WizardState, limit: int = 6) -> list[str]:
|
|
808
|
+
"""Return recent okstra-brief outputs for the selected task-group.
|
|
809
|
+
|
|
810
|
+
``okstra-brief`` writes to ``.okstra/briefs/<task-group>/**/*.md``. The
|
|
811
|
+
wizard exposes those paths after task-group selection so users can pick a
|
|
812
|
+
generated brief instead of typing its path. Paths are project-relative.
|
|
813
|
+
"""
|
|
814
|
+
if not state.project_root or not state.task_group:
|
|
815
|
+
return []
|
|
816
|
+
project_root = Path(state.project_root)
|
|
817
|
+
root = project_root / ".okstra" / "briefs" / state.task_group
|
|
818
|
+
if not root.is_dir():
|
|
819
|
+
return []
|
|
820
|
+
candidates: list[tuple[float, str]] = []
|
|
821
|
+
for path in root.rglob("*.md"):
|
|
822
|
+
if not path.is_file():
|
|
823
|
+
continue
|
|
824
|
+
tg_suggestion, _ = _brief_suggestions(path)
|
|
825
|
+
if tg_suggestion:
|
|
826
|
+
try:
|
|
827
|
+
if _slug_or_die(tg_suggestion, "task_group") != state.task_group:
|
|
828
|
+
continue
|
|
829
|
+
except WizardError:
|
|
830
|
+
continue
|
|
831
|
+
try:
|
|
832
|
+
mtime = path.stat().st_mtime
|
|
833
|
+
except OSError:
|
|
834
|
+
mtime = 0.0
|
|
835
|
+
candidates.append((mtime, _project_relative_path(path, project_root)))
|
|
836
|
+
candidates.sort(key=lambda item: (-item[0], item[1]))
|
|
837
|
+
return [rel for _, rel in candidates[:limit]]
|
|
838
|
+
|
|
839
|
+
|
|
774
840
|
def _build_task_group(state: WizardState) -> Prompt:
|
|
775
841
|
sugg = state.task_group_suggestion
|
|
776
842
|
if sugg:
|
|
@@ -1000,6 +1066,12 @@ def _build_brief_path_pick(state: WizardState) -> Prompt:
|
|
|
1000
1066
|
if standard and standard != existing:
|
|
1001
1067
|
options.append(_opt("__standard__",
|
|
1002
1068
|
t["options"]["__standard__"].format(standard=standard)))
|
|
1069
|
+
brief_label = t["labels"].get("brief_candidate", "{path}")
|
|
1070
|
+
for relpath in _suggest_group_briefs(state):
|
|
1071
|
+
if relpath in (existing, standard):
|
|
1072
|
+
continue
|
|
1073
|
+
options.append(_opt(f"{_BRIEF_PREFIX}{relpath}",
|
|
1074
|
+
brief_label.format(path=relpath)))
|
|
1003
1075
|
options.append(_opt(PICK_TYPE_CUSTOM, t["options"][PICK_TYPE_CUSTOM]))
|
|
1004
1076
|
return Prompt(step=S_BRIEF_PATH_PICK, kind="pick",
|
|
1005
1077
|
label=t["label"], options=options,
|
|
@@ -1013,20 +1085,24 @@ def _submit_brief_path_pick(state: WizardState, value: str) -> Optional[str]:
|
|
|
1013
1085
|
t = _p(state.workspace_root, "brief_path_pick")
|
|
1014
1086
|
raise WizardError(t["errors"]["existing_missing"])
|
|
1015
1087
|
p = _require_file(existing, Path(state.project_root), "task brief")
|
|
1016
|
-
state
|
|
1017
|
-
state.brief_path_pending_text = False
|
|
1088
|
+
_accept_brief_path(state, p)
|
|
1018
1089
|
return f"brief: {p}"
|
|
1019
1090
|
if value == "__standard__":
|
|
1020
1091
|
p = _require_file(standard, Path(state.project_root), "task brief")
|
|
1021
|
-
state
|
|
1022
|
-
|
|
1092
|
+
_accept_brief_path(state, p)
|
|
1093
|
+
return f"brief: {p}"
|
|
1094
|
+
if value.startswith(_BRIEF_PREFIX):
|
|
1095
|
+
relpath = value[len(_BRIEF_PREFIX):]
|
|
1096
|
+
p = _require_file(relpath, Path(state.project_root), "task brief")
|
|
1097
|
+
_accept_brief_path(state, p)
|
|
1023
1098
|
return f"brief: {p}"
|
|
1024
1099
|
if value == PICK_TYPE_CUSTOM:
|
|
1025
1100
|
state.brief_path_pending_text = True
|
|
1026
1101
|
state.brief_path = ""
|
|
1027
1102
|
return None
|
|
1028
1103
|
raise WizardError(
|
|
1029
|
-
f"expected '__existing__', '__standard__',
|
|
1104
|
+
f"expected '__existing__', '__standard__', '{_BRIEF_PREFIX}<path>', "
|
|
1105
|
+
f"or {PICK_TYPE_CUSTOM!r}, "
|
|
1030
1106
|
f"got: {value!r}"
|
|
1031
1107
|
)
|
|
1032
1108
|
|
|
@@ -1042,16 +1118,7 @@ def _build_brief_path(state: WizardState) -> Prompt:
|
|
|
1042
1118
|
|
|
1043
1119
|
def _submit_brief_path(state: WizardState, value: str) -> Optional[str]:
|
|
1044
1120
|
p = _require_file(value, Path(state.project_root), "task brief")
|
|
1045
|
-
state
|
|
1046
|
-
state.brief_path_pending_text = False
|
|
1047
|
-
# When the user is starting a brand-new task, pull task-group /
|
|
1048
|
-
# task-id candidates from the brief frontmatter so the next two
|
|
1049
|
-
# prompts can offer them as a one-click pick instead of forcing a
|
|
1050
|
-
# free-text retype of what the brief already declares.
|
|
1051
|
-
if state.is_new_task and not state.task_group and not state.task_id:
|
|
1052
|
-
tg, tid = _brief_suggestions(p)
|
|
1053
|
-
state.task_group_suggestion = tg
|
|
1054
|
-
state.task_id_suggestion = tid
|
|
1121
|
+
_accept_brief_path(state, p)
|
|
1055
1122
|
return f"brief: {p}"
|
|
1056
1123
|
|
|
1057
1124
|
|
|
@@ -1907,13 +1974,25 @@ STEPS: list[Step] = [
|
|
|
1907
1974
|
"profile_optional_workers",
|
|
1908
1975
|
"task_group_suggestion", "task_id_suggestion",
|
|
1909
1976
|
"task_group_pending_text", "task_id_pending_text")),
|
|
1977
|
+
Step(S_TASK_GROUP,
|
|
1978
|
+
applies=lambda s: (bool(s.is_new_task)
|
|
1979
|
+
and not s.task_group
|
|
1980
|
+
and not s.task_group_pending_text),
|
|
1981
|
+
build=_build_task_group, submit=_submit_task_group,
|
|
1982
|
+
owns=("task_group", "task_group_pending_text")),
|
|
1983
|
+
Step(S_TASK_GROUP_TEXT,
|
|
1984
|
+
applies=lambda s: (bool(s.is_new_task)
|
|
1985
|
+
and not s.task_group
|
|
1986
|
+
and s.task_group_pending_text),
|
|
1987
|
+
build=_build_task_group_text, submit=_submit_task_group_text,
|
|
1988
|
+
owns=("task_group", "task_group_pending_text")),
|
|
1910
1989
|
Step(S_BRIEF_PATH_PICK,
|
|
1911
1990
|
applies=lambda s: (
|
|
1912
1991
|
not s.brief_path
|
|
1913
1992
|
and not s.brief_path_pending_text
|
|
1914
1993
|
and S_BRIEF_PATH_PICK not in s.answered
|
|
1915
1994
|
and (
|
|
1916
|
-
(s.is_new_task is True)
|
|
1995
|
+
(s.is_new_task is True and bool(s.task_group))
|
|
1917
1996
|
or (s.is_new_task is False
|
|
1918
1997
|
and S_TASK_TYPE in s.answered
|
|
1919
1998
|
and (s.keep_existing_brief is False
|
|
@@ -1927,20 +2006,6 @@ STEPS: list[Step] = [
|
|
|
1927
2006
|
and S_BRIEF_PATH not in s.answered,
|
|
1928
2007
|
build=_build_brief_path, submit=_submit_brief_path,
|
|
1929
2008
|
owns=("brief_path", "task_group_suggestion", "task_id_suggestion")),
|
|
1930
|
-
Step(S_TASK_GROUP,
|
|
1931
|
-
applies=lambda s: (bool(s.is_new_task)
|
|
1932
|
-
and bool(s.brief_path)
|
|
1933
|
-
and not s.task_group
|
|
1934
|
-
and not s.task_group_pending_text),
|
|
1935
|
-
build=_build_task_group, submit=_submit_task_group,
|
|
1936
|
-
owns=("task_group", "task_group_pending_text")),
|
|
1937
|
-
Step(S_TASK_GROUP_TEXT,
|
|
1938
|
-
applies=lambda s: (bool(s.is_new_task)
|
|
1939
|
-
and bool(s.brief_path)
|
|
1940
|
-
and not s.task_group
|
|
1941
|
-
and s.task_group_pending_text),
|
|
1942
|
-
build=_build_task_group_text, submit=_submit_task_group_text,
|
|
1943
|
-
owns=("task_group", "task_group_pending_text")),
|
|
1944
2009
|
Step(S_TASK_ID,
|
|
1945
2010
|
applies=lambda s: (bool(s.is_new_task)
|
|
1946
2011
|
and bool(s.brief_path)
|
|
@@ -330,12 +330,13 @@
|
|
|
330
330
|
|
|
331
331
|
"implementationPlanning": {
|
|
332
332
|
"type": "object",
|
|
333
|
-
"description": "RENDER_IF taskType == implementation-planning. §
|
|
333
|
+
"description": "RENDER_IF taskType == implementation-planning. §5.5 deliverables.",
|
|
334
334
|
"required": [
|
|
335
335
|
"optionCandidates",
|
|
336
336
|
"tradeoffMatrix",
|
|
337
337
|
"recommendedOption",
|
|
338
|
-
"
|
|
338
|
+
"stageMap",
|
|
339
|
+
"stages",
|
|
339
340
|
"dependencyMigrationRisk",
|
|
340
341
|
"validationChecklist",
|
|
341
342
|
"rollbackStrategy",
|
|
@@ -355,7 +356,18 @@
|
|
|
355
356
|
"items": { "$ref": "#/$defs/TradeoffRow" }
|
|
356
357
|
},
|
|
357
358
|
"recommendedOption": { "$ref": "#/$defs/RecommendedOption" },
|
|
359
|
+
"stageMap": {
|
|
360
|
+
"type": "array",
|
|
361
|
+
"minItems": 1,
|
|
362
|
+
"items": { "$ref": "#/$defs/StageMapRow" }
|
|
363
|
+
},
|
|
364
|
+
"stages": {
|
|
365
|
+
"type": "array",
|
|
366
|
+
"minItems": 1,
|
|
367
|
+
"items": { "$ref": "#/$defs/ImplementationPlanStage" }
|
|
368
|
+
},
|
|
358
369
|
"stepwiseExecution": {
|
|
370
|
+
"description": "Legacy flat summary kept for compatibility only. New reports use stageMap/stages.",
|
|
359
371
|
"type": "array",
|
|
360
372
|
"minItems": 1,
|
|
361
373
|
"items": { "$ref": "#/$defs/StepRow" }
|
|
@@ -385,7 +397,7 @@
|
|
|
385
397
|
|
|
386
398
|
"releaseHandoff": {
|
|
387
399
|
"type": "object",
|
|
388
|
-
"description": "RENDER_IF taskType == release-handoff. §
|
|
400
|
+
"description": "RENDER_IF taskType == release-handoff. §5.6 deliverables.",
|
|
389
401
|
"required": [
|
|
390
402
|
"sourceVerificationReport",
|
|
391
403
|
"featureBranchState",
|
|
@@ -419,11 +431,14 @@
|
|
|
419
431
|
},
|
|
420
432
|
"userSelections": {
|
|
421
433
|
"type": "object",
|
|
422
|
-
"required": ["h1", "h3"],
|
|
434
|
+
"required": ["h1", "h2b", "h3"],
|
|
423
435
|
"additionalProperties": false,
|
|
424
436
|
"properties": {
|
|
425
437
|
"h1": { "enum": ["local only", "push + PR", "skip"] },
|
|
426
438
|
"h2": { "type": "string" },
|
|
439
|
+
"h2b": {
|
|
440
|
+
"enum": ["not-run", "clean", "proceed anyway", "change base branch", "cancel"]
|
|
441
|
+
},
|
|
427
442
|
"h3": { "enum": ["use as-is", "edit then proceed", "cancel"] }
|
|
428
443
|
}
|
|
429
444
|
},
|
|
@@ -482,12 +497,13 @@
|
|
|
482
497
|
|
|
483
498
|
"implementation": {
|
|
484
499
|
"type": "object",
|
|
485
|
-
"description": "RENDER_IF taskType == implementation. §
|
|
500
|
+
"description": "RENDER_IF taskType == implementation. §5.7 deliverables.",
|
|
486
501
|
"required": [
|
|
487
502
|
"approvedPlanReference",
|
|
488
503
|
"commitList",
|
|
489
504
|
"diffSummary",
|
|
490
505
|
"outOfPlanEdits",
|
|
506
|
+
"stageSidecarEvidence",
|
|
491
507
|
"validationEvidence",
|
|
492
508
|
"verifierResults",
|
|
493
509
|
"rollbackVerification",
|
|
@@ -526,6 +542,7 @@
|
|
|
526
542
|
"type": "array",
|
|
527
543
|
"items": { "$ref": "#/$defs/OutOfPlanEditRow" }
|
|
528
544
|
},
|
|
545
|
+
"stageSidecarEvidence": { "$ref": "#/$defs/StageSidecarEvidence" },
|
|
529
546
|
"validationEvidence": {
|
|
530
547
|
"type": "array",
|
|
531
548
|
"minItems": 1,
|
|
@@ -547,7 +564,7 @@
|
|
|
547
564
|
|
|
548
565
|
"finalVerification": {
|
|
549
566
|
"type": "object",
|
|
550
|
-
"description": "RENDER_IF taskType == final-verification. §
|
|
567
|
+
"description": "RENDER_IF taskType == final-verification. §5.8 deliverables.",
|
|
551
568
|
"required": [
|
|
552
569
|
"sourceImplementationReport",
|
|
553
570
|
"acceptanceBlockers",
|
|
@@ -624,7 +641,7 @@
|
|
|
624
641
|
|
|
625
642
|
"allOf": [
|
|
626
643
|
{
|
|
627
|
-
"description": "implementation-planning task-type requires §
|
|
644
|
+
"description": "implementation-planning task-type requires §5.5 block.",
|
|
628
645
|
"if": {
|
|
629
646
|
"properties": { "header": { "properties": { "taskType": { "const": "implementation-planning" } } } },
|
|
630
647
|
"required": ["header"]
|
|
@@ -634,7 +651,7 @@
|
|
|
634
651
|
}
|
|
635
652
|
},
|
|
636
653
|
{
|
|
637
|
-
"description": "release-handoff task-type requires §
|
|
654
|
+
"description": "release-handoff task-type requires §5.6 block.",
|
|
638
655
|
"if": {
|
|
639
656
|
"properties": { "header": { "properties": { "taskType": { "const": "release-handoff" } } } },
|
|
640
657
|
"required": ["header"]
|
|
@@ -644,7 +661,7 @@
|
|
|
644
661
|
}
|
|
645
662
|
},
|
|
646
663
|
{
|
|
647
|
-
"description": "implementation task-type requires §
|
|
664
|
+
"description": "implementation task-type requires §5.7 block.",
|
|
648
665
|
"if": {
|
|
649
666
|
"properties": { "header": { "properties": { "taskType": { "const": "implementation" } } } },
|
|
650
667
|
"required": ["header"]
|
|
@@ -654,7 +671,7 @@
|
|
|
654
671
|
}
|
|
655
672
|
},
|
|
656
673
|
{
|
|
657
|
-
"description": "final-verification task-type requires §
|
|
674
|
+
"description": "final-verification task-type requires §5.8 block.",
|
|
658
675
|
"if": {
|
|
659
676
|
"properties": { "header": { "properties": { "taskType": { "const": "final-verification" } } } },
|
|
660
677
|
"required": ["header"]
|
|
@@ -1066,6 +1083,90 @@
|
|
|
1066
1083
|
}
|
|
1067
1084
|
},
|
|
1068
1085
|
|
|
1086
|
+
"StageMapRow": {
|
|
1087
|
+
"type": "object",
|
|
1088
|
+
"required": ["stage", "title", "dependsOn", "stepCount", "exitContractSummary"],
|
|
1089
|
+
"additionalProperties": false,
|
|
1090
|
+
"properties": {
|
|
1091
|
+
"stage": { "type": "integer", "minimum": 1 },
|
|
1092
|
+
"title": { "type": "string", "minLength": 1 },
|
|
1093
|
+
"dependsOn": {
|
|
1094
|
+
"type": "string",
|
|
1095
|
+
"minLength": 1,
|
|
1096
|
+
"description": "Literal Stage Map depends-on cell: `(none)` or a comma-separated stage number list."
|
|
1097
|
+
},
|
|
1098
|
+
"stepCount": { "type": "integer", "minimum": 0, "maximum": 6 },
|
|
1099
|
+
"exitContractSummary": { "type": "string", "minLength": 1 }
|
|
1100
|
+
}
|
|
1101
|
+
},
|
|
1102
|
+
|
|
1103
|
+
"ImplementationPlanStage": {
|
|
1104
|
+
"type": "object",
|
|
1105
|
+
"required": [
|
|
1106
|
+
"stage",
|
|
1107
|
+
"title",
|
|
1108
|
+
"sliceValue",
|
|
1109
|
+
"acceptance",
|
|
1110
|
+
"carryIn",
|
|
1111
|
+
"stepwiseExecution",
|
|
1112
|
+
"exitContract",
|
|
1113
|
+
"stageValidation"
|
|
1114
|
+
],
|
|
1115
|
+
"additionalProperties": false,
|
|
1116
|
+
"oneOf": [
|
|
1117
|
+
{
|
|
1118
|
+
"required": ["conformanceTests"],
|
|
1119
|
+
"not": { "required": ["conformanceExemption"] }
|
|
1120
|
+
},
|
|
1121
|
+
{
|
|
1122
|
+
"required": ["conformanceExemption"],
|
|
1123
|
+
"not": { "required": ["conformanceTests"] }
|
|
1124
|
+
}
|
|
1125
|
+
],
|
|
1126
|
+
"properties": {
|
|
1127
|
+
"stage": { "type": "integer", "minimum": 1 },
|
|
1128
|
+
"title": { "type": "string", "minLength": 1 },
|
|
1129
|
+
"sliceValue": { "type": "string", "minLength": 1 },
|
|
1130
|
+
"acceptance": { "type": "string", "minLength": 1 },
|
|
1131
|
+
"conformanceTests": {
|
|
1132
|
+
"type": "string",
|
|
1133
|
+
"minLength": 1,
|
|
1134
|
+
"description": "Text after the `Conformance tests: stage-N — ...` prefix."
|
|
1135
|
+
},
|
|
1136
|
+
"conformanceExemption": {
|
|
1137
|
+
"type": "string",
|
|
1138
|
+
"minLength": 1,
|
|
1139
|
+
"description": "Text after the `Conformance exemption:` prefix."
|
|
1140
|
+
},
|
|
1141
|
+
"tddExemption": {
|
|
1142
|
+
"type": "string",
|
|
1143
|
+
"description": "Optional text after the `TDD exemption:` prefix."
|
|
1144
|
+
},
|
|
1145
|
+
"carryIn": { "type": "string", "minLength": 1 },
|
|
1146
|
+
"stepwiseExecution": {
|
|
1147
|
+
"type": "array",
|
|
1148
|
+
"minItems": 1,
|
|
1149
|
+
"maxItems": 6,
|
|
1150
|
+
"items": { "$ref": "#/$defs/StageStepRow" }
|
|
1151
|
+
},
|
|
1152
|
+
"exitContract": { "type": "string", "minLength": 1 },
|
|
1153
|
+
"stageValidation": { "type": "string", "minLength": 1 }
|
|
1154
|
+
}
|
|
1155
|
+
},
|
|
1156
|
+
|
|
1157
|
+
"StageStepRow": {
|
|
1158
|
+
"type": "object",
|
|
1159
|
+
"required": ["step", "action", "files", "command", "expected"],
|
|
1160
|
+
"additionalProperties": false,
|
|
1161
|
+
"properties": {
|
|
1162
|
+
"step": { "type": "integer", "minimum": 1 },
|
|
1163
|
+
"action": { "type": "string", "minLength": 1 },
|
|
1164
|
+
"files": { "type": "string", "minLength": 1 },
|
|
1165
|
+
"command": { "type": "string", "minLength": 1 },
|
|
1166
|
+
"expected": { "type": "string", "minLength": 1 }
|
|
1167
|
+
}
|
|
1168
|
+
},
|
|
1169
|
+
|
|
1069
1170
|
"StepRow": {
|
|
1070
1171
|
"type": "object",
|
|
1071
1172
|
"required": ["step", "ticketId", "action", "files", "commandOrTest", "expectedOutcome"],
|
|
@@ -1080,6 +1181,22 @@
|
|
|
1080
1181
|
}
|
|
1081
1182
|
},
|
|
1082
1183
|
|
|
1184
|
+
"StageSidecarEvidence": {
|
|
1185
|
+
"type": "object",
|
|
1186
|
+
"required": ["stageNumber", "stageTitle", "carryJson", "consumerRows"],
|
|
1187
|
+
"additionalProperties": false,
|
|
1188
|
+
"properties": {
|
|
1189
|
+
"stageNumber": { "type": "integer", "minimum": 1 },
|
|
1190
|
+
"stageTitle": { "type": "string", "minLength": 1 },
|
|
1191
|
+
"carryJson": { "type": "string", "minLength": 1 },
|
|
1192
|
+
"consumerRows": {
|
|
1193
|
+
"type": "array",
|
|
1194
|
+
"minItems": 1,
|
|
1195
|
+
"items": { "type": "string", "minLength": 1 }
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
},
|
|
1199
|
+
|
|
1083
1200
|
"DependencyRow": {
|
|
1084
1201
|
"type": "object",
|
|
1085
1202
|
"required": ["id", "kind", "item", "impact", "mitigation"],
|
|
@@ -62,6 +62,14 @@ If you suspect an overlay applies but the layout is non-standard, ask one questi
|
|
|
62
62
|
- [ ] Existing code searched: `grep` for the symbol / file / identifier you are about to add. Do not duplicate.
|
|
63
63
|
- [ ] Project conventions checked: `.editorconfig`, `CONTRIBUTING.md`, formatter config (`.prettierrc`, `rustfmt.toml`, `ktlint`, `google-java-format`, etc.). **Project rules override this skill on conflict.**
|
|
64
64
|
|
|
65
|
+
## Completion sweep (before declaring a multi-file change done)
|
|
66
|
+
|
|
67
|
+
Per-file checks miss cross-cutting issues; each commit can be individually clean while the sum violates DRY. Before saying "done":
|
|
68
|
+
|
|
69
|
+
- [ ] **Domain-literal sweep:** `grep -rn` every domain enum value / predicate you added or touched in WHERE clauses, filters, or branches. The same literal at 2+ I/O sites is a *candidate* scattered decision — ask: would these sites change together when the business rule changes? Same decision → consolidate into one named constant or query builder in the domain layer and make every site reference it. Different decisions that merely share a value → leave them separate; coupling incidental duplication is worse than the repetition. (The identifier grep above does NOT catch this — sweep *values*, not just names.)
|
|
70
|
+
- [ ] **Stand-alone name test for exports:** for each exported identifier, look at its siblings — can a caller pick the right one from the names alone? If a comment must explain which to use, the name fails; encode the distinguishing fact in it (e.g., the input shape: `parseRows` vs `parseRowsFromFlatItems`).
|
|
71
|
+
- [ ] **No documented forks:** two deliberate variants of one capability must not survive as parallel implementations with a comment explaining the delta. Re-read both bodies and check the deltas are genuinely parametric: if they reduce to a few orthogonal options, collapse into one implementation taking explicit option parameters that encode them. If encoding the delta would take more than ~3 options, or add more branching than the duplication it removes, they are two capabilities — keep two implementations with distinct honest names and delete the "variant of" framing. Either way the comment-documented fork dies. "The divergence is documented" stays a refused rationalization, and a two-capabilities verdict must come from reading the bodies, not from reluctance to refactor.
|
|
72
|
+
|
|
65
73
|
## Boundaries
|
|
66
74
|
|
|
67
75
|
- This skill does **not** auto-format. Run the project's formatter yourself.
|
|
@@ -22,6 +22,11 @@ const sum = (arr) => arr.reduce((acc, x) => acc + x, 0);
|
|
|
22
22
|
|
|
23
23
|
Caveat: do not extract until the duplication is real (rule of three). Premature abstraction is also a code smell.
|
|
24
24
|
|
|
25
|
+
Two more forms of duplication that hide from symbol-level grep:
|
|
26
|
+
|
|
27
|
+
- **Scattered domain literals** — the same enum value / predicate repeated across queries or filters *may* be one business decision duplicated. The test: would the sites change together when the rule changes? If yes, consolidate to a named constant or builder at a single reference point; if they merely share a value, leave them apart — coupling incidental duplication is worse than repetition.
|
|
28
|
+
- **Documented forks** — two deliberate variants of one capability kept as parallel implementations "because the delta is commented". If the delta is genuinely parametric (a few orthogonal options), collapse into one implementation with explicit option parameters; if not, split into two distinctly named capabilities. The commented fork itself is never the end state.
|
|
29
|
+
|
|
25
30
|
## KISS — Keep It Simple
|
|
26
31
|
|
|
27
32
|
The simplest solution that meets the requirement wins. Cleverness has a maintenance cost.
|
|
@@ -104,6 +109,7 @@ The test: if the name appeared in a stacktrace, an autocomplete list, or a grep
|
|
|
104
109
|
- Generic verbs with no information: `handle`, `process`, `execute`, `doStuff`, `manage`. If the function deletes-then-inserts, name it `replace`, not `update`.
|
|
105
110
|
- Constants that will collide with siblings later: `BUCKET` → `FONTS_BUCKET`, `TIMEOUT` → `HTTP_READ_TIMEOUT_MS`, `URL` → `AUTH_SERVICE_URL`.
|
|
106
111
|
- Repository / port methods named after the rule they enforce, not the data they fetch: `findValid*` / `findActive*` / `findEligible*` — the adjective encodes a business rule the caller can't inspect from the name. Prefer `findDesktopLibrariesForUser(userId)` + a domain predicate applied by the caller.
|
|
112
|
+
- Near-twin siblings a caller can't tell apart from the names alone (`parseRows` vs `parseRowsFromItems`): if a comment must explain which to pick, rename — encode the distinguishing fact (e.g., input shape: `parseRowsFromFlatItems`). Matching the existing sibling's style does not excuse the ambiguity.
|
|
107
113
|
|
|
108
114
|
This rule applies most strongly to **names crossing module boundaries**: public methods, exported constants, port methods. Private helpers in a tight local scope get more slack.
|
|
109
115
|
|
|
@@ -120,7 +120,7 @@ That is the entire interactive flow. The wizard handles:
|
|
|
120
120
|
|
|
121
121
|
- new-vs-existing task split, task-group / task-id slug validation,
|
|
122
122
|
- task-type pick (with `nextRecommendedPhase` surfaced as recommended for existing tasks),
|
|
123
|
-
- brief path (
|
|
123
|
+
- brief path after task-group selection (same-group `.okstra/briefs/<task-group>/**/*.md` candidates first, direct input last; `유지 / 변경` for existing tasks),
|
|
124
124
|
- base-ref pick + git rev-parse validation (skipped when reusing an active worktree),
|
|
125
125
|
- `implementation`-only sub-flow: approved-plan path (frontmatter `approved: true` check) + stage pick (`auto` = 의존성 충족된 가장 빠른 미완료 stage, 또는 특정 stage 번호) + executor pick,
|
|
126
126
|
- `Use defaults / Customize` branch with profile-aware worker/model questions,
|
|
@@ -17,25 +17,25 @@ okstra release-handoff 기본 PR 본문 템플릿.
|
|
|
17
17
|
- `git log --oneline <base>..HEAD` 의 commit 범위
|
|
18
18
|
- `git diff <base>..HEAD --stat` 의 변경 파일 통계
|
|
19
19
|
|
|
20
|
-
빈 섹션은 그대로 두지 말고 통째로 삭제합니다. HTML 주석은 PR 생성 전에
|
|
21
|
-
모두 제거됩니다.
|
|
22
20
|
-->
|
|
23
21
|
|
|
24
|
-
##
|
|
22
|
+
## **Please check if the PR fulfills these requirements**
|
|
23
|
+
- [ ] Commits have a single intent
|
|
24
|
+
- [ ] Tests for the changes have been added (for bug fixes / features)
|
|
25
|
+
- [ ] I reviewed my own code
|
|
26
|
+
- [ ] I tested the changes (if not, explain why in the "Other information" section)
|
|
27
|
+
- [ ] Docs have been added / updated
|
|
25
28
|
|
|
26
|
-
|
|
29
|
+
## **What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...)
|
|
27
30
|
|
|
28
|
-
## Changes
|
|
29
31
|
|
|
30
|
-
|
|
32
|
+
## **What is the current behavior?** (You can also link to an open issue here)
|
|
31
33
|
|
|
32
|
-
## Test plan
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
## **What is the new behavior (if this is a feature change)?**
|
|
35
36
|
|
|
36
|
-
- [ ] <검증 항목>
|
|
37
|
-
- [ ] <검증 항목>
|
|
38
37
|
|
|
39
|
-
##
|
|
38
|
+
## **Does this PR introduce a breaking change?** (What changes might users need to make in their application due to this PR?)
|
|
40
39
|
|
|
41
|
-
|
|
40
|
+
|
|
41
|
+
## **Other information**:
|
|
@@ -140,7 +140,7 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
140
140
|
{% if header.taskType == 'implementation-planning' %}
|
|
141
141
|
## 5.5 Implementation Plan Deliverables
|
|
142
142
|
|
|
143
|
-
###
|
|
143
|
+
### Option Candidates{% if t("sectionAside.optionCandidates") != "Option Candidates" %} ({{ t("sectionAside.optionCandidates") }}){% endif %}
|
|
144
144
|
|
|
145
145
|
{% for opt in implementationPlanning.optionCandidates %}
|
|
146
146
|
**{{ opt.name }}**
|
|
@@ -158,7 +158,7 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
158
158
|
|
|
159
159
|
{% endfor %}
|
|
160
160
|
|
|
161
|
-
###
|
|
161
|
+
### Trade-off Matrix{% if t("sectionAside.tradeOffMatrix") != "Trade-off Matrix" %} ({{ t("sectionAside.tradeOffMatrix") }}){% endif %}
|
|
162
162
|
|
|
163
163
|
| Option | Complexity | Risk | Reversibility | Test Coverage Cost | Rollout Cost |
|
|
164
164
|
|--------|-----------|------|---------------|--------------------|--------------|
|
|
@@ -166,7 +166,7 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
166
166
|
| {{ row.option }} | {{ row.complexity }} | {{ row.risk }} | {{ row.reversibility }} | {{ row.testCoverageCost }} | {{ row.rolloutCost }} |
|
|
167
167
|
{% endfor %}
|
|
168
168
|
|
|
169
|
-
###
|
|
169
|
+
### Recommended Option{% if t("sectionAside.recommendedOption") != "Recommended Option" %} ({{ t("sectionAside.recommendedOption") }}){% endif %}
|
|
170
170
|
|
|
171
171
|
| {{ t("implementationPlanning.recommendedTableHeaderLabel") }} | {{ t("implementationPlanning.recommendedTableHeaderValue") }} |
|
|
172
172
|
|------|----|
|
|
@@ -175,15 +175,7 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
175
175
|
| {{ t("implementationPlanning.rationaleLabel") }} | {{ implementationPlanning.recommendedOption.rationale }} |
|
|
176
176
|
| {{ t("implementationPlanning.rejectedSummaryLabel") }} | {{ implementationPlanning.recommendedOption.rejectedSummary }} |
|
|
177
177
|
|
|
178
|
-
###
|
|
179
|
-
|
|
180
|
-
| Step | Ticket ID | Action (≤ 5min) | Files | Command / Test | Expected outcome |
|
|
181
|
-
|------|-----------|------------------|-------|----------------|-------------------|
|
|
182
|
-
{% for row in implementationPlanning.stepwiseExecution -%}
|
|
183
|
-
| {{ row.step }} | `{{ row.ticketId }}` | {{ row.action }} | `{{ row.files }}` | `{{ row.commandOrTest }}` | {{ row.expectedOutcome }} |
|
|
184
|
-
{% endfor %}
|
|
185
|
-
|
|
186
|
-
### 5.5.5 Dependency / Migration Risk{% if t("sectionAside.dependencyRisk") != "Dependency / Migration Risk" %} ({{ t("sectionAside.dependencyRisk") }}){% endif %}
|
|
178
|
+
### Dependency / Migration Risk{% if t("sectionAside.dependencyRisk") != "Dependency / Migration Risk" %} ({{ t("sectionAside.dependencyRisk") }}){% endif %}
|
|
187
179
|
|
|
188
180
|
{% if implementationPlanning.dependencyMigrationRisk | length == 0 -%}
|
|
189
181
|
{{ t("emptyState.dependencyRisk") }}
|
|
@@ -195,7 +187,7 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
195
187
|
{% endfor %}
|
|
196
188
|
{%- endif %}
|
|
197
189
|
|
|
198
|
-
###
|
|
190
|
+
### Validation Checklist{% if t("sectionAside.validationChecklist") != "Validation Checklist" %} ({{ t("sectionAside.validationChecklist") }}){% endif %}
|
|
199
191
|
|
|
200
192
|
| Phase | Ticket ID | Check | Command / Observation | Expected outcome |
|
|
201
193
|
|-------|-----------|-------|------------------------|-------------------|
|
|
@@ -203,7 +195,7 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
203
195
|
| {{ row.phase }} | `{{ row.ticketId }}` | {{ row.check }} | `{{ row.commandOrObservation }}` | {{ row.expectedOutcome }} |
|
|
204
196
|
{% endfor %}
|
|
205
197
|
|
|
206
|
-
###
|
|
198
|
+
### Rollback Strategy{% if t("sectionAside.rollbackStrategy") != "Rollback Strategy" %} ({{ t("sectionAside.rollbackStrategy") }}){% endif %}
|
|
207
199
|
|
|
208
200
|
| ID | Step | Action | Trigger signal | {{ t("columns.checkMethod") }} |
|
|
209
201
|
|----|------|--------|----------------|-----------|
|
|
@@ -211,7 +203,7 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
211
203
|
| {{ row.id }} | {{ row.step }} | `{{ row.action }}` | {{ row.triggerSignal }} | `{{ row.verificationMethod }}` |
|
|
212
204
|
{% endfor %}
|
|
213
205
|
|
|
214
|
-
###
|
|
206
|
+
### Requirement Coverage
|
|
215
207
|
|
|
216
208
|
| ID | Source | Requirement | Covered by option / stage / step | Status |
|
|
217
209
|
|----|--------|-------------|-----------------------------------|--------|
|
|
@@ -219,6 +211,45 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
219
211
|
| {{ row.id }} | `{{ row.source }}` | {{ row.requirement }} | {{ row.coveredBy }} | `{{ row.status }}` |
|
|
220
212
|
{% endfor %}
|
|
221
213
|
|
|
214
|
+
## 5.5 Stage Map
|
|
215
|
+
|
|
216
|
+
| stage | title | depends-on | step-count | exit-contract-summary |
|
|
217
|
+
|-------|-------|------------|------------|-----------------------|
|
|
218
|
+
{% for row in implementationPlanning.stageMap -%}
|
|
219
|
+
| {{ row.stage }} | {{ row.title }} | {{ row.dependsOn }} | {{ row.stepCount }} | {{ row.exitContractSummary }} |
|
|
220
|
+
{% endfor %}
|
|
221
|
+
|
|
222
|
+
{% for stage in implementationPlanning.stages %}
|
|
223
|
+
## 5.5.{{ stage.stage }} Stage {{ stage.stage }}: {{ stage.title }}
|
|
224
|
+
|
|
225
|
+
Slice value: {{ stage.sliceValue }}
|
|
226
|
+
Acceptance: {{ stage.acceptance }}
|
|
227
|
+
{% if stage.conformanceTests %}Conformance tests: stage-{{ stage.stage }} — {{ stage.conformanceTests }}
|
|
228
|
+
{% else %}Conformance exemption: {{ stage.conformanceExemption }}
|
|
229
|
+
{% endif %}{% if stage.tddExemption %}TDD exemption: {{ stage.tddExemption }}
|
|
230
|
+
{% endif %}
|
|
231
|
+
### Carry-In
|
|
232
|
+
|
|
233
|
+
{{ stage.carryIn }}
|
|
234
|
+
|
|
235
|
+
### Stepwise Execution Order
|
|
236
|
+
|
|
237
|
+
| step | action | files | command | expected |
|
|
238
|
+
|------|--------|-------|---------|----------|
|
|
239
|
+
{% for row in stage.stepwiseExecution -%}
|
|
240
|
+
| {{ row.step }} | {{ row.action }} | `{{ row.files }}` | `{{ row.command }}` | {{ row.expected }} |
|
|
241
|
+
{% endfor %}
|
|
242
|
+
|
|
243
|
+
### Stage Exit Contract
|
|
244
|
+
|
|
245
|
+
{{ stage.exitContract }}
|
|
246
|
+
|
|
247
|
+
### Stage Validation
|
|
248
|
+
|
|
249
|
+
{{ stage.stageValidation }}
|
|
250
|
+
|
|
251
|
+
{% endfor %}
|
|
252
|
+
|
|
222
253
|
### 5.5.9 Plan Body Verification{% if t("sectionAside.planBodyVerification") != "Plan Body Verification" %} ({{ t("sectionAside.planBodyVerification") }}){% endif %}
|
|
223
254
|
|
|
224
255
|
{{ t("sectionIntro.planBodyVerification") }}
|
|
@@ -271,6 +302,7 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
271
302
|
|---------|-----------|--------------------|--------------------|
|
|
272
303
|
| H1 | {{ t("releaseHandoff.h1Body") }} | `{{ releaseHandoff.userSelections.h1 }}` | `local only` / `push + PR` / `skip` |
|
|
273
304
|
| H2 | {{ t("releaseHandoff.h2Body") }} | `{{ releaseHandoff.userSelections.h2 or t("releaseHandoff.h2DefaultLabel") }}` | {{ t("releaseHandoff.h2OptionsLabel") }} |
|
|
305
|
+
| H2b | Merge conflict probe | `{{ releaseHandoff.userSelections.h2b or releaseHandoff.mergeConflictProbe.kind }}` | `not-run` / `clean` / `proceed anyway` / `change base branch` / `cancel` |
|
|
274
306
|
| H3 | {{ t("releaseHandoff.h3Body") }} | `{{ releaseHandoff.userSelections.h3 }}` | `use as-is` / `edit then proceed` / `cancel` |
|
|
275
307
|
|
|
276
308
|
### 5.6.4 Executed Commands
|
|
@@ -371,7 +403,19 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
371
403
|
{% endfor %}
|
|
372
404
|
{%- endif %}
|
|
373
405
|
|
|
374
|
-
### 5.7.5
|
|
406
|
+
### 5.7.5 Stage Sidecar Evidence
|
|
407
|
+
|
|
408
|
+
- Stage: `{{ implementation.stageSidecarEvidence.stageNumber }}` — {{ implementation.stageSidecarEvidence.stageTitle }}
|
|
409
|
+
- Carry sidecar JSON:
|
|
410
|
+
```json
|
|
411
|
+
{{ implementation.stageSidecarEvidence.carryJson }}
|
|
412
|
+
```
|
|
413
|
+
- Appended `consumers.jsonl` rows:
|
|
414
|
+
{% for row in implementation.stageSidecarEvidence.consumerRows -%}
|
|
415
|
+
> {{ row }}
|
|
416
|
+
{% endfor %}
|
|
417
|
+
|
|
418
|
+
### 5.7.6 Validation Evidence
|
|
375
419
|
|
|
376
420
|
| Phase | Command | Exit code | Output tail | TDD evidence |
|
|
377
421
|
|-------|---------|-----------|-------------|--------------|
|
|
@@ -379,7 +423,7 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
379
423
|
| {{ row.phase }} | `{{ row.command }}` | `{{ row.exitCode }}` | {{ row.outputTail }} | {{ row.tddEvidence or '--' }} |
|
|
380
424
|
{% endfor %}
|
|
381
425
|
|
|
382
|
-
### 5.7.
|
|
426
|
+
### 5.7.7 Verifier Results
|
|
383
427
|
|
|
384
428
|
{% for block in implementation.verifierResults %}
|
|
385
429
|
- **{{ block.verifier }}** — Verdict: `{{ block.verdict }}`
|
|
@@ -390,7 +434,7 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
390
434
|
- Discrepancy: {{ block.discrepancy or t("emptyState.discrepancy") }}
|
|
391
435
|
{% endfor %}
|
|
392
436
|
|
|
393
|
-
### 5.7.
|
|
437
|
+
### 5.7.8 Rollback Verification
|
|
394
438
|
|
|
395
439
|
| Category | Rollback command | Verification | Result |
|
|
396
440
|
|----------|-------------------|---------------|--------|
|
|
@@ -398,7 +442,7 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
398
442
|
| {{ row.category }} | `{{ row.rollbackCommand }}` | {{ row.verification }} | `{{ row.result }}` |
|
|
399
443
|
{% endfor %}
|
|
400
444
|
|
|
401
|
-
### 5.7.
|
|
445
|
+
### 5.7.9 Routing Recommendation
|
|
402
446
|
|
|
403
447
|
{{ implementation.routingRecommendation }}
|
|
404
448
|
|
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
"h2Body": "PR base branch (when H1 = `push + PR`)",
|
|
106
106
|
"h3Body": "How should the PR title/body draft be handled?",
|
|
107
107
|
"h2DefaultLabel": "(n/a)",
|
|
108
|
-
"h2OptionsLabel": "staging / preprod /
|
|
108
|
+
"h2OptionsLabel": "staging / preprod / main / user input",
|
|
109
109
|
"noMutationNote": "(no mutating command — H1=`skip` or H3=`cancel`)",
|
|
110
110
|
"commandsTableHeader": {
|
|
111
111
|
"outputSummary": "stdout/stderr summary"
|
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
"h2Body": "PR base 브랜치 (H1=`push + PR` 인 경우)",
|
|
106
106
|
"h3Body": "PR title/body 초안 처리",
|
|
107
107
|
"h2DefaultLabel": "(n/a)",
|
|
108
|
-
"h2OptionsLabel": "staging / preprod /
|
|
108
|
+
"h2OptionsLabel": "staging / preprod / main / 사용자 입력",
|
|
109
109
|
"noMutationNote": "(mutating 명령 미실행 — H1=`skip` 또는 H3=`cancel`)",
|
|
110
110
|
"commandsTableHeader": {
|
|
111
111
|
"outputSummary": "stdout/stderr 요약"
|
|
@@ -27,7 +27,7 @@ taskType: "{{FM_TASK_TYPE}}"
|
|
|
27
27
|
## Approved Plan Reference (mandatory)
|
|
28
28
|
|
|
29
29
|
- Approved plan path: `runs/implementation-planning/<run-id>/reports/final-report.md`
|
|
30
|
-
- Approval evidence (quoted exactly from the plan's
|
|
30
|
+
- Approval evidence (quoted exactly from the plan's YAML frontmatter, e.g. `approved: true`):
|
|
31
31
|
- Recommended option name selected from the plan:
|
|
32
32
|
- Plan's bite-sized step list (paste or reference by anchor):
|
|
33
33
|
|
|
@@ -89,12 +89,12 @@ The final report of an `implementation-planning` run MUST contain every section
|
|
|
89
89
|
1. **Option Candidates** — at least two viable options, each with the exact list of files to create or modify and the principal change per file.
|
|
90
90
|
2. **Trade-off Matrix** — comparison of the candidates across complexity, risk, reversibility, performance impact, scope, and required test surface.
|
|
91
91
|
3. **Recommended Option** — selected option with explicit rationale referencing the trade-off matrix.
|
|
92
|
-
4. **
|
|
92
|
+
4. **Stage Map** — `## 5.5 Stage Map` plus one `## 5.5.<i> Stage <i>:` section per stage. Each stage is a thin vertical slice with `Slice value:`, `Acceptance:`, `Conformance tests:` or `Conformance exemption:`, the four required subsections, and a RED/GREEN step order unless a `TDD exemption:` line applies.
|
|
93
93
|
5. **Dependency and Migration Risk** — schema, data, ordering, feature-flag, and cross-service risks that the recommended option introduces.
|
|
94
94
|
6. **Validation Checklist** — pre-execution, mid-execution, and post-execution checks (commands, expected outputs, observability points).
|
|
95
95
|
7. **Rollback Strategy** — exact reverse procedure or compensating action for each significant step.
|
|
96
96
|
8. **Scope Boundary** — an explicit list of adjacent areas, files, refactors, or quality improvements that this plan **does NOT** cover, each with a one-line reason (deferred, separate owner, separate decision, out of requirement). Any item the analysers were tempted to fold in but chose to exclude MUST appear here. An empty list is allowed only when the analysers explicitly state "no adjacent expansion was considered" — silence is not acceptable.
|
|
97
|
-
9. **
|
|
97
|
+
9. **Approval frontmatter** — the final report's YAML frontmatter MUST emit `approved: false` and `implementation-option:`. Do NOT create a `User Approval Request` body block; the next `implementation` run reads only the frontmatter gate.
|
|
98
98
|
|
|
99
99
|
## Phase Boundary
|
|
100
100
|
|
|
@@ -108,7 +108,7 @@ The final report of an `implementation-planning` run MUST contain every section
|
|
|
108
108
|
|
|
109
109
|
## Stage Output Shape (reference)
|
|
110
110
|
|
|
111
|
-
This run's final report MUST emit `## 5.5 Stage Map` and `## 5.5.<i> Stage <i>` sections per the implementation-planning profile §"Required deliverable shape". Two illustrative
|
|
111
|
+
This run's final report MUST emit `## 5.5 Stage Map` and `## 5.5.<i> Stage <i>` sections per the implementation-planning profile §"Required deliverable shape". Two illustrative Stage Map tables:
|
|
112
112
|
|
|
113
113
|
### Shape A — single stage (small work)
|
|
114
114
|
| stage | title | depends-on | step-count | exit-contract-summary |
|
|
@@ -991,6 +991,7 @@ PLANNING_REQUIRED_SECTIONS = (
|
|
|
991
991
|
"Option Candidates",
|
|
992
992
|
"Trade-off",
|
|
993
993
|
"Recommended Option",
|
|
994
|
+
"Stage Map",
|
|
994
995
|
"Stepwise Execution Order",
|
|
995
996
|
"Dependency",
|
|
996
997
|
"Validation Checklist",
|
|
@@ -1005,6 +1006,7 @@ IMPLEMENTATION_REQUIRED_SECTIONS = (
|
|
|
1005
1006
|
"Commit List",
|
|
1006
1007
|
"Diff Summary",
|
|
1007
1008
|
"Out-of-plan Edits",
|
|
1009
|
+
"Stage Sidecar Evidence",
|
|
1008
1010
|
"Validation Evidence",
|
|
1009
1011
|
"Verifier Results",
|
|
1010
1012
|
"Rollback Verification",
|