okstra 0.24.0 → 0.25.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okstra",
3
- "version": "0.24.0",
3
+ "version": "0.25.0",
4
4
  "description": "Multi-agent cross-verification orchestrator runtime + Claude Code skills.",
5
5
  "license": "MIT",
6
6
  "author": "devonshin",
@@ -1,5 +1,5 @@
1
1
  {
2
- "package": "0.24.0",
3
- "builtAt": "2026-05-15T05:35:39.299Z",
2
+ "package": "0.25.0",
3
+ "builtAt": "2026-05-15T07:26:25.848Z",
4
4
  "repoRoot": "/home/runner/work/okstra/okstra"
5
5
  }
@@ -57,7 +57,7 @@ Unlike the Codex / Gemini workers, you are an in-process Claude subagent — you
57
57
  Before producing any output, you MUST read every input file enumerated in the `[Required reading]` block of the lead's prompt from the very first character to the very last character. This includes the task brief, analysis profile, analysis material (if present), reference expectations, the carry-in clarification response (if present), and the final report template.
58
58
 
59
59
  - Use a single `Read` call per file with no `offset` and no `limit`. If a file is genuinely too large for one read, page through it with explicit `offset` / `limit` calls that together cover the entire file, and record the page boundaries in your Findings.
60
- - For the carry-in clarification response, walk every row of sub-section 5.1 (`A1`, `A2`, ... material requests) and sub-section 5.2 (`Q1`, `Q2`, ... user questions) in full, even when the answer column is blank. Skimming these rows is the most common failure mode here; do not repeat it just because the file you will eventually contribute to has a structurally similar Section 5.
60
+ - For the carry-in clarification response, walk every row of `## 5. Clarification Items` (`C-001`, `C-002`, ...) in full, including rows whose `User input` cell is blank a blank `User input` with `Status=open` is itself a signal you must surface, not skip. Skimming these rows is the most common failure mode here; the fact that the file you will eventually contribute to has a structurally similar section 5 is NOT a license to skim. If the prior report uses the deprecated `4.5.9 Open Questions` / `5.1` / `5.2` layout with `OQ-*` / `A*` / `Q*` IDs, walk all three blocks the same way (legacy carry-in transitional rule).
61
61
  - Before listing any Findings, state one sentence per input file confirming you read it end-to-end (e.g. "Read task-brief.md end-to-end (147 lines)."). If you cannot truthfully say this for a file, record a `tool-failure` in the errors sidecar instead of fabricating Findings.
62
62
  - Do not skip a file because its name suggests its content is already familiar from a prior run. Each file is canonical for the current run only.
63
63
 
@@ -125,7 +125,7 @@ This wrapper does NOT invoke MCP tools directly. MCP availability inside the Cod
125
125
  Before producing any output, you MUST ensure the underlying Codex CLI run reads every input file enumerated in the `[Required reading]` block of the lead's prompt from the very first character to the very last character. This includes the task brief, analysis profile, analysis material (if present), reference expectations, the carry-in clarification response (if present), and the final report template.
126
126
 
127
127
  - The lead's prompt body, which you persist verbatim and feed into Codex via stdin, already contains the explicit list of files and the end-to-end reading rule. Do not strip or summarize that block before passing it to the CLI.
128
- - For the carry-in clarification response, the CLI must walk every row of sub-section 5.1 (`A1`, `A2`, ... material requests) and sub-section 5.2 (`Q1`, `Q2`, ... user questions) in full, even when the answer column is blank. The fact that the prior run's final report and the upcoming output share Section 5 structure is NOT a license to skim.
128
+ - For the carry-in clarification response, the CLI must walk every row of `## 5. Clarification Items` (`C-001`, `C-002`, ...) in full, including rows whose `User input` cell is blank a blank `User input` with `Status=open` is itself a signal you must surface. The fact that the prior run's final report and the upcoming output share section 5 structure is NOT a license to skim. If the prior report uses the deprecated `4.5.9 Open Questions` / `5.1` / `5.2` layout with `OQ-*` / `A*` / `Q*` IDs, walk all three blocks the same way (legacy carry-in transitional rule).
129
129
  - The Codex output you return MUST begin with one sentence per input file confirming end-to-end reading (e.g. "Read task-brief.md end-to-end (147 lines)."). If any file was skipped, record a `tool-failure` in the errors sidecar instead of fabricating Findings.
130
130
 
131
131
  ## Worker Output Structure
@@ -125,7 +125,7 @@ This wrapper does NOT invoke MCP tools directly. MCP availability inside the Gem
125
125
  Before producing any output, you MUST ensure the underlying Gemini CLI run reads every input file enumerated in the `[Required reading]` block of the lead's prompt from the very first character to the very last character. This includes the task brief, analysis profile, analysis material (if present), reference expectations, the carry-in clarification response (if present), and the final report template.
126
126
 
127
127
  - The lead's prompt body, which you persist verbatim and feed into Gemini via stdin, already contains the explicit list of files and the end-to-end reading rule. Do not strip or summarize that block before passing it to the CLI.
128
- - For the carry-in clarification response, the CLI must walk every row of sub-section 5.1 (`A1`, `A2`, ... material requests) and sub-section 5.2 (`Q1`, `Q2`, ... user questions) in full, even when the answer column is blank. The structural similarity between the prior final report and the upcoming output is the most common reason this step gets skipped — do not repeat that.
128
+ - For the carry-in clarification response, the CLI must walk every row of `## 5. Clarification Items` (`C-001`, `C-002`, ...) in full, including rows whose `User input` cell is blank a blank `User input` with `Status=open` is itself a signal you must surface. The structural similarity between the prior final report and the upcoming output is the most common reason this step gets skipped — do not repeat that. If the prior report uses the deprecated `4.5.9 Open Questions` / `5.1` / `5.2` layout with `OQ-*` / `A*` / `Q*` IDs, walk all three blocks the same way (legacy carry-in transitional rule).
129
129
  - The Gemini output you return MUST begin with one sentence per input file confirming end-to-end reading (e.g. "Read task-brief.md end-to-end (147 lines)."). If any file was skipped, record a `tool-failure` in the errors sidecar instead of fabricating Findings.
130
130
 
131
131
  ## Worker Output Structure
@@ -46,7 +46,7 @@ If you find yourself thinking "I'll just return the report inline and let lead s
46
46
  Before writing the final report, you MUST read every input file enumerated in the `[Required reading]` block of the lead's prompt from the very first character to the very last character. This always includes `final-report-template.md` and every analysis worker's result file under `worker-results/`, plus the convergence output under `state/convergence-<task-type>-<seq>.json` (if present).
47
47
 
48
48
  - Use a single `Read` call per file with no `offset` and no `limit`. If a file is too large for one read, page through it with explicit `offset` / `limit` calls covering the full file.
49
- - For the carry-in `clarification-response.md` (if present), walk every row of sub-section 5.1 (`A1`, `A2`, ...) and 5.2 (`Q1`, `Q2`, ...) including blank-answer rows. The fact that the file you write has a structurally similar Section 5/6 is NOT an excuse to skim.
49
+ - For the carry-in `clarification-response.md` (if present), walk every row of `## 5. Clarification Items` (`C-001`, `C-002`, ...) including rows whose `User input` cell is blank — a blank cell with `Status=open` is itself a signal you must surface in section 0. The fact that the file you write has a structurally similar section 5 is NOT an excuse to skim. If the prior report uses the deprecated `4.5.9 Open Questions` / `5.1` / `5.2` layout with `OQ-*` / `A*` / `Q*` IDs, walk all three blocks the same way (legacy carry-in transitional rule).
50
50
  - Open every analysis-worker result file under `worker-results/` end-to-end. Do not summarize them from convergence output alone — convergence captures classifications, not full evidence.
51
51
  - Before writing, state one sentence per input file confirming end-to-end reading. If you cannot truthfully say this for a file, record a `tool-failure` in the errors sidecar instead of fabricating the report.
52
52
  - When the convergence-state file is present, read it fully and reproduce the `roundHistory[]` array, `round2SkippedReason`, and `finalClassificationCounts` in the final report's Section 1 Round History sub-table. Do not derive these values from worker results alone — they live in `state/convergence-<task-type>-<seq>.json`.
@@ -85,4 +85,4 @@ Invoke the `okstra` skill now. Read the manifests below for all task metadata, p
85
85
 
86
86
  - Source path: `{{CLARIFICATION_RESPONSE_RELATIVE_PATH}}`
87
87
  - If the source path above is empty, no prior clarification response was attached to this run.
88
- - If the source path is set, a copy is staged at `{{INSTRUCTION_SET_RELATIVE_PATH}}/clarification-response.md`. Read it before running workers; reconcile each `Q*` row in section 5 of the prior report against new evidence and record the outcome in section 0 of this run's final report.
88
+ - If the source path is set, a copy is staged at `{{INSTRUCTION_SET_RELATIVE_PATH}}/clarification-response.md`. Read it before running workers; reconcile each `C-*` row in section 5 (`## 5. Clarification Items`) of the prior report against new evidence and record the outcome in section 0 of this run's final report. If the prior report uses the deprecated `4.5.9 Open Questions` / `5.1` / `5.2` layout with `OQ-*` / `A*` / `Q*` IDs, follow the legacy-carry-in mapping rule in `final-report-template.md` section 0.
@@ -15,7 +15,7 @@ profile document.
15
15
  - the read-only MCP servers declared in the task brief's `## Available MCP Servers` section may be queried as a read-only cross-check; that section is the canonical source of which servers and tools exist for this run, and any MCP-derived finding MUST cite server, table, and the SELECT used. MCP MUST NEVER be used as a write path — schema/data mutations go through repository migration files reviewed by humans.
16
16
  - Authority & permissions assumption (HARD RULE — applies to every okstra task-type):
17
17
  - **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.
18
- - 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, `Open Questions`, or any day/effort estimate. They are not legitimate sources of schedule extension.
18
+ - 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 `## 5. Clarification Items` table, or any day/effort estimate. They are not legitimate sources of schedule extension.
19
19
  - Internal okstra phase handoffs (e.g. the `User Approval Request` block in `implementation-planning`) are unaffected — those are the user themselves approving and proceed without external coordination.
20
20
  - 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.
21
21
  - Anti-escalation rule (shared):
@@ -32,8 +32,11 @@ profile document.
32
32
  - On `아니오` / `n` / `keep` → leave the panes intact; remind the user that they will be cleaned up automatically when Claude `/exit` fires the `SessionEnd` hook.
33
33
  - The question MUST be a clean yes/no — do NOT offer "close some / keep some" partial answers, do NOT propose alternatives like "close only codex panes". The whole-set decision keeps the wrap-up predictable.
34
34
  - This step is mandatory for every phase (`requirements-discovery`, `error-analysis`, `implementation-planning`, `implementation`, `final-verification`, `release-handoff`). It is silent-skipped when `$TMUX_PANE` is unset (lead running outside tmux); the lead MUST NOT fabricate a synthetic pane list in that case.
35
- - Clarification request policy (shared — applies whenever a profile uses `## 5. Clarification Requests for the Next Run`):
36
- - section 5 MUST be split into two distinct sub-sections per `final-report-template.md` `5.1 추가 자료 요청 (Additional Materials Requested)` for files/logs/screenshots/links the user must attach, and `5.2 사용자 확인 질문 (Questions for the User)` for decisions or facts only the user can confirm. Never mix material requests and decision questions in the same row or list.
37
- - write every entry in full, descriptive sentences that a non-developer can act on without further context. Avoid abbreviations and internal jargon. For each material request, state *why* it is needed, *where* the user can find it, and *where* to place it. For each question, state *why* the answer changes the next step, *what* is being asked in a complete sentence, and *what shape of answer* is expected (예/아니오, 보기 하나, 숫자/날짜, 짧은 서술 등); supply concrete option choices when applicable.
35
+ - Clarification request policy (shared — applies whenever a profile uses `## 5. Clarification Items`):
36
+ - section 5 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, do not create a parallel table elsewhere in the report, and do not duplicate the same item into `## 4.5.8 User Approval Request` or any other section.
37
+ - 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`.
38
+ - each row's `Blocks` column picks one of `{approval, next-phase, none}`. `approval` is reserved for items that gate the `implementation-planning` User Approval Request — never use `approval` outside that task-type. `next-phase` blocks the next run from starting cleanly. `none` is informational/audit-only.
39
+ - 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 shape of the answer (예/아니오, 보기 중 하나, 숫자/날짜, 파일 경로, 짧은 서술 등); supply concrete option choices when applicable.
38
40
  - 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.
39
- - if a clarification response was carried in for this run, reconcile each prior `A*` (material) and `Q*` (question) row in section 0 and update its `Status` (`resolved`, `obsolete`) before issuing the next decision/verdict.
41
+ - if a clarification response was carried in for this run, walk every `C-*` row of the prior report's `## 5. Clarification Items` table in section 0 of this report, reconcile each one against new evidence, and update its `Status` to `resolved` or `obsolete` before issuing the next decision/verdict.
42
+ - transitional rule for legacy carry-in (one release cycle): if the prior report uses the deprecated `4.5.9 Open Questions` / `5.1 Additional Materials` / `5.2 Questions for the User` layout, follow the mapping described in section 0 of `final-report-template.md` — collapse `OQ-*` / `A*` / `Q*` into `C-*` rows in this run's new section 5 (legacy ID preserved in the Statement for traceability).
@@ -20,7 +20,7 @@
20
20
  - uncertainty boundaries
21
21
  - practical next diagnostic steps
22
22
  - Clarification request policy (phase-specific addenda — shared policy is in `_common-contract.md`):
23
- - if any blocking uncertainty remains at the time of writing the final report, populate `## 5. Clarification Requests for the Next Run` in `final-report-template.md`
23
+ - if any blocking uncertainty remains at the time of writing the final report, populate `## 5. Clarification Items` in `final-report-template.md` (a single unified table; `Blocks=next-phase` for items the next run cannot start without)
24
24
  - prefer plain Korean over abbreviations (e.g. write "초당 평균 요청 수" instead of "QPS", "재현 절차" instead of "repro")
25
25
  - Non-goals:
26
26
  - implementation details unless they are necessary to validate the cause
@@ -34,7 +34,7 @@
34
34
  - **Two-tier command lookup (shared with `implementation`):** when this phase performs its own independent re-validation, the command source is exactly the same two tiers `implementation` verifiers use — Tier 1 is the originating task brief / approved plan's `validation` set, Tier 2 is `<PROJECT_ROOT>/.project-docs/okstra/project.json` under `qaCommands`. Auto-detecting tools from manifest files is forbidden; missing tiers are recorded as `qa-command not configured: <category>` and do NOT trigger a guess. The `cmd` deny-list (`--fix`, `--write`, ` -w`, ` -u`, `--snapshot-update`, `INSTA_UPDATE=<not-no>`, `cargo update`, `npm install` without `ci`, etc.) is enforced identically. NOTE: runtime fail-fast validation (`okstra_ctl.qa_commands.validate_qa_commands`) only fires at `--task-type implementation` run-prep, so this phase MUST self-check each `qaCommands` entry against the deny-list before executing it — if a denied token is present, skip the command and record it as a `Read-only command log` line `qa-command rejected (denied token: <token>): <label>`.
35
35
  - **Routing recommendation**: brief note on the next safe phase (`done`, `error-analysis`, `implementation-planning`) tied to the verdict and blocker list.
36
36
  - Clarification request policy (phase-specific addendum — shared policy is in `_common-contract.md`):
37
- - populate section 5 only when a blocker hinges on information only the user can supply (deployment intent, intended target environment, business-rule interpretation)
37
+ - populate `## 5. 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
38
38
  - Self-review pass before finalising the report (`Claude lead` runs this; do not delegate to a generic subagent):
39
39
  1. **Verdict precision** — section 2 includes `Verdict Token` with one of the three allowed verdict tokens; `conditional-accept` lists every condition as an actionable item.
40
40
  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.
@@ -12,7 +12,7 @@
12
12
  - read the task brief, related-task briefs, and any cited spec / design doc end-to-end
13
13
  - inspect the current state of every file the task names (or the closest matching files if names are stale) — record current responsibilities, public interfaces, and known coupling points
14
14
  - skim recent commits touching those files (`git log -- <path>`) to surface in-flight work or contested areas
15
- - flag any requirement that is ambiguous, contradictory, or missing success criteria — list these in the report's `Open Questions` block instead of guessing
15
+ - flag any requirement that is ambiguous, contradictory, or missing success criteria — register each one as a row in the report's `## 5. Clarification Items` table with `Blocks=approval` instead of guessing
16
16
  - Primary focus areas:
17
17
  - requirement gaps
18
18
  - affected components and boundaries
@@ -57,7 +57,7 @@
57
57
  - validation checklist (pre / mid / post) — each item is an exact command or observable outcome
58
58
  - rollback strategy — exact revert path (commits, flags, migrations) and the signal that triggers rollback
59
59
  - explicit `User Approval Request (사용자 승인 게이트)` block placed at the **top of the report** with a single canonical checkbox marker `- [ ] Approved` (user toggles to `- [x] Approved` to authorise the next `implementation` run). Section `4.5.8` is retained only as a back-pointer to this top block for validator/key-substring compatibility — it must NOT carry an independent marker.
60
- - `Open Questions` block listing every ambiguity flagged during pre-planning that the user must resolve before approval
60
+ - every ambiguity flagged during pre-planning that the user must resolve before approval registered as a `Blocks=approval` row in the `## 5. Clarification Items` table (do NOT create a separate `Open Questions` block under `4.5.x` — the unified table is the single home)
61
61
  - No-placeholder rule (plan failures — reject any option or step that contains these):
62
62
  - "TBD", "TODO", "implement later", "fill in details", "add appropriate error handling", "handle edge cases", "write tests for the above" without actual test code
63
63
  - "similar to Option/Task N" without repeating the concrete content (readers may consume sections out of order)
@@ -67,5 +67,5 @@
67
67
  1. **Spec coverage** — for every requirement in the task brief, point to the option(s) and step(s) that satisfy it. List gaps explicitly.
68
68
  2. **Placeholder scan** — search the report for the patterns in the No-placeholder rule above and fix inline.
69
69
  3. **Internal consistency** — option file lists, trade-off matrix, and recommended step list must agree on file paths, names, and signatures. A symbol called `clearLayers()` in the matrix and `clearFullLayers()` in the steps is a bug.
70
- 4. **Ambiguity check** — any requirement that could be read two ways must be made explicit or moved to `Open Questions`.
70
+ 4. **Ambiguity check** — any requirement that could be read two ways must be made explicit or moved to the `## 5. Clarification Items` table as a `Blocks=approval` row.
71
71
  5. **Scope check** — if the recommended plan now spans multiple independent subsystems, recommend splitting into separate planning runs rather than shipping an oversized plan.
@@ -128,7 +128,7 @@
128
128
  - **Feature-flag-gated changes**: confirm the off-switch path was exercised in this run's validation evidence (i.e. one of the validation commands ran with the flag off and succeeded). A plan that ships a flag without exercising the off-path does NOT satisfy this requirement.
129
129
  - **Schema migrations, config-format changes, or any change with persisted state**: a **dry-run of the rollback step is mandatory**, not preferred. Record the exact rollback command and its captured exit code / stdout. If the migration tool offers no dry-run mode (`--dry-run`, `--plan`, equivalent), the executor MUST refuse to claim rollback verification and instead end the run with a routing recommendation back to `implementation-planning` for a safer rollback strategy. Skipping this step on a stateful change is treated as a `contract-violated` outcome by `final-verification`.
130
130
  - **Routing recommendation for `final-verification`**: brief note on whether the changes are ready for final-verification phase or need a new error-analysis / planning loop first.
131
- - **Follow-up tasks (Section 7 of the final report)**: every item discovered during this run that was *not* delivered MUST appear in the final report's `## 7. Follow-up Tasks (후속 작업)` table with a concrete `Origin`, `New Task ID`, `Suggested task-type`, `Scope`, and `Reason / Why deferred`. Sources include: out-of-scope discoveries that the executor consciously chose not to fold into this run, verifier concerns the executor declined to fix in-place, scope-boundary items from the approved plan that turned out to need their own ticket, and any open question carried over from `4.5.9`. An empty section is acceptable but only when expressed as the single line `- 후속 작업 없음.` — silence is treated as a contract violation. Rows with `Auto-spawn? = yes` will be materialised by `scripts/okstra-spawn-followups.py` in Phase 7; rows with `Auto-spawn? = no` MUST also appear in `Section 6. Recommended Next Steps` so the user knows to act manually.
131
+ - **Follow-up tasks (Section 7 of the final report)**: every item discovered during this run that was *not* delivered MUST appear in the final report's `## 7. Follow-up Tasks (후속 작업)` table with a concrete `Origin`, `New Task ID`, `Suggested task-type`, `Scope`, and `Reason / Why deferred`. Sources include: out-of-scope discoveries that the executor consciously chose not to fold into this run, verifier concerns the executor declined to fix in-place, scope-boundary items from the approved plan that turned out to need their own ticket, and any unresolved `## 5. Clarification Items` row carried over from the approved plan (`Status` ∈ `{open, answered}` at approval time). An empty section is acceptable but only when expressed as the single line `- 후속 작업 없음.` — silence is treated as a contract violation. Rows with `Auto-spawn? = yes` will be materialised by `scripts/okstra-spawn-followups.py` in Phase 7; rows with `Auto-spawn? = no` MUST also appear in `Section 6. Recommended Next Steps` so the user knows to act manually.
132
132
  - Self-review pass before finalising the report (`Claude lead` runs this; do not delegate to a generic subagent):
133
133
  1. **Plan coverage** — every step in the approved plan's recommended option must point to a commit (or an explicit `Skipped: <reason>` entry). List gaps.
134
134
  2. **Evidence completeness** — every `Validation evidence` and `TDD evidence` claim has the actual command line and exit code? No paraphrased "tests pass" without output?
@@ -19,7 +19,7 @@
19
19
  - uncertainty boundaries and missing inputs
20
20
  - next recommended phase and safe resume guidance
21
21
  - Clarification request policy (phase-specific addenda — shared policy is in `_common-contract.md`):
22
- - if any blocking input is missing at the time of writing the final report, populate `## 5. Clarification Requests for the Next Run` in `final-report-template.md`
22
+ - if any blocking input is missing at the time of writing the final report, populate `## 5. Clarification Items` in `final-report-template.md` (a single unified table; `Blocks=next-phase` for items the next run cannot start without)
23
23
  - prefer concrete questions whose answers map directly to a routing decision (`bugfix` vs `feature`, `error-analysis` vs `implementation-planning`, etc.). State each option in plain language with one sentence describing what choosing it would mean for the next phase.
24
24
  - Non-goals:
25
25
  - full implementation design unless it is required to decide the next phase
@@ -0,0 +1,190 @@
1
+ """Parse the ``## 5. Clarification Items`` table from a final-report markdown.
2
+
3
+ The unified §5 table (introduced when §4.5.9 / §5.1 / §5.2 collapsed into a
4
+ single section) is the canonical home for every clarification an
5
+ implementation-planning run owes the user — decisions, file attachments,
6
+ single data points. Each row carries a ``Blocks`` column whose value picks
7
+ one of ``{approval, next-phase, none}``. Rows with ``Blocks=approval`` are
8
+ the approval gate: they MUST resolve before the user marks the report
9
+ ``Approved`` and starts the next ``implementation`` run.
10
+
11
+ This module exposes one read function for that gate so both
12
+ ``_validate_approved_plan`` (pre-implementation run-prep) and any later
13
+ validator can share the same parsing logic.
14
+
15
+ Legacy compatibility: reports written before the §5 unification used
16
+ ``4.5.9 Open Questions`` + ``5.1 Additional Materials`` + ``5.2 Questions
17
+ for the User`` and lacked a ``Blocks`` column. Those reports cannot be
18
+ gate-checked by Blocks; the parser returns ``None`` to signal "schema
19
+ absent, skip check" rather than fabricating a verdict.
20
+ """
21
+ from __future__ import annotations
22
+
23
+ import re
24
+ from dataclasses import dataclass
25
+ from pathlib import Path
26
+ from typing import Optional
27
+
28
+
29
+ SECTION_HEADING_PATTERN = re.compile(r"^##\s+5\.\s+Clarification Items\s*$", re.MULTILINE)
30
+ NEXT_TOP_LEVEL_HEADING_PATTERN = re.compile(r"^##\s+(?!5\.)", re.MULTILINE)
31
+
32
+
33
+ @dataclass(frozen=True)
34
+ class ClarificationItem:
35
+ """One row of the §5 table.
36
+
37
+ ``raw_*`` fields preserve the exact cell text (after backtick stripping)
38
+ for diagnostics; canonical lowercased versions live in ``blocks`` /
39
+ ``status`` for predicate use.
40
+ """
41
+ row_id: str
42
+ kind: str # "material" | "decision" | "data-point" | other
43
+ blocks: str # canonical lowercase: "approval" | "next-phase" | "none" | other
44
+ status: str # canonical lowercase: "open" | "answered" | "resolved" | "obsolete" | other
45
+ raw_blocks: str
46
+ raw_status: str
47
+
48
+
49
+ def _strip_backticks(cell: str) -> str:
50
+ s = cell.strip()
51
+ if s.startswith("`") and s.endswith("`") and len(s) >= 2:
52
+ s = s[1:-1].strip()
53
+ return s
54
+
55
+
56
+ def _split_pipe_row(line: str) -> list[str]:
57
+ """Split a markdown pipe-table row into cells (strip outer pipes + cell whitespace)."""
58
+ stripped = line.strip()
59
+ if stripped.startswith("|"):
60
+ stripped = stripped[1:]
61
+ if stripped.endswith("|"):
62
+ stripped = stripped[:-1]
63
+ return [_strip_backticks(c) for c in stripped.split("|")]
64
+
65
+
66
+ def _is_separator_row(line: str) -> bool:
67
+ """Detect ``|---|---|---|`` divider lines that separate header from body."""
68
+ stripped = line.strip()
69
+ if not stripped.startswith("|"):
70
+ return False
71
+ inner = stripped.strip("|").strip()
72
+ # A separator cell is dashes optionally framed by colons; anything else
73
+ # (real text) means this is a data row, not a separator.
74
+ for cell in inner.split("|"):
75
+ if not re.fullmatch(r"\s*:?-{2,}:?\s*", cell):
76
+ return False
77
+ return True
78
+
79
+
80
+ def _section_5_slice(report_text: str) -> Optional[str]:
81
+ """Return the substring spanning the §5 section (heading exclusive of the
82
+ next ``##`` heading), or None if §5 is absent."""
83
+ start_match = SECTION_HEADING_PATTERN.search(report_text)
84
+ if not start_match:
85
+ return None
86
+ rest = report_text[start_match.end():]
87
+ end_match = NEXT_TOP_LEVEL_HEADING_PATTERN.search(rest)
88
+ return rest[: end_match.start()] if end_match else rest
89
+
90
+
91
+ def parse_clarification_items(report_text: str) -> Optional[list[ClarificationItem]]:
92
+ """Return the list of §5 rows. ``None`` means "no unified §5 table
93
+ detected" (legacy report or missing section) — caller must NOT treat
94
+ that as "table is empty".
95
+
96
+ An empty list ``[]`` means "table exists but has no data rows" (e.g.,
97
+ just the ``- 추가 정보 요청 없음.`` placeholder); that IS a confident
98
+ "no approval-blocking items".
99
+ """
100
+ section = _section_5_slice(report_text)
101
+ if section is None:
102
+ return None
103
+
104
+ lines = section.splitlines()
105
+ # Locate the header row containing both `Blocks` and `Status` columns.
106
+ # We scan for the first table-shaped header line that includes both
107
+ # column names (case-insensitive); anything else (legacy 5.1 / 5.2
108
+ # tables, intro tables) is rejected.
109
+ header_idx = -1
110
+ headers: list[str] = []
111
+ for idx, line in enumerate(lines):
112
+ if not line.lstrip().startswith("|"):
113
+ continue
114
+ cells = [c.lower() for c in _split_pipe_row(line)]
115
+ if "blocks" in cells and "status" in cells:
116
+ header_idx = idx
117
+ headers = cells
118
+ break
119
+ if header_idx < 0:
120
+ # Section heading present but no Blocks/Status header — legacy
121
+ # layout or schema not yet adopted.
122
+ return None
123
+
124
+ blocks_col = headers.index("blocks")
125
+ status_col = headers.index("status")
126
+ id_col = headers.index("id") if "id" in headers else 0
127
+ kind_col = headers.index("kind") if "kind" in headers else None
128
+
129
+ items: list[ClarificationItem] = []
130
+ body_started = False
131
+ for line in lines[header_idx + 1:]:
132
+ if not line.lstrip().startswith("|"):
133
+ if body_started:
134
+ break
135
+ continue
136
+ if _is_separator_row(line):
137
+ body_started = True
138
+ continue
139
+ if not body_started:
140
+ # A second header (unlikely) or a malformed row before the
141
+ # separator — skip.
142
+ continue
143
+ cells = _split_pipe_row(line)
144
+ if max(blocks_col, status_col, id_col) >= len(cells):
145
+ # malformed row; skip rather than crash
146
+ continue
147
+ raw_blocks = cells[blocks_col]
148
+ raw_status = cells[status_col]
149
+ row_id = cells[id_col]
150
+ kind = cells[kind_col] if kind_col is not None and kind_col < len(cells) else ""
151
+ # Treat template-placeholder rows (e.g. literal ``C-001`` with
152
+ # angle-bracket sample text) as still-real rows but caller can
153
+ # filter on raw_id == "C-001" if needed.
154
+ items.append(
155
+ ClarificationItem(
156
+ row_id=row_id,
157
+ kind=kind.lower(),
158
+ blocks=raw_blocks.lower(),
159
+ status=raw_status.lower(),
160
+ raw_blocks=raw_blocks,
161
+ raw_status=raw_status,
162
+ )
163
+ )
164
+ return items
165
+
166
+
167
+ UNRESOLVED_STATUSES = {"open", "answered"}
168
+
169
+
170
+ def unresolved_approval_blockers(report_text: str) -> Optional[list[ClarificationItem]]:
171
+ """Return rows that gate the User Approval Request — ``Blocks=approval``
172
+ AND ``Status`` in ``{open, answered}``.
173
+
174
+ ``None`` propagates the "schema absent" signal from
175
+ ``parse_clarification_items``: caller may decide to soft-pass legacy
176
+ reports and only enforce on the new-format §5.
177
+ """
178
+ items = parse_clarification_items(report_text)
179
+ if items is None:
180
+ return None
181
+ return [
182
+ it for it in items
183
+ if it.blocks == "approval" and it.status in UNRESOLVED_STATUSES
184
+ ]
185
+
186
+
187
+ def unresolved_approval_blockers_in_file(path: Path) -> Optional[list[ClarificationItem]]:
188
+ return unresolved_approval_blockers(
189
+ Path(path).read_text(encoding="utf-8", errors="replace")
190
+ )
@@ -24,6 +24,7 @@ from datetime import datetime, timezone
24
24
  from pathlib import Path
25
25
 
26
26
  from okstra_project import upsert_project_json
27
+ from .clarification_items import unresolved_approval_blockers
27
28
  from .qa_commands import format_errors as _format_qa_errors, validate_qa_commands
28
29
  from .material import (
29
30
  build_analysis_material,
@@ -131,7 +132,8 @@ def _validate_approved_plan(path: str) -> None:
131
132
  p = Path(path)
132
133
  if not p.is_file():
133
134
  raise PrepareError(f"approved plan file not found: {path}")
134
- if not APPROVED_PLAN_PATTERN.search(p.read_text(encoding="utf-8", errors="replace")):
135
+ body = p.read_text(encoding="utf-8", errors="replace")
136
+ if not APPROVED_PLAN_PATTERN.search(body):
135
137
  raise PrepareError(
136
138
  f"approved plan has no recognised user-approval marker: {path}\n"
137
139
  ' canonical form (single line, top-of-report block): "- [x] Approved"\n'
@@ -140,6 +142,21 @@ def _validate_approved_plan(path: str) -> None:
140
142
  " shortcut: re-run okstra with --approve to have the CLI itself "
141
143
  "record the approval marker on this file."
142
144
  )
145
+ # The approval marker is set. Cross-check the §5 Clarification Items
146
+ # table: any row with Blocks=approval that is still open/answered
147
+ # invalidates the approval. Legacy reports (no unified §5 with a
148
+ # Blocks column) return None — soft-pass to preserve compatibility
149
+ # during the transitional release.
150
+ blockers = unresolved_approval_blockers(body)
151
+ if blockers:
152
+ lines = [
153
+ f"approved plan marker is set but §5 has {len(blockers)} unresolved "
154
+ f"`Blocks=approval` row(s); resolve them or mark them obsolete before approving:",
155
+ ]
156
+ for b in blockers:
157
+ lines.append(f" - {b.row_id} (Status={b.raw_status})")
158
+ lines.append(f" file: {path}")
159
+ raise PrepareError("\n".join(lines))
143
160
 
144
161
 
145
162
  # `- [ ] Approved` 라인을 정확히 한 번만 매치한다. 좌측 leading whitespace 와
@@ -68,7 +68,7 @@ PHASE_RULES: dict[str, dict[str, str]] = {
68
68
  " - trade-off matrix across options (complexity, risk, reversibility, test cost, rollout cost) and recommended option with rationale tied to isolation / single-responsibility / YAGNI principles\n"
69
69
  " - bite-sized stepwise execution order for the recommended option (each step ~2-5 min, exact file paths and commands, TDD ordering when applicable, no placeholders)\n"
70
70
  " - dependency / migration risk assessment, validation checklist (pre / mid / post with exact commands), rollback strategy with revert path and trigger signal\n"
71
- " - `Open Questions` block listing every unresolved ambiguity\n"
71
+ " - every unresolved ambiguity registered as a `Blocks=approval` row in the `## 5. Clarification Items` table (do NOT create a separate `Open Questions` block under `4.5.x` the unified table is the single home)\n"
72
72
  " - explicit `User Approval Request` block awaiting human approval\n"
73
73
  " - self-review confirmation (spec coverage, placeholder scan, internal consistency, ambiguity, scope)"
74
74
  ),
@@ -224,7 +224,7 @@ Skipping this file because "the real report is in `reports/`" is wrong. Both fil
224
224
 
225
225
  Section numbering matches `okstra-final-report.template.md`. Section 0 is the carry-in reconciliation that runs first when a clarification response was provided; sections 1–7 follow the template's main body order.
226
226
 
227
- 0. **Clarification Response Carried In** - if `{{CLARIFICATION_RESPONSE_RELATIVE_PATH}}` is non-empty, read `instruction-set/clarification-response.md`, reconcile every prior `Q*` row, and record the outcome (`resolved`/`obsolete`) plus the new evidence in this section before drafting the verdict
227
+ 0. **Clarification Response Carried In** - if `{{CLARIFICATION_RESPONSE_RELATIVE_PATH}}` is non-empty, read `instruction-set/clarification-response.md`, walk every `C-*` row of the prior report's `## 5. Clarification Items` table, reconcile each one against new evidence, and record the outcome (`resolved`/`obsolete`) plus the citation in this section before drafting the verdict. If the prior report uses the deprecated `4.5.9 Open Questions` / `5.1` / `5.2` layout with `OQ-*`/`A*`/`Q*` IDs, follow the legacy-carry-in mapping rule in `final-report-template.md` section 0.
228
228
  1. **Problem or Verification Summary** - Key summary based on the brief and data (3–5 bullet points)
229
229
  2. **Cross Verification Results** (Use 4 categories when convergence is enabled, per `okstra-convergence`)
230
230
  - Round History sub-table (convergence-enabled runs only): one row per executed round with columns `Round | inputQueueSize | resolvedCount | carriedForwardCount | dispatches (worker:status:durationMs) | skippedWorkers (worker:reason)`. Add a one-line note immediately under the table with `round2SkippedReason: <value>` (always present, even when `"not-skipped"`). Pull all values verbatim from `convergence-<task-type>-<seq>.json`.
@@ -239,7 +239,7 @@ Section numbering matches `okstra-final-report.template.md`. Section 0 is the ca
239
239
  - If explicit expected values are present in `reference-expectations.md`, specify whether they match or differ from the expected values in config files / deployment manifests
240
240
  - Supporting evidence or alternative interpretations
241
241
  5. **Missing Information and Risks** - Uncertain/I don't know items
242
- 6. **Clarification Requests for the Next Run** - structured Q&A table the user fills inline before reruns
242
+ 6. **Clarification Items** - single unified table (`C-001`, `C-002`, ...) the user fills inline before reruns. Columns: `ID`, `Ticket ID`, `Kind` (`material` / `decision` / `data-point`), `Statement`, `Expected form`, `Blocks` (`approval` / `next-phase` / `none`), `Status`, `User input`. Replaces the legacy `4.5.9 Open Questions` / `5.1` / `5.2` triple; never create those sub-sections — same item appearing in two places is the failure mode this table prevents.
243
243
  - Required for `task-type` `error-analysis` and `requirements-discovery` whenever blocking uncertainty remains
244
244
  - Optional for other task-types; explicitly state "no clarification needed" when none
245
245
  - Follow the table format from `final-report-template.md` exactly (columns: Question ID, Blocking, Why this matters, Question, Expected answer shape, Status, Answer)
@@ -127,13 +127,15 @@ Reading rules:
127
127
  large for one read; if you must page, you MUST cover the entire file
128
128
  before moving on, and you MUST state the page boundaries you used in your
129
129
  Findings section.
130
- - For the carry-in clarification response, read sub-section 0, sub-section
131
- 5.1 (`A1`, `A2`, ... material requests), and sub-section 5.2 (`Q1`,
132
- `Q2`, ... user questions) in full, including every row of every table,
133
- even if the answer column appears blank. The fact that you will write
134
- your output into a file with a structurally similar Section 5 is NOT an
135
- excuse to skim the prior `A*` and `Q*` rows carry context you cannot
136
- reconstruct from the new run alone.
130
+ - For the carry-in clarification response, read sub-section 0 and every
131
+ row of `## 5. Clarification Items` (`C-001`, `C-002`, ...) in full,
132
+ including rows whose `User input` cell is blank. The fact that you
133
+ will write your output into a file with a structurally similar
134
+ section 5 is NOT an excuse to skim the prior `C-*` rows carry
135
+ context you cannot reconstruct from the new run alone. If the prior
136
+ report uses the deprecated `4.5.9 Open Questions` / `5.1` / `5.2`
137
+ layout with `OQ-*` / `A*` / `Q*` IDs, walk all three blocks the
138
+ same way (legacy carry-in transitional rule).
137
139
  - Before writing any Findings, state in one sentence per file that you
138
140
  read it end-to-end. Example: "Read task-brief.md end-to-end (147 lines)."
139
141
  If you cannot truthfully say this for a file, do not produce Findings —
@@ -24,7 +24,7 @@ taskType: "{{FM_TASK_TYPE}}"
24
24
 
25
25
  > **이 블록은 `task-type` = `implementation-planning` 결과에서만 의미를 가집니다.** 다른 task-type의 보고서에서는 이 섹션 전체를 삭제하세요.
26
26
  >
27
- > 다음 `implementation` run은 아래 체크박스가 `[x]`로 표시되어 있을 때에만 진입할 수 있습니다 (`okstra_ctl.run._validate_approved_plan` 가 이 마커를 line-anchored 정규식으로 검사하여 통과/거부합니다). 본문(`Sections 1`–`4.5`)을 끝까지 읽고, `4.5.9 Open Questions`가 비어 있거나 모두 해소된 뒤 승인해 주세요.
27
+ > 다음 `implementation` run은 아래 체크박스가 `[x]`로 표시되어 있을 때에만 진입할 수 있습니다 (`okstra_ctl.run._validate_approved_plan` 가 이 마커를 line-anchored 정규식으로 검사하여 통과/거부합니다). 본문(`Sections 1`–`4.5`)을 끝까지 읽고, `## 5. Clarification Items` 표에서 `Blocks=approval` 모든 행이 `Status` 가 `resolved` 또는 `obsolete` 인지 확인한 뒤 승인해 주세요. 한 행이라도 `open`/`answered` 로 남아 있으면 `Approved` 마커를 새기지 마십시오 — 향후 `validate-run.py` 가 이 정합성을 자동 검사할 예정입니다.
28
28
 
29
29
  - 승인 여부 (사용자가 직접 편집): `- [ ] Approved` ← 승인하려면 `[ ]` 를 `[x]` 로 변경하여 저장하세요.
30
30
  - 승인 후 다음 단계 명령어 (방법 A — 수동 편집):
@@ -34,13 +34,13 @@ taskType: "{{FM_TASK_TYPE}}"
34
34
  - Claude Code 세션 안: `/okstra-run task-key={{TASK_KEY}} task-type=implementation approved-plan=<이 보고서 경로> approve`
35
35
  - 별도 터미널: `scripts/okstra.sh --task-key {{TASK_KEY}} --task-type implementation --approved-plan <이 보고서 경로> --approve`
36
36
  - 방법 B 는 `--approve` 입력 행위 자체를 승인 의사로 모델링합니다. 런타임이 본 블록의 체크박스를 자동으로 `[x]` 로 바꾸고, 본 섹션 하단에 `승인 일시 (CLI ack): <ISO8601>` audit 라인을 한 줄 덧붙입니다.
37
- - 승인을 보류하거나 거부하려면 체크박스는 `[ ]` 로 두고 `--approve` 도 사용하지 마세요. 필요한 변경 사항은 `4.5.9 Open Questions` 또는 `Section 5 Clarification Requests` 기록한 같은 phase 를 재실행해 주세요.
37
+ - 승인을 보류하거나 거부하려면 체크박스는 `[ ]` 로 두고 `--approve` 도 사용하지 마세요. 필요한 변경 사항은 `## 5. Clarification Items` 표에 행으로 기록한 뒤 (`Blocks=approval` 표시) 같은 phase 를 재실행해 주세요.
38
38
 
39
39
  ## 0. Clarification Response Carried In From Previous Run
40
40
  - Source file: `{{CLARIFICATION_RESPONSE_RELATIVE_PATH}}`
41
41
  - If the source path is empty, write `- No prior clarification response was provided for this run.` and skip the rest of this section.
42
- - If the source path is set, walk through both halves of the prior section 5 material requests (`A1`, `A2`, ...) from sub-section 5.1 and user questions (`Q1`, `Q2`, ...) from sub-section 5.2 and summarize how each one was used or invalidated by this run's evidence. Keep the two prefixes separate; do not collapse them into a single list.
43
- - Cite the entry id (`A*` or `Q*`) and the new evidence (file path, line number, log excerpt, worker finding, etc.) that resolves or refutes the prior answer or material.
42
+ - If the source path is set, walk every row of the prior report's `## 5. Clarification Items` table (`C-001`, `C-002`, ...). For each row, summarize how its `User input` (or its absence) was used by this run's evidence, cite the new evidence (file path, line number, log excerpt, worker finding) that resolves or refutes the prior answer, and update the row's `Status` to `resolved` or `obsolete` when carrying it into this run's section 5.
43
+ - Transitional rule (legacy carry-in only): if the prior report uses the deprecated `4.5.9 Open Questions` / `5.1 Additional Materials` / `5.2 Questions for the User` layout with `OQ-*` / `A*` / `Q*` IDs, walk every row of all three blocks. Map each legacy row into a single `C-*` row in this run's new `## 5. Clarification Items` table assign `Kind = material` for prior `A*`, `Kind = decision` for prior `Q*` and prior `OQ*`, and `Blocks = approval` only for items the prior report flagged as gating the implementation-planning approval (default: `next-phase`). Keep the legacy ID in the new row's Statement column for traceability (e.g. `"[legacy OQ-003] ..."`).
44
44
 
45
45
  ## Summary of the Problem or Verification Target
46
46
 
@@ -223,14 +223,7 @@ revert 경로와 롤백 트리거 신호를 표로 정리합니다. 추상적
223
223
  ### 4.5.8 User Approval Request (사용자 승인 요청 — 본 보고서 상단 참조)
224
224
  - 실제 승인 게이트는 본 보고서 **상단 `User Approval Request (사용자 승인 게이트)` 블록**에 있습니다. 이 하위 섹션은 validator가 요구하는 영문 키워드(`User Approval Request`)와 본문 구조 일관성을 위해 남겨 둡니다.
225
225
  - 본 섹션에는 승인 결정에 영향을 주는 *플랜 측 보충 메모*만 적습니다(예: 위험을 줄이기 위한 사전 작업, 승인 전 사용자가 확인해 두어야 할 사항). 승인 마커는 본 섹션이 아니라 상단 블록의 체크박스로만 부여합니다.
226
-
227
- ### 4.5.9 Open Questions
228
-
229
- pre-planning에서 발견된 모호점을 표로 남깁니다. 사용자가 승인 전에 해소해야 할 질문 목록입니다. 없을 시: `- Open question 없음.` 한 줄.
230
-
231
- | ID | Ticket ID | 질문 | Blocking (예/아니오) | 기대 답 형태 | Status |
232
- |----|-----------|------|----------------------|--------------|--------|
233
- | OQ-001 | `<TICKET-or-fallback>` | <질문 본문> | 예 | <예/아니오 / 선택 / 자유서술> | open |
226
+ - 사용자가 답하거나 자료를 첨부해야만 승인이 가능한 항목은 **이 섹션에 적지 않습니다** — `## 5. Clarification Items` 표에 한 행으로 등록하고 `Blocks=approval` 로 표시하세요. 같은 항목을 두 위치에 적으면 sync 가 깨집니다.
234
227
 
235
228
  ## 4.6 Release Handoff Deliverables (release-handoff runs only)
236
229
 
@@ -287,49 +280,56 @@ H1 이 `skip` 이거나 H3 가 `cancel` 인 경우, 본 섹션 다음의 4.6.4 ~
287
280
  - 단, H1=`skip` 또는 H3=`cancel` 로 종료된 경우 사용자가 추후 재진입해 다시 release-handoff 를 실행할 수 있는지 한 줄로 명시합니다.
288
281
  - 형식 예시: `- Routing: done. PR <url> opened against <base>; no follow-up required.`
289
282
 
290
- ## 5. Clarification Requests for the Next Run
283
+ ## 5. Clarification Items
284
+
285
+ 이 섹션은 다음 run 으로 넘어가기 전에 사용자가 답하거나 자료를 첨부해야 하는 항목을 **한 표 안에서** 추적합니다. `task-type`이 `error-analysis` 또는 `requirements-discovery` 이고 지금까지의 증거만으로 확신 있는 최종 판단이 어려울 때는 반드시 채웁니다. 그 외의 `task-type`에서는 lead 가 필요하다고 판단할 때만 채우고, 그렇지 않다면 `- 추가 정보 요청 없음. Section 2의 최종 판단이 그대로 유효합니다.` 한 줄만 남깁니다.
291
286
 
292
- 섹션은 다음 run으로 넘어가기 전에 사용자에게 받아야 입력을 정리하는 자리입니다. `task-type`이 `error-analysis` 또는 `requirements-discovery`이고 지금까지의 증거만으로는 확신 있는 최종 판단이 어려울 반드시 채웁니다. 외의 `task-type`에서는 lead가 추가 run이 필요하다고 판단했을 때만 채우고, 그렇지 않다면 `- 추가 정보 요청 없음. Section 2의 최종 판단이 그대로 유효합니다.` 라고만 한 줄 적은 뒤 Section 6으로 넘어갑니다.
287
+ 표가 도입되기 전에는 `4.5.9 Open Questions` / `5.1 추가 자료 요청` / `5.2 사용자 확인 질문` 자리에 같은 항목이 중복으로 적히는 문제가 있었습니다. 위치는 이상 사용하지 않으며, 모든 항목은 표의 행으로 표현합니다. 외부에 같은 항목을 다시 적지 마세요 (sync 깨집니다).
293
288
 
294
289
  작성 원칙:
295
290
 
296
291
  - 개발자가 아닌 사람도 한 번 읽고 무엇을 어디서 가져와야 하는지 이해할 수 있게, 줄임말과 내부 용어 대신 풀어 쓴 문장으로 작성합니다. (예: "QPS" 대신 "초당 평균 요청 수", "AC 미정" 대신 "합격 기준이 아직 정의되지 않았습니다")
297
- - 한 항목은 "왜 묻는지", "구체적으로 무엇을 묻는지", "어떤 형태의 답을 기대하는지"가 모두 분명히 드러나야 합니다. 한 줄짜리 단답형 질문은 피하고, 필요한 배경을 1~2문장으로 함께 적습니다.
298
- - 사용자에게 추가로 부탁드리는 자료(로그, 스크린샷, 설정 파일 사본, 재현 절차 등)와, 사용자에게 답을 받아야 하는 질문은 **반드시 개의 별도 하위 섹션으로 나누어 적습니다.** 같은 표나 같은 항목 안에 섞지 않습니다.
299
- - run 사이에 일관된 추적이 가능하도록 항목별 ID(`A1`, `A2` / `Q1`, `Q2`)는 한 번 부여한 뒤 다음 run에서도 유지합니다. 이미 답변된 항목은 다음 run에서도 삭제하지 말고 `Status`만 `resolved` 또는 `obsolete`로 갱신합니다.
300
- - Status 값의 의미는 다음과 같습니다.
301
- - `open`: 요청했지만 아직 받지 못했습니다.
302
- - `answered`: 사용자가 아래 답변 칸을 채워 두었습니다(다음 run의 lead가 검증해야 합니다).
303
- - `resolved`: 다음 run에서 lead가 답변을 받아 검증을 마쳤습니다.
304
- - `obsolete`: 이후 분석 결과로 더 이상 필요 없어진 항목입니다.
305
-
306
- 이 보고서에 답을 채우신 뒤에는 한 줄로 같은 phase를 다시 실행하실 수 있습니다(자동으로 `$EDITOR`가 이 파일을 열고, 저장하면 같은 phase가 `--clarification-response`로 carry-in 되어 재실행됩니다).
292
+ - 한 행은 "왜 필요한지", "무엇을 묻거나 받아야 하는지", "어떤 형태의 답을 기대하는지"가 모두 분명히 드러나야 합니다. 한 줄짜리 단답형 질문은 피하고, 필요한 배경을 1~2 문장으로 함께 적습니다.
293
+ - run 사이에 일관된 추적이 가능하도록 항목별 ID (`C-001`, `C-002`, ...) 부여한 다음 run 에서도 유지합니다. 이미 답변된 항목은 다음 run 에서도 삭제하지 말고 `Status`만 `resolved` 또는 `obsolete` 로 갱신합니다.
294
+
295
+ 이 보고서에 답을 채우신 뒤에는 한 줄로 같은 phase 를 다시 실행하실 수 있습니다 (자동으로 `$EDITOR` 가 이 파일을 열고, 저장하면 같은 phase 가 `--clarification-response` carry-in 되어 재실행됩니다).
307
296
  - Claude Code 세션 안: `/okstra-run resume-clarification task-key={{TASK_KEY}}`
308
297
  - 별도 터미널: `scripts/okstra.sh --resume-clarification --task-key {{TASK_KEY}}`
309
298
 
310
- 스크립트로 자동화하실 때는 셸 형식 `scripts/okstra.sh --task-key {{TASK_KEY}} --task-type {{TASK_TYPE}} --clarification-response <이 파일 경로>`도 그대로 사용하실 수 있습니다. Node `okstra` admin CLI 는 `--task-key`/`--task-type`/`--resume-clarification` 을 받지 않으므로 위 두 진입점 중 하나를 사용하세요.
311
-
312
- ### 5.1 추가 자료 요청 (Additional Materials Requested)
313
-
314
- 하위 섹션에는 **사용자가 다음 run 전에 첨부 또는 공유해 주셨으면 하는 자료**만 적습니다. 질문이 아니라 자료 요청입니다. 자료가 어디에 있는지(파일 경로, 페이지 URL, 데이터베이스 쿼리 결과 등), 어떤 시점/조건의 데이터를 받아야 하는지, 받은 자료를 어디에 두면 되는지(예: `<project-root>/.project-docs/<task-group>/<task-id>/` 아래) 함께 적습니다. 받을 자료가 없다면 `- 추가로 요청드릴 자료가 없습니다.` 줄만 남깁니다.
315
-
316
- | 자료 ID | Ticket ID | 무엇을 / 필요한지 (문장으로 서술) | 어디에서 가져올 있는지 (파일 경로, 시스템 이름, URL 등) | 어디에 두면 되는지 / 어떻게 전달해 주실지 | Status | 사용자가 첨부한 위치 또는 메모 (다음 run 전에 사용자가 채움) |
317
- |---------|-----------|--------------------------------------|------------------------------------------------------------|--------------------------------------------|--------|---------------------------------------------------------------|
318
- | A1 | `<TICKET-or-fallback>` | <어떤 자료를 받아야 하는지를 1~2문장으로. 예: "오류가 처음 보고된 시각 전후 30분 동안의 worker 로그가 필요합니다. 어떤 task가 실패했고 그때 큐에 무엇이 쌓여 있었는지를 함께 확인해야 root cause 가설을 좁힐 수 있기 때문입니다." > | <예: "Datadog 로그에서 `service:worker env:prod` 필터, 시각 범위 2026-04-30 09:30~10:30 KST" / 또는 정확한 파일 경로> | <예: "`.project-docs/tasks/8852/logs/` 아래에 `worker-2026-04-30.log` 라는 이름으로 저장해 주시면 됩니다."> | open | <빈칸으로 두고 다음 run 전에 사용자가 채움> |
319
-
320
- ### 5.2 사용자 확인 질문 (Questions for the User)
321
-
322
- 하위 섹션에는 **사용자가 직접 결정해 주시거나 확인해 주셔야만 다음 단계로 진행할 수 있는 질문**만 적습니다. 자료 요청은 위 5.1에 두고 여기에는 의사결정/확인 질문만 둡니다. 질문이 없다면 `- 추가로 확인이 필요한 질문이 없습니다.` 한 줄만 남깁니다.
323
-
324
- 질문은 다음을 분명히 보여주어야 합니다.
325
-
326
- - **왜 답이 필요한가**: 답에 따라 무엇이 어떻게 달라지는지 문장으로.
327
- - **무엇을 묻는가**: 약어 없이 풀어 완전한 문장의 질문.
328
- - **어떤 형태의 답을 기대하는가**: 예/아니오인지, 둘 중 하나를 고르는 선택인지, 숫자/날짜/파일경로인지, 아니면 짧은 자유서술인지. 가능하면 보기를 함께 제시합니다.
329
-
330
- | 질문 ID | Ticket ID | 이 답이 필요한 이유 (왜) | 질문 본문 (무엇을 묻는지, 풀어 쓴 문장) | 기대하는 답의 형태 / 보기 | Blocking (예 / 아니오) | Status | 사용자 답변 (다음 run 전에 사용자가 채움) |
331
- |---------|-----------|--------------------------|-----------------------------------------|----------------------------|------------------------|--------|--------------------------------------------|
332
- | Q1 | `<TICKET-or-fallback>` | <예: "이 결정에 따라 `bugfix`로 이어갈지 `feature`로 이어갈지가 갈리며, 다음 run에서 사용할 task-type이 달라집니다."> | <예: "지난 주 새벽에 보고된 결제 실패가 일회성 사고였는지, 아니면 같은 증상이 다시 발생한 적이 있는지 알려주실 수 있을까요? 같은 증상이 다시 발생했다면 가장 최근 발생 시각과 영향 받은 사용자 수도 함께 부탁드립니다."> | <예: "다음 중 한 줄로 답해 주시면 됩니다 — (a) 일회성으로 그 후 재발 없음, (b) 재발 있음, 가장 최근 시각: YYYY-MM-DD HH:MM, 영향 사용자 수 약 N명"> | 예 | open | <빈칸으로 두고 다음 run 전에 사용자가 채움> |
299
+ 스크립트로 자동화하실 때는 셸 형식 `scripts/okstra.sh --task-key {{TASK_KEY}} --task-type {{TASK_TYPE}} --clarification-response <이 파일 경로>` 그대로 사용하실 수 있습니다. Node `okstra` admin CLI 는 `--task-key`/`--task-type`/`--resume-clarification` 을 받지 않으므로 위 두 진입점 중 하나를 사용하세요.
300
+
301
+ ### 5.1 Clarification Items table
302
+
303
+ | ID | Ticket ID | Kind | Statement (무엇이 필요한지 + ) | Expected form (답·자료의 모양) | Blocks | Status | User input (사용자가 다음 run 전에 채움) |
304
+ |----|-----------|------|----------------------------------|-------------------------------|--------|--------|------------------------------------------|
305
+ | C-001 | `<TICKET-or-fallback>` | `decision` | <예: "지난 새벽에 보고된 결제 실패가 일회성 사고였는지, 아니면 같은 증상이 다시 발생한 적이 있는지 알려주십시오. 결정에 따라 다음 run 의 task-type 이 `error-analysis` 로 갈지 `requirements-discovery` 로 갈지가 갈립니다."> | <예: "(a) 일회성·재발 없음, (b) 재발 있음 — 가장 최근 시각 YYYY-MM-DD HH:MM, 영향 사용자 수 약 N명"> | `next-phase` | open | <빈칸으로 두고 다음 run 전에 사용자가 채움> |
306
+ | C-002 | `<TICKET-or-fallback>` | `material` | <예: "오류가 처음 보고된 시각 전후 30분 동안의 worker 로그가 필요합니다. 어떤 task 가 실패했고 그때 큐에 무엇이 쌓여 있었는지를 함께 확인해야 root cause 가설을 좁힐 수 있기 때문입니다. Datadog `service:worker env:prod` 필터, 시각 범위 2026-04-30 09:30~10:30 KST. `.project-docs/tasks/8852/logs/worker-2026-04-30.log` 로 저장해 주십시오."> | <예: "위 경로의 .log 파일 (gzip 압축 가능)"> | `next-phase` | open | |
307
+ | C-003 | `<TICKET-or-fallback>` | `data-point` | <예: "본 배치 시작 직전 `common.FontManualClassifiers` `prediction=0` / `prediction=1` 수가 필요합니다. acceptance #5 동일성 검증 ground truth 입니다."> | <예: " 정수 (prediction=0 count, prediction=1 count) 줄로"> | `approval` | open | |
308
+
309
+ ### 5.2 컬럼 가이드
310
+
311
+ - **`Kind`** `{material, decision, data-point}`:
312
+ - `material` — 파일 / 스냅샷 / 로그 / 스크린샷 등 사용자가 따로 **첨부 또는 경로 공유** 해야 하는 자료.
313
+ - `decision` 사용자가 선택지 중 하나를 고르거나 (a/b/c 보기, yes/no) 짧게 자유서술로 결정해 주셔야 진행 가능한 의사결정.
314
+ - `data-point` — 숫자 / ID / 날짜 / 짧은 문자열처럼 사용자가 명령 한 번으로 뽑아 **inline 으로** 답해 주실 수 있는 사실 확인.
315
+ - **`Statement`**: 무엇을 묻거나 받아야 하는지 + 필요한지 + (material 경우) 어디서 가져오는지·어디에 두면 되는지를 1~3 문장으로. 자료 출처가 길면 본 셀에 풀어 적고 `Expected form` 셀은 짧게 유지합니다.
316
+ - **`Expected form`**: / 자료의 모양 예/아니오, 보기 a/b/c, 정수, 날짜, 파일 경로, URL 등. 가능하면 정확한 보기 또는 예시 한 줄.
317
+ - **`Blocks`** `{approval, next-phase, none}`:
318
+ - `approval` — `implementation-planning` 의 User Approval Request 통과를 막는 항목. 모두 `resolved`/`obsolete` 가 되어야 상단 체크박스를 `[x]` 로 바꿀 수 있습니다.
319
+ - `next-phase` 승인 게이트가 아니더라도 다음 run 진입에 답이 필요한 항목.
320
+ - `none` — informational. 답이 없어도 다음 phase 는 진행 가능하지만 audit 기록을 위해 남깁니다.
321
+ - **`Status`** `{open, answered, resolved, obsolete}`:
322
+ - `open` — 요청했지만 아직 받지 못함.
323
+ - `answered` — 사용자가 `User input` 칸을 채워 두었음 (다음 run 의 lead 가 검증해야 함).
324
+ - `resolved` — 다음 run 에서 lead 가 답변·자료를 받아 검증을 마침.
325
+ - `obsolete` — 이후 분석 결과로 더 이상 필요 없어진 항목.
326
+ - **`User input`**: 사용자가 직접 채우는 칸. `material` 이면 첨부 파일 경로 / URL / 메모, `decision` 이면 선택 또는 짧은 서술, `data-point` 이면 inline 값.
327
+
328
+ 작성 안티패턴 (자동 거절):
329
+
330
+ - 같은 항목을 `## 4.5.8 User Approval Request` 보충 메모와 본 표에 동시 등록 — `Blocks=approval` 행 하나로 충분합니다.
331
+ - 한 행을 두 개로 나눠 "자료 요청 행" 과 "관련 질문 행" 으로 쪼개기 — 자료 + yes/no 가 묶인 항목은 `Kind=material`, `Expected form` 에 "파일 경로 + yes/no" 라고 적은 한 행으로 처리합니다.
332
+ - 사용자가 답하지 않아도 진행 가능한 항목을 `Blocks=approval` 로 표시 — `Blocks=none` 또는 `next-phase` 로 정확히 표시하세요. 잘못 표시하면 사용자가 불필요하게 게이트에 걸립니다.
333
333
 
334
334
  ## 6. Recommended Next Steps
335
335
 
@@ -346,7 +346,7 @@ When concrete actions exist, list them as a numbered list using the rules below.
346
346
  - 별도 터미널: `scripts/okstra.sh --task-key {{TASK_KEY}} --task-type <next-phase>`
347
347
  2. **Additional verification needed before implementation or release.** List read-only checks (test commands, log queries, dashboard URLs) that the user should run before moving to the next phase. No state-mutating commands here.
348
348
  3. **Follow-up tasks or related tasks if needed.** Reference them by `task-key` when they already exist; otherwise describe the new brief to draft.
349
- 4. **If section 5 has any `open` rows**, the highest-priority next step MUST be the clarification turn-around. Show both forms:
349
+ 4. **If section 5 has any rows with `Status` in `{open, answered}`**, the highest-priority next step MUST be the clarification turn-around. Show both forms:
350
350
  - Preferred (interactive) — opens this file in `$EDITOR`, then auto-reruns the same phase with `--clarification-response` carry-in:
351
351
  - Claude Code 세션 안: `/okstra-run resume-clarification task-key={{TASK_KEY}}`
352
352
  - 별도 터미널: `scripts/okstra.sh --resume-clarification --task-key {{TASK_KEY}}`
@@ -370,7 +370,7 @@ Empty-state placeholder, copy verbatim when nothing else applies:
370
370
  - `out-of-plan` — 구현 중 발견됐으나 본 plan의 file list / step 범위를 벗어나 처리하지 못한 항목 (Out-of-plan edits 블록에 기록되지 않고 미처리로 남은 것).
371
371
  - `verifier-concern` — verifier가 PASS는 줬으나 후속 개선 권고로 남긴 항목.
372
372
  - `scope-boundary` — `implementation-planning`의 `4.5.5 Dependency / Migration Risk` 또는 task-brief `Out of Scope` 에서 의도적으로 제외했으나, 본 run 결과에 비추어 별도 ticket이 필요한 항목.
373
- - `open-question` — `4.5.9 Open Questions` / `Section 5 Clarification Requests` 에서 분리한 후속 작업.
373
+ - `open-question` — `## 5. Clarification Items` 에서 분리한 후속 작업 (사용자 답을 그대로 닫지 않고 별도 task 로 진행해야 하는 경우).
374
374
  - `manual` — lead가 추가로 식별한 follow-up.
375
375
 
376
376
  규칙: