okstra 0.111.0 → 0.112.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.
Files changed (55) hide show
  1. package/README.kr.md +2 -2
  2. package/README.md +2 -2
  3. package/bin/okstra +7 -1
  4. package/docs/for-ai/README.md +2 -2
  5. package/docs/for-ai/skills/okstra-brief.md +2 -3
  6. package/docs/for-ai/skills/okstra-container-build.md +2 -3
  7. package/docs/for-ai/skills/okstra-inspect.md +5 -12
  8. package/docs/for-ai/skills/okstra-rollup.md +3 -4
  9. package/docs/for-ai/skills/okstra-run.md +3 -5
  10. package/docs/for-ai/skills/okstra-schedule.md +2 -7
  11. package/docs/for-ai/skills/okstra-setup.md +1 -1
  12. package/docs/kr/architecture/storage-model.md +1 -2
  13. package/docs/kr/architecture.md +2 -2
  14. package/docs/kr/cli.md +4 -2
  15. package/docs/project-structure-overview.md +5 -3
  16. package/package.json +1 -1
  17. package/runtime/BUILD.json +2 -2
  18. package/runtime/prompts/coding-preflight/overview.md +3 -1
  19. package/runtime/prompts/launch.template.md +1 -5
  20. package/runtime/prompts/lead/context-loader.md +2 -4
  21. package/runtime/prompts/lead/convergence.md +11 -240
  22. package/runtime/prompts/lead/okstra-lead-contract.md +70 -34
  23. package/runtime/prompts/lead/plan-body-verification.md +240 -0
  24. package/runtime/prompts/lead/report-writer.md +7 -7
  25. package/runtime/prompts/lead/team-contract.md +15 -17
  26. package/runtime/prompts/profiles/_common-contract.md +1 -37
  27. package/runtime/prompts/profiles/_implementation-executor.md +2 -2
  28. package/runtime/prompts/profiles/_implementation-verifier.md +1 -1
  29. package/runtime/prompts/profiles/final-verification.md +1 -1
  30. package/runtime/prompts/profiles/implementation-planning.md +2 -2
  31. package/runtime/prompts/profiles/implementation.md +1 -1
  32. package/runtime/python/okstra_ctl/path_hints.py +1 -0
  33. package/runtime/python/okstra_ctl/paths.py +3 -0
  34. package/runtime/python/okstra_ctl/render.py +8 -0
  35. package/runtime/python/okstra_ctl/set_work_status.py +147 -0
  36. package/runtime/python/okstra_ctl/team_reconcile.py +1 -1
  37. package/runtime/skills/okstra-brief/SKILL.md +32 -176
  38. package/runtime/skills/okstra-brief/references/reporter-confirmations.md +71 -0
  39. package/runtime/skills/okstra-brief/references/tracker-recursion.md +90 -0
  40. package/runtime/skills/okstra-container-build/SKILL.md +7 -20
  41. package/runtime/skills/okstra-graphify/SKILL.md +8 -8
  42. package/runtime/skills/okstra-inspect/SKILL.md +27 -43
  43. package/runtime/skills/okstra-rollup/SKILL.md +6 -6
  44. package/runtime/skills/okstra-run/SKILL.md +7 -16
  45. package/runtime/skills/okstra-schedule/SKILL.md +64 -419
  46. package/runtime/skills/okstra-setup/SKILL.md +25 -223
  47. package/runtime/skills/okstra-setup/references/project-config.md +188 -0
  48. package/runtime/templates/reports/schedule.template.md +2 -2
  49. package/runtime/templates/worker-prompt-preamble.md +1 -1
  50. package/runtime/validators/validate-run.py +7 -7
  51. package/src/cli-registry.mjs +14 -0
  52. package/src/commands/inspect/set-work-status.mjs +31 -0
  53. package/src/commands/lifecycle/check-project.mjs +68 -56
  54. package/src/commands/lifecycle/install.mjs +1 -0
  55. package/src/commands/lifecycle/preflight.mjs +82 -0
@@ -6,168 +6,118 @@ model: opus
6
6
 
7
7
  # OKSTRA Schedule
8
8
 
9
- ## Overview
10
-
11
- Generate a consolidated work schedule for all non-done tasks in a given `task-group`. The skill reads each task's `task-manifest.json` and `latestReport`, classifies tasks into phases by priority and risk, and writes a single Markdown plan file under `.okstra/tasks/<task-group>/schedule/`.
12
-
13
- The skill runs as a single Claude lead synthesis (lightweight mode). A `--cross-verify` multi-agent variant was previously sketched here but never specified end-to-end; it has been dropped pre-1.0 and is tracked as a follow-up if multi-agent schedule verification is needed later.
9
+ Generate a consolidated work schedule for all non-done tasks in a given `task-group`. The skill reads each task's `task-manifest.json` and `latestReport`, classifies tasks into phases by priority and risk, and writes a single Markdown plan file under `.okstra/tasks/<task-group>/schedule/`. It runs as a single Claude lead synthesis (lightweight mode); the frontmatter `model: opus` switches supporting harnesses to Opus-class for the turn — cross-task synthesis needs that reasoning depth.
14
10
 
15
11
  ## When to Use
16
12
 
17
13
  - User asks to generate a work schedule / plan / "일정" for an entire `task-group`
18
14
  - User wants a single document that summarizes all non-done tasks with effort, risk, and dependencies
19
- - A `task-group` exists in `.okstra/discovery/task-catalog.json` with at least one task whose `workStatus` is not `done`
20
15
 
21
- **Do NOT use** for single-task analysis (use `okstra-inspect status` instead) or when the user wants to actually execute one task (use the parent `okstra` skill).
16
+ **Do NOT use** for single-task analysis (use `okstra-inspect status`) or to execute one task (use `okstra-run`).
22
17
 
23
- ## Trigger Patterns (recognize both)
18
+ Explicit command form: `okstra schedule <task-group> [--title "<custom title>"] [--directive-file <abs-path>]`. If `--title` is omitted, derive a default from `task-group` (e.g. `uploadFont` → `uploadFont — Work Schedule`).
24
19
 
25
- **Natural language (Korean / English)**:
26
- - "uploadFont 작업 일정 만들어줘"
27
- - "uploadFont schedule 생성"
28
- - "uploadFont 일정 계획표"
29
- - "Generate schedule for uploadFont"
20
+ ## Step 0: Preflight
30
21
 
31
- **Explicit command**:
32
- - `okstra schedule <task-group>`
33
- - `okstra schedule <task-group> --title "<custom title>"`
34
- - `okstra schedule <task-group> --directive-file <abs-path>` (optional; see "Directive override" below)
22
+ Run one Bash tool call, starting with the literal token `okstra` (never wrapped in `if`/`eval`/`export`/`$(...)`/`VAR=...`/`||`/`&&`/`npx` — a non-literal leading token defeats the `Bash(okstra:*)` permission match):
23
+
24
+ ```bash
25
+ okstra preflight --runtime claude-code --json
26
+ ```
35
27
 
36
- If `--title` is omitted, derive a default title from `task-group` (e.g. `uploadFont` → `uploadFontWork Schedule`).
28
+ Parse the stdout JSON. `ok: true` carry `projectRoot` as a literal string and use it to locate `.okstra/discovery/task-catalog.json` and the task-group directory. `ok: false` → tell the user to run `/okstra-setup` first, then stop. If the call fails with `unknown command: preflight`, the `okstra` binary on PATH predates this skill tell the user to update it (`npm i -g okstra@latest`), then stop (`/okstra-setup` does not update the binary).
37
29
 
38
- ## Preflight: Verify okstra runtime + project setup
30
+ ## Audience & authority (READ FIRST drives everything below)
39
31
 
40
- Before anything else in this skill, run each of the following commands as a **separate Bash tool call**. Each command starts with the literal token `okstra` so the `Bash(okstra:*)` permission match succeeds. Do **not** wrap any of them in `if`, `eval`, `export`, `$(...)`, `VAR=...`, `||`, or `&&`, and do **not** introduce a `$OKSTRA_CMD` variable or an `npx -y okstra@latest` fallback — those leading tokens defeat the permission match and force a confirmation prompt on every call. The LLM (you) inspects each command's output and decides what to do next in natural language — never in shell.
32
+ **The schedule is a client-facing work plan.** It assumes the team has all permissions and can proceed without further approval. Even when the underlying per-task reports flag blocking items, missing approvals, or "사용자 확인 필요 항목", **the schedule MUST NOT surface them** — those belong in the internal report. Never emit a decision checklist, a `#### 사용자 확인 필요 항목` sub-section, `Done`/`Ready?`/`Blocking Decisions` columns, or checkbox lists; "Status" reflects work phase only.
41
33
 
42
- 1. `okstra ensure-installed --runtime claude-code`
43
- If this exits non-zero, tell the user: "okstra not installed — run `/okstra-setup` first." Then stop. Do **not** try to invoke `npx -y okstra@latest ...` as a fallback.
34
+ **Assume the user and their team hold full authority and every permission required.** External approvals, access grants, sign-off, and vendor coordination are treated as already satisfied unless a report names a concrete external dependency outside the user's control. Concretely:
44
35
 
45
- 2. `okstra check-project --json`
46
- Reads the project from the current working directory. Parse the JSON from stdout. The shape is `{ok, projectRoot, projectJsonPath, projectId}`.
36
+ - **Effort sizing & day totals** count engineering work only — strip approval-waiting / coordination buffers from source sizings (note the adjustment in `## Executive Summary` if material).
37
+ - **Gantt bars** represent engineering duration only; no dead-time gaps for approval cycles. `(after <TASK-ID>)` marks genuine engineering dependencies only.
38
+ - **Risk Mitigation Strategy** lists real engineering risks (data loss, regression surface, rollback path) — permission/coordination items are dropped.
39
+ - **Recommended Immediate Actions / Next Action** are concrete engineering steps; "권한 확인", "승인 요청", "이해관계자 정렬" 류는 출력 금지.
40
+ - **Cross-Task Dependencies** covers engineering coupling only (shared modules, release order, package versions).
47
41
 
48
- - `ok: false` tell the user: "this project has no okstra setup. Run `/okstra-setup` first." Then stop.
49
- - `ok: true` → carry `projectRoot` as a literal string and use it to locate `.okstra/discovery/task-catalog.json` and the task-group directory.
42
+ **The schedule must be self-contained.** Opaque codes pulled from internal reports (`FC-5`, `UC-12`, `M1`, decision-item letters, …) must not appear unresolved. Choose one per identifier: **Form A** (≤3 codes) replace the code inline with a 5–20자 one-line description of the item; **Form B** (≥4 recurring codes) — keep the codes and emit a `## Glossary` table as the last section resolving every one. Decision-item letters (`A1`, `B2`, …) are approval items and may not appear at all. TASK-IDs listed in `## At a Glance` need neither.
50
43
 
51
- Subsequent `okstra <subcmd>` calls self-bootstrap their Python path, so this skill never needs `okstra paths --shell` / `export PYTHONPATH=...`.
44
+ ## Contract SSOT template + validator
52
45
 
53
- ## Process Procedure
46
+ The installed template `~/.okstra/templates/reports/schedule.template.md` is the **byte-for-byte SSOT** for the output shape: frontmatter, top header block, the mandatory `##` heading list and order, per-task `Item / Detail` field labels and sub-section order, table column shapes, the ASCII Gantt format (relative day axis, plain fence, `█`/`░`/`! crit`/`est` legend), the dependency-graph shapes, and the optional `## Glossary` gate. **Read the template before writing the schedule and follow it exactly** — do not re-derive section shapes from memory. Headings and field labels stay English literals regardless of the source-report language; body prose is Korean. When a section has no data, render its heading with `_없음_` — never delete or reorder headings. Never emit mermaid or any graph DSL.
54
47
 
55
- ### Step 0: Verify model (권장 게이트)
48
+ `~/.okstra/lib/validators/validate-schedule.py` is the enforcement for all of the above (heading order, field labels, controlled vocabulary — e.g. `Med-High` is the canonical risk form — forbidden translations, checkbox bans, Gantt fence rules, unresolved-code detection). The Step 4 ambiguous-classification rationale line is the one rule the validator does not yet enforce — emit it yourself.
56
49
 
57
- This skill performs cross-task synthesis (multi-task classification, dependency reasoning, phase placement, Gantt/timeline assembly) which benefits substantially from Opus-class reasoning. The frontmatter `model: opus` field above instructs supporting Claude Code harness versions to switch automatically; if the harness ignores it, this gate catches the case explicitly.
50
+ Two computation rules the template scaffold cannot carry inline:
58
51
 
59
- 1. Inspect the active session model. The model is shown in the status line, accessible via `/model`, and embedded in the runtime context as the model name (e.g. `claude-fable-5`, `claude-opus-4-7`, `claude-sonnet-4-6`, `claude-haiku-4-5-*`).
60
- 2. If the active model is **Opus-class or above** (`claude-opus-*`, `claude-fable-*`): proceed to Step 1.
61
- 3. If the active model is **Sonnet or Haiku-class**: STOP and output the following message verbatim, then wait for user response:
62
- ```
63
- okstra-schedule는 Opus-class 모델에서 실행하는 것을 권장합니다 (현재: <active-model>).
64
- /model opus 로 전환 후 다시 호출하시거나, 'sonnet으로 진행' 이라고 명시하시면 그대로 실행합니다.
65
- ```
66
- 4. If the user explicitly insists on the lower model ("sonnet으로 진행", "그대로 진행", "force", or similar): proceed to Step 1, but prepend a single-line warning at the top of the generated schedule file: `> ⚠️ Generated with <model> (not Opus). Cross-task synthesis quality may be reduced.`
52
+ - **Effort-to-Day mapping**: day ranges per size are defined once in the template's `### Effort Sizing 기준` table. For the At a Glance totals line, sum that table's lower bounds across in-scope tasks for the lower total and upper bounds for the upper total.
53
+ - **`[NEEDS-OKSTRA-RUN]` / `[PARSE-ERROR: <section>]` placement**: put the marker immediately under the per-task heading inside its Phase section and fill only the available fields — never relocate it to a sibling section.
67
54
 
68
- This is a recommendation gate, not a blocking mechanism — the override path in step 4 always lets the run proceed.
55
+ ## Procedure
69
56
 
70
57
  ### Step 1: Resolve task-group and collect tasks
71
58
 
72
59
  1. Read `.okstra/discovery/task-catalog.json`.
73
- 2. **Normalise the user-supplied `<task-group>` argument:** lowercase it, then strip every character that is not `[a-z0-9]` (drop spaces, hyphens, underscores, dots, etc.). Apply the same transform to each entry's `taskGroupPathSegment`. Match when the two normalised forms are equal. This is the single comparison rule do NOT also fall back to the raw `taskGroup` field.
60
+ 2. **Normalise the user-supplied `<task-group>` argument:** lowercase it, then strip every character that is not `[a-z0-9]`. Apply the same transform to each entry's `taskGroupPathSegment`. Match on equality this is the single comparison rule; do NOT also fall back to the raw `taskGroup` field.
74
61
  3. If no tasks found, output `해당 task-group을 찾을 수 없습니다.` and stop.
75
62
  4. For each matched task, read `.okstra/tasks/<task-group-segment>/<task-id-segment>/task-manifest.json` directly. Catalog data may be stale; the manifest is authoritative.
76
- 5. **Derive `<project-id>`** for the schedule header: prefer `task-catalog.json`'s top-level `projectId` field if present, otherwise use the first matched manifest's `projectId` field. Do not invent a value.
63
+ 5. **Derive `<project-id>`** for the header: prefer `task-catalog.json`'s top-level `projectId`, otherwise the first matched manifest's `projectId`. Do not invent a value.
77
64
 
78
65
  ### Step 2: Filter by workStatus
79
66
 
80
- `workStatus` is also surfaced directly inside each catalog entry, but Step 1 still re-reads each `task-manifest.json` because the manifest is authoritativeno extra fetch is needed beyond that.
67
+ For inference when `workStatus` is missing or empty, defer to the inference table in `skills/okstra-inspect/SKILL.md` (`status.4` "Default value convention")do not duplicate it here. Then filter: resolved `done` → exclude; everything else (`todo` / `in-progress` / `blocked` / `phase-done` / inferred non-done) → include.
81
68
 
82
- For inference rules when `workStatus` is missing or empty, **defer to the inference table in `skills/okstra-inspect/SKILL.md` (`status.4` "Default value convention")** instead of duplicating it here. Apply that table to derive a working value, then filter:
83
-
84
- - If the resolved value is `done` (set by the user OR inferred via `currentStatus == "completed"` + terminal next-phase) → exclude.
85
- - Otherwise (`todo` / `in-progress` / `blocked` / `phase-done` / explicit user value / inferred non-done) → include.
86
-
87
- If after filtering 0 tasks remain, output:
88
- ```
89
- 해당 task-group의 모든 task가 done 상태입니다. 생성할 schedule이 없습니다.
90
- ```
91
- Then stop without creating a file.
69
+ If 0 tasks remain, output `해당 task-group의 모든 task가 done 상태입니다. 생성할 schedule이 없습니다.` and stop **without creating a file**.
92
70
 
93
71
  ### Step 3: Per-task data extraction
94
72
 
95
- For each included task, gather:
96
-
97
- **From `task-manifest.json`**:
98
- - `taskId`, `taskGroup`, `taskKey`
99
- - `workCategory`
100
- - `workflow.currentPhase`, `workflow.currentPhaseState`
101
- - `taskType`
102
- - `workStatus`
103
- - `latestReportPath`
104
-
105
- **From the report file (`latestReportPath`)**, parse:
106
- - Title / Problem statement
107
- - Solution / Architecture
108
- - Work Breakdown (table)
109
- - Verification Commands
110
- - Rollback strategy
111
- - Effort, Risk, Priority, Scope, Repos
112
-
113
- Note: per-task reports may include "사용자 확인 필요 항목" / blocking items / approval requests. **Do not extract or surface these in the schedule** — the schedule is a client-facing work plan. Internal blockers stay in the source report.
73
+ From `task-manifest.json`: `taskId`, `taskGroup`, `taskKey`, `workCategory`, `workflow.currentPhase`, `workflow.currentPhaseState`, `taskType`, `workStatus`, `latestReportPath`.
114
74
 
115
- If the report file does not exist or cannot be parsed:
116
- - **Before tagging `[NEEDS-OKSTRA-RUN]`, attempt the manifest-staleness fallback** (manifest pointer can become stale when an interleaved `--render-only` prep run advances `latestReportPath` to a path that will never be written):
117
- 1. Resolve `<task-root>` from `task-manifest.json` → `taskRootPath`.
118
- 2. List `<task-root>/runs/<taskType>/reports/final-report-*.md` and pick the **newest existing file by mtime** (NOT by filename timestamp — render-only prep can create entries whose timestamp is later than the last completed run's report). If none exists, also try `<task-root>/runs/*/reports/final-report-*.md` across all task-types as a last-resort.
119
- 3. If a candidate is found, parse it and proceed normally. Append a one-line note at the top of that task's per-task block in the schedule output:
120
- `> _Report fallback: manifest pointed at non-existent <pointed-name>, used <actual-name> (mtime <ts>) instead. Manifest may be stale due to a render-only prep — see Phase 6 of okstra contract._`
121
- 4. If no candidate file exists either, only then fall through to `[NEEDS-OKSTRA-RUN]`.
122
- - If still unresolvable: use only manifest metadata, mark the task entry with `[NEEDS-OKSTRA-RUN]`, and do not abort — continue with remaining tasks.
75
+ From the report file (`latestReportPath`): Title / Problem statement, Solution / Architecture, Work Breakdown table, Verification Commands, Rollback strategy, Effort, Risk, Priority, Scope, Repos. Blocking items / approval requests in the report are **not** extracted (see Audience).
123
76
 
124
- If parsing fails for a specific section:
125
- - Mark the entry with `[PARSE-ERROR: <section>]` and continue.
77
+ If the report file does not exist or cannot be parsed, attempt the **manifest-staleness fallback** before tagging (`latestReportPath` can go stale when an interleaved `--render-only` prep advances it to a path that is never written):
126
78
 
127
- ### Step 4: Synthesis
79
+ 1. Resolve `<task-root>` from the manifest's `taskRootPath`.
80
+ 2. List `<task-root>/runs/<taskType>/reports/final-report-*.md` and pick the **newest existing file by mtime** (NOT by filename timestamp). If none, try `<task-root>/runs/*/reports/final-report-*.md` across all task-types.
81
+ 3. If found, parse it and add one line at the top of that task's block: `> _Report fallback: manifest pointed at non-existent <pointed-name>, used <actual-name> (mtime <ts>) instead._`
82
+ 4. Only if no candidate exists, tag the entry `[NEEDS-OKSTRA-RUN]`, use manifest metadata only, and continue with the remaining tasks. If parsing fails for one section, tag `[PARSE-ERROR: <section>]` and continue.
128
83
 
129
- Claude lead synthesises the schedule directly using collected data. Proceed to Step 5.
84
+ ### Step 4: Phase classification
130
85
 
131
- ### Step 5: Phase classification heuristic
132
-
133
- Classify each task into one of three phases based on report data.
134
-
135
- **`workCategory` accepted values** — the category keys come from `scripts/okstra_ctl/worktree.py::_WORK_CATEGORY_PREFIX` (code source), plus the `unknown` fallback emitted by `render.py`. The phase mapping below is layered on top: when a category is added to `worktree.py`, add a row here too — otherwise the new category falls through to `unknown` → Phase 2.
86
+ `workCategory` keys come from `scripts/okstra_ctl/worktree.py::_WORK_CATEGORY_PREFIX` (code source) plus the `unknown` fallback from `render.py`. When a category is added there, add a row here — otherwise it falls through to `unknown` → Phase 2.
136
87
 
137
88
  | workCategory | Default phase |
138
89
  |--------------|---------------|
139
- | `bugfix` | Phase 1 (when risk is High/Med-High); otherwise Phase 2 |
140
- | `feature` | Phase 2 |
141
- | `improvement` | Phase 2 |
142
- | `refactor` | Phase 3 |
143
- | `ops` | Phase 3 |
144
- | `docs` / `doc` | Phase 2 |
145
- | `unknown` (or unmatched / missing) | **Phase 2**, with a one-line rationale `> _workCategory '<raw-value>' 미정의 — Phase 2로 기본 분류._` at the top of that phase section |
90
+ | `bugfix` | Phase 1 when risk is High/Med-High; otherwise Phase 2 |
91
+ | `feature` / `improvement` / `docs` / `doc` | Phase 2 |
92
+ | `refactor` / `ops` | Phase 3 |
93
+ | `unknown` (or unmatched / missing) | Phase 2, with rationale `> _workCategory '<raw-value>' 미정의 — Phase 2로 기본 분류._` at the top of that phase section |
94
+
95
+ Priority overrides category: `P0` Phase 1; `P1`/`P2` Phase 2; `P3` or multi-repo + infrastructure scope → Phase 3. When still ambiguous, place the task in the closest phase and add a one-line rationale at the top of that phase section (not validator-enforced — emit it yourself).
146
96
 
147
- Priority overrides category:
97
+ ### Step 5: Gantt decision (render by default)
148
98
 
149
- - **Phase 1 (안정성/Critical)**: priority `P0`, OR `workCategory == "bugfix"` with High / Med-High risk
150
- - **Phase 2 (개선/기능)**: priority `P1` or `P2`, OR `workCategory in {"feature", "improvement", "docs", "doc"}`, OR fallback per the table above
151
- - **Phase 3 (확장/아키텍처)**: priority `P3`, OR `workCategory in {"refactor", "ops"}`, OR multi-repo + infrastructure scope
99
+ `## Gantt Chart` is **rendered by default** — skip ONLY when literally no day signal exists (every task is effort=XXL with no visible decomposition, or all tasks lack both effort sizing and decomposition). Render whenever any of these hold: 2+ tasks with effort sizing; 1 task whose effort yields a range (mid-point bar, or `lo`/`hi` two-bar form); 1 task with Part/Phase/Step decomposition in the source (bars at decomposition-unit level); total estimated effort ≥ 3 days. When per-unit day allocations aren't itemized, split the parent range across the visible units yourself and append the `est` annotation (or add `> 일별 배분은 추정치이며 차단 항목 해소 후 갱신 권장.`). "Range is wide", "single task", "user decisions pending" are NOT skip reasons — render an estimate-tagged chart instead.
152
100
 
153
- When the classification is genuinely ambiguous after applying the table + priority override, place the task in the closest phase and add a one-line rationale at the top of that phase section.
101
+ When you do skip, insert in the section's position exactly: `> _Gantt Chart 생략: <concrete reason referencing the actual data>._`
154
102
 
155
- > Note: enum codification (turning this de-facto list into a `validators/`-enforced contract) is out of scope for this skillfile as a follow-up if needed.
103
+ **Directive override (highest priority).** Before applying the heuristic, check for a `## Directive` section, first hit wins: (1) the `--directive-file <abs-path>` argument; (2) `<PROJECT_ROOT>/.okstra/tasks/<task-group-segment>/schedule/instruction-set/analysis-material.md`; (3) none → apply the default heuristic silently. A found directive overrides the render/skip heuristic for the affected section note it inline as `> _Per Directive directive: <verbatim short excerpt>._` — and its pre-supplied day allocations / phase weights are used verbatim as bar lengths. A directive file without a `## Directive` heading counts as "no directive".
156
104
 
157
105
  ### Step 6: Write the schedule file
158
106
 
159
- Output path:
160
107
  ```
161
108
  .okstra/tasks/<task-group-segment>/schedule/<task-group-segment>-plan-<YYYY-MM-DD_HH-MM-SS>.md
162
109
  ```
163
110
 
164
- - `<task-group-segment>`: from manifest's `taskGroupPathSegment` (lowercase, hyphenated).
165
- - Timestamp: current local time at generation, format `YYYY-MM-DD_HH-MM-SS`.
166
- - Auto-create the parent directory if it does not exist.
111
+ `<task-group-segment>` comes from the manifest's `taskGroupPathSegment` verbatim. Timestamp is current local time. Auto-create the parent directory. Never overwrite an existing file — on a same-second collision append `-2`, `-3`, ….
167
112
 
168
- ### Step 7: Completion message (Korean)
113
+ ### Step 7: Self-validate before reporting completion
169
114
 
170
- After writing the file, reply briefly:
115
+ 1. Re-read the file you just wrote.
116
+ 2. Run `python3 ~/.okstra/lib/validators/validate-schedule.py <output-path>`.
117
+ 3. If it exits non-zero, fix the file and re-validate. Do not print the Step 8 message until it passes.
118
+ 4. If the validator is not installed, fall back to a manual check against the template: every template `##` heading present with exact spelling, title ends with `— Work Schedule`, `> Generated:` block present.
119
+
120
+ ### Step 8: Completion message (Korean)
171
121
 
172
122
  ```
173
123
  ✓ Schedule 생성 완료: <relative-path>
@@ -177,325 +127,20 @@ After writing the file, reply briefly:
177
127
  - 모드: lightweight
178
128
  ```
179
129
 
180
- ## Audience (READ FIRST — drives everything below)
181
-
182
- **The schedule is a client-facing work plan.** It assumes the team has all permissions and can proceed without further approval. Even when the underlying per-task reports flag blocking items, missing approvals, or "사용자 확인 필요 항목", **the schedule MUST NOT surface them**. Those concerns belong in the internal report, not the deliverable.
183
-
184
- ### Authority & permissions assumption (schedule sizing)
185
-
186
- **Assume the user (and their team) holds full authority and every permission required for every included task.** External approvals, third-party access grants, role/IAM permissions, organisational sign-off, legal/compliance review, vendor coordination, and "verify access exists" steps are treated as already satisfied unless the source per-task report explicitly identifies a concrete external dependency outside the user's control.
187
-
188
- Concretely, when computing this schedule:
189
-
190
- - **Effort sizing & day totals**: Count engineering work only. Do NOT inflate `S/M/L/XL` or the `<X.X> ~ <Y.Y> days` total for permission-checking, approval-waiting, stakeholder-coordination, or "권한 확인" buffer time. If a per-task report's effort sizing already bakes in such buffers, strip them when synthesising the schedule total and note the adjustment inside `## Executive Summary` if material.
191
- - **Gantt Chart**: Bars represent engineering duration only. Do not insert dead-time gaps for approval cycles or coordination wait. Sequential `(after <TASK-ID>)` annotations remain valid only for genuine engineering dependencies.
192
- - **Risk Mitigation Strategy**: Permission/authority/external-coordination items are NOT acceptable risk strategies — drop them. Real risks (data loss, regression surface, rollback path, performance regression, etc.) only.
193
- - **Recommended Immediate Actions** and **Execution Priority Matrix → Next Action**: Every action is a concrete engineering step (write test, modify file, run command). "권한 확인", "승인 요청", "이해관계자 정렬" 같은 항목은 출력 금지.
194
- - **Cross-Task Dependencies & Shared Concerns**: Only engineering coupling (shared modules, release order, package versions). Cross-team coordination notes inherited from per-task reports MUST be filtered out.
195
-
196
- This rule is independent of the existing client-facing audience rule — even if a future variant of this skill chose to surface decision items, the authority assumption still suppresses external-permission accounting.
197
-
198
- Concretely:
199
- - No "Consolidated User Decision Checklist" section.
200
- - No per-task "사용자 확인 필요 항목" sub-section.
201
- - No `Done` / `Ready?` / `Blocking Decisions` columns anywhere.
202
- - No checkboxes (`[ ]` / `[x]`) — bullet lists only.
203
- - "Status" field reflects work phase only; never bubble up "blocked" rationale prose.
204
-
205
- **The schedule must be self-contained.** Every identifier the client reads must be resolvable from the schedule itself. Cross-document opaque codes pulled from internal reports — `FC-5`, `UC-12`, `TC-3`, `RC-2`, `M1`, `A1`, `B2`, `C3`, decision-item letters, finding codes, milestone codes — **MUST NOT appear unresolved**. The client cannot look them up against the source report, and a bare code adds zero value to a delivery plan.
206
-
207
- **Two acceptable forms — choose one per identifier:**
208
-
209
- **Form A — inline replacement (preferred for ≤3 codes).** Replace the identifier with a brief description of that item's content (한 줄 요약, 5–20자 내외). Drop the code entirely; do not write code-and-content together.
210
-
211
- | Source report | ❌ Forbidden | ✅ Form A |
212
- |---------------|--------------|-----------|
213
- | `FC-5: 결제 게이트웨이 타임아웃 처리 누락` | `FC-5 수정` / `FC-5 (결제 타임아웃)` | `결제 게이트웨이 타임아웃 처리` |
214
- | `M2: 1차 베타 배포` | `M2 도달` | `1차 베타 배포` |
215
- | `UC-3: 폰트 업로드 흐름` | `UC-3 검증` | `폰트 업로드 흐름 검증` |
216
-
217
- **Form B — `## Glossary` at document bottom (preferred when ≥4 codes recur).** Keep the bare identifier in the body **only if** the document also emits a `## Glossary` section as the last `##` section (after `## Recommended Immediate Actions`). The glossary MUST resolve every opaque code that appears in the body. Format:
218
-
219
- ```markdown
220
- ## Glossary
221
-
222
- | Code | Description |
223
- |------|-------------|
224
- | FC-5 | 결제 게이트웨이 타임아웃 처리 누락 |
225
- | M2 | 1차 베타 배포 |
226
- | UC-3 | 폰트 업로드 흐름 |
227
- ```
228
-
229
- Rules for Form B:
230
- - Header literal `| Code | Description |` is required.
231
- - Every opaque code referenced anywhere in the body MUST have a glossary row. The validator flags any unresolved code.
232
- - Decision-item letters (`A1`, `B2`, `C3`, `D4`) — these are blocking/approval items that MUST NOT appear in the schedule at all (see "client-facing" rule above). The glossary cannot whitewash them.
233
-
234
- The only cross-reference that needs neither inlining nor a glossary entry is a TASK-ID that itself appears in the `## At a Glance` table.
235
-
236
- The validator rejects any of these patterns.
237
-
238
- ## Section Contract (required)
239
-
240
- The canonical template lives at `~/.okstra/templates/reports/schedule.template.md`. **Read it before writing the schedule** and follow it byte-for-byte for headings.
241
-
242
- ### MUST — exact ordered heading list
243
-
244
- The output file MUST contain exactly these top-level (`##`) sections, in this order, with these exact English headings (verbatim, no translation, no rewording, no extra punctuation):
245
-
246
- 1. `## At a Glance`
247
- 2. `## Executive Summary` (with subsection `### Effort Sizing 기준`)
248
- 3. `## Task Dependency Graph`
249
- 4. `## Phase 1: Critical Fixes`
250
- 5. `## Phase 2: Enhancements`
251
- 6. `## Phase 3: Architecture`
252
- 7. `## Execution Priority Matrix`
253
- 8. `## Cross-Task Dependencies & Shared Concerns`
254
- 9. `## Risk Mitigation Strategy`
255
- 10. `## Recommended Immediate Actions`
256
-
257
- **Optional sections** (allowed at fixed positions only):
258
- - `## Gantt Chart` — between `## Task Dependency Graph` and `## Phase 1: Critical Fixes`.
259
- - `## Glossary` — last `##` section, after `## Recommended Immediate Actions`. Required when the body contains any opaque code (FC-N, UC-N, M-N, …) using Form B; forbidden in any other position.
260
-
261
- If a phase has zero tasks, keep its heading and write `_없음_` underneath. **Never delete a heading.** **Never reorder.**
262
-
263
- ### SHOULD — graphic section (English heading only)
264
-
265
- The following section is the only graphic output and MUST appear in this position (between `## Task Dependency Graph` and `## Phase 1: Critical Fixes`), with this exact English heading literal:
266
-
267
- - `## Gantt Chart` — render as a fenced **ASCII** chart inside a plain ```` ``` ```` block (no language tag, no `mermaid`). See "ASCII Gantt format" below.
268
-
269
- #### ASCII Gantt format (REQUIRED — mermaid is forbidden)
270
-
271
- Render the chart as a monospace text block. **The horizontal axis MUST be a relative day count starting at Day 1 (or J1) — never calendar dates from "today" or any other absolute date.** The schedule describes effort/duration in days, not a wall-clock plan; rendering `2026-05-04`, `Mon May 4`, etc. is a contract violation. Use this structure exactly:
272
-
273
- ````
274
- ```
275
- Day: 1 5 10 15 20 25 30
276
- | | | | | | |
277
- Phase 1
278
- <TASK-ID> (<S/M/L/XL>) ██████ ! crit
279
- <TASK-ID> (<S/M/L/XL>) ████████
280
- Phase 2
281
- <TASK-ID> (<S/M/L/XL>) ██████░░ est
282
- Phase 3
283
- <TASK-ID> (<S/M/L/XL>) ████
284
- ```
285
- ````
286
-
287
- Rules:
288
- - Fence the block with plain ```` ``` ```` (NOT ```` ```mermaid ````, NOT ```` ```gantt ````, NOT ```` ```text ````). The fence MUST have no language hint so downstream validators can detect the format.
289
- - The first line is a `Day:` axis with **relative day numbers only** (1, 2, 3, … or J1, J2, J3, …). Do NOT substitute calendar dates, weekdays, ISO timestamps, or "today + N" labels — even if the user's environment shows today's date. Choose a tick interval (1, 2, or 5 days) so the axis fits within ~80 columns; align tick labels with `|` markers on the second line.
290
- - Day 1 is the start of Phase 1 (the first scheduled task). Subsequent task start columns are computed from cumulative durations, NOT from any external calendar.
291
- - Group bars under `Phase 1` / `Phase 2` / `Phase 3` headings (omit a phase if it has no tasks — but keep the order). Indent task rows by two spaces.
292
- - Each task row is ` <TASK-ID> (<size>)` left-aligned in a fixed-width label column (pad with spaces so all bars start in the same column), followed by spaces to position the bar at its start day, then the bar.
293
- - Use `█` for solid (committed) duration and `░` for the upper-bound / uncertainty tail when the effort sizing has a range. A single bar at mid-point is also acceptable.
294
- - Append annotations after the bar separated by spaces:
295
- - `! crit` for critical-path items (replaces mermaid's `:crit,`).
296
- - `est` when day allocations are estimated (replaces `:est,`).
297
- - `(after <TASK-ID>)` to mark a sequential dependency when not visually obvious.
298
- - Keep total width ≤ 100 columns. If span exceeds the axis, increase the tick interval rather than wrapping.
299
- - When effort is XXL ("세분화 필요") and no decomposition exists, follow the skip-reason rule instead of inventing bars.
300
-
301
- #### Render-by-default rule (REVERSED from prior contract)
302
-
303
- These sections are **rendered by default**. Skip them ONLY when literally no usable day data exists anywhere in the source. Specifically:
304
-
305
- **`## Gantt Chart` — render whenever ANY of these hold:**
306
- - 2+ tasks with any effort sizing (S/M/L/XL — all map to day ranges via the Effort Sizing 기준 table)
307
- - 1 task whose Effort sizing yields any range (use range mid-point as a single bar, or render two bars `lo` and `hi` for visible uncertainty)
308
- - 1 task with explicit Part/Phase/Step decomposition in the source report (look for headings like "Part 1", "Phase 1", "Step N", "Quick Wins + CDN Architecture" etc.) — render bars at the **decomposition unit** level, even if internal day allocations are estimates
309
- - Any case where total estimated effort is ≥ 3 days
310
-
311
- When per-decomposition day allocations aren't crisply itemized, **estimate them yourself by splitting the parent effort range across the visible decomposition units** and append the `est` annotation after the bar (see ASCII Gantt format below) or add a one-line note `> 일별 배분은 추정치이며 차단 항목 해소 후 갱신 권장.` The reader benefits more from a rough visual than from no visual.
312
-
313
- **Skip is permitted ONLY when:**
314
- - Every task has effort=XXL ("세분화 필요") AND no decomposition is visible in the source — i.e. there is genuinely no day signal to plot
315
- - All included tasks lack BOTH effort sizing AND any decomposition
316
-
317
- When skipping, follow the skip-reason note rule below — but the skip should be rare. "Range is wide" or "user decisions pending" or "single task" are NOT acceptable skip reasons by themselves; render an estimate-tagged chart instead.
318
-
319
- #### Directive override (highest priority)
320
-
321
- Before applying the heuristics above, **check the schedule-level directive source** for a `## Directive` section.
322
-
323
- **Resolution order (first hit wins):**
324
-
325
- 1. Absolute path passed via the `--directive-file <abs-path>` argument when invoking the skill.
326
- 2. `<PROJECT_ROOT>/.okstra/tasks/<task-group-segment>/schedule/instruction-set/analysis-material.md` (the canonical schedule-level instruction-set location — okstra writes here when a schedule run is dispatched with `--directive`).
327
- 3. **No directive file** — fall through and apply the default heuristic without any override. This is the normal case; do not warn, do not block.
328
-
329
- If a directive source is found and contains a `## Directive` section that affects Gantt rendering — e.g. "render a Gantt even with single XL task", "no Gantt needed" — that directive **overrides the heuristic and the skip-reason rule** for the affected section. When following a Directive override that contradicts the default heuristic, append a one-line note inside the rendered (or skipped) section: `> _Per Directive directive: <verbatim short excerpt>._` so the reader can trace why the section appeared/disappeared against the default. The Directive may also pre-supply day allocations, phase weights, or sub-task decompositions — use those verbatim as bar lengths in the Gantt.
330
-
331
- A directive file that exists but contains no `## Directive` heading is treated as "no directive" — fall through to the heuristic, no warning required.
332
-
333
- `## Gantt Chart` is the only `##` section that MAY be added beyond the 10 mandatory ones. Any other extra `##` heading is forbidden.
334
-
335
- #### MUST — emit a skip-reason note when omitting Gantt Chart (rare case)
336
-
337
- Skipping should be **rare** under the render-by-default rule above. When you do skip — only when the strict skip-permitted conditions hold — insert a one-line blockquote in the section's position (between `## Task Dependency Graph` and `## Phase 1: Critical Fixes`), in this exact shape:
338
-
339
- ```
340
- > _Gantt Chart 생략: <concrete reason referencing the actual data>._
341
- ```
342
-
343
- The reason MUST reference the actual data and must be one of the legitimate skip cases. Acceptable example:
344
-
345
- - `> _Gantt Chart 생략: 모든 task가 effort=XXL (세분화 필요), day 추정 산출 불가._`
346
-
347
- **NOT acceptable as skip reasons** (these mean you should render an estimate-tagged chart instead):
348
- - "단일 task" — render with the task's effort range as bars
349
- - "effort range가 넓음 (XL 5-15d)" — render mid-point or `lo`/`hi` two-bar form
350
- - "사용자 결정 전 확정 불가" — render with `est` annotation and a `> 일별 배분은 추정치` note
351
- - "Part/Phase 내부 day 배분 미정" — split the parent range across visible decomposition units
352
-
353
- This rule exists because rendering an estimated chart with a clear "estimate" tag is **always** more useful than no chart at all.
354
-
355
- ### MUST — top header (verbatim shape)
356
-
357
- ```markdown
358
- # <Title> — Work Schedule
359
-
360
- > Generated: <YYYY-MM-DD HH:MM> | Project: <project-id> | Task Group: <task-group>
361
- > Source: okstra <mode> (<N> tasks included, <M> done excluded)
362
- ```
363
-
364
- The title suffix MUST be `— Work Schedule` (em dash, English). Korean titles like `작업 일정 계획서` are forbidden.
365
-
366
- ### MUST — per-task block field set
367
-
368
- Every per-task block MUST include the `Item / Detail` table with these field rows in this order: `**Category**`, `**Priority**`, `**Effort**`, `**Status**`, `**Risk**`, `**Scope**`, `**Repo**`. None may be omitted, renamed, or translated.
369
-
370
- ### MUST — per-task block sub-section order
371
-
372
- After the `Item / Detail` table, every per-task block MUST emit these sub-sections in this exact order — no insertions, no reordering, no omissions (unless the block is tagged `[NEEDS-OKSTRA-RUN]` / `[PARSE-ERROR: …]`):
373
-
374
- 1. `**Problem**:`
375
- 2. `**Solution**:`
376
- 3. `**Work Breakdown**:` (followed by the `| Step | File | Action | Detail |` table)
377
- 4. `**Verification Commands**:` (followed by a fenced ` ```bash ` block)
378
- 5. `**Rollback**:`
379
-
380
- The validator enforces order by scanning each `### N-i.` block. A missing section or an out-of-order section is a contract violation. **Never emit `#### 사용자 확인 필요 항목`** — this is a client-facing schedule (see "Audience" above).
381
-
382
- ### MUST — controlled vocabulary (enums)
383
-
384
- The following columns/labels accept ONLY the listed values — anywhere they appear (At a Glance table, per-task `Item / Detail` table, Execution Priority Matrix). Free-text is forbidden; if the source data is missing, leave the cell empty (`–` is also accepted by the validator only inside per-task `Item / Detail`, not in `## At a Glance`).
385
-
386
- | Field | Allowed values |
387
- |-------|----------------|
388
- | **Effort** | `S`, `M`, `L`, `XL`, `XXL` (leading token only — trailing scope hint like `L (5-15 files)` is allowed inside parens) |
389
- | **Priority** | `P0`, `P1`, `P2`, `P3` |
390
- | **Risk** | `Very Low`, `Low`, `Medium`, `Med-High`, `High` |
391
- | **Phase** | `1`, `2`, `3` |
392
-
393
- `Med-High` (hyphenated) is the canonical form — `Medium-High` / `중상` / `Moyen-Élevé` are all rejected.
394
-
395
- ### MUST — At a Glance totals line
396
-
397
- The At a Glance section MUST contain exactly one totals line in this shape (single line, all bold):
398
-
399
- ```
400
- **총 <N>개 task / 예상 소요: <X.X> ~ <Y.Y> days (Effort 합산)**
401
- ```
402
-
403
- `<N>` is the included-task count, `<X.X>` and `<Y.Y>` are the lower/upper sums of the Effort-to-Day mapping. The validator checks the literal pattern.
404
-
405
- ### MUST — table & list shapes (no checkboxes anywhere)
406
-
407
- The schedule is a client-facing deliverable, not an internal todo board. **No table or list may contain `[ ]` / `[x]` checkbox cells, and no `Done` / `Ready?` / `Blocking Decisions` columns are permitted.**
408
-
409
- Required shapes:
410
-
411
- - `## Execution Priority Matrix` — header `| Priority | Task | Effort | Risk | Next Action |`. No leading `Done` column, no `Ready?`, no `Blocking Decisions`.
412
- - `## Recommended Immediate Actions` — markdown bullet list (`- <action>`). Numbered lists and checkbox lists are both rejected.
413
- - `## Risk Mitigation Strategy` — numbered list (it states *strategies*, not actions to tick off).
414
-
415
- ### MUST NOT — forbidden patterns
416
-
417
- | Pattern | Why forbidden |
418
- |---------|---------------|
419
- | Translating any `##` heading to Korean (`전체 개요`, `요약`, `위험 완화 전략`, `즉시 권장 액션`, etc.) or any other language | Heading literals are stable identifiers used by the validator and downstream tooling |
420
- | Adding sections not in the contract (`## Effort-to-Day 매핑`, `## Référentiel …`, etc. — anything beyond the 10 mandatory + the 1 optional `## Gantt Chart`) | Sizing tables belong inside `### Effort Sizing 기준` (Executive Summary). All other graphic detail belongs inside per-phase prose |
421
- | Translating the optional graphic section (`## Gantt 다이어그램`, `## Diagramme de Gantt`) | Use `## Gantt Chart` literally — translation is forbidden |
422
- | Adding `## Cumulative Timeline` (or any translated form: `## 누적 일정표`, `## Calendrier Cumulé`) | The Cumulative Timeline section has been removed from the contract — never emit it |
423
- | Using calendar dates / weekdays / "today + N" labels in the Gantt axis (e.g. `2026-05-04`, `Mon May 4`, `오늘 + 3일`) | The axis MUST be relative day-counts (`Day 1`, `J1`, …) — the schedule describes effort, not a wall-clock plan |
424
- | Using French (`Charge estimée`, `Détail`, `Catégorie`, `Priorité`, `Risque`, `Dépôt`, `Vue d'Ensemble`, `Résumé Exécutif`, etc.) anywhere in headings or field labels | Body prose is Korean, identifiers/headings/field labels are English literals. No third language anywhere structural |
425
- | **Mirroring the source-report language into headings** — e.g. source analyse-report is in French → headings become French; source is heavily Korean → headings become Korean | Body prose may match the source language (the SKILL allows Korean prose), but the 10 mandatory + 1 optional `##` heading literals and the 7 per-task field labels remain English ALWAYS, regardless of source-report language |
426
- | Translating per-task field labels (`출처`, `상세`, `위험`, `Repo별 영향` already-translated label OK only as table column header in `At a Glance` rows) | Field labels are part of the contract |
427
- | Emitting `## Consolidated User Decision Checklist` or `#### 사용자 확인 필요 항목` | Forbidden — schedule is a client-facing deliverable; blocking/approval/decision items belong in the internal report |
428
- | Adding `Done` / `Ready?` / `Blocking Decisions` columns to any table, or `[ ]` / `[x]` checkbox cells in any list | Forbidden for the same reason — see "Audience" section |
429
- | Replacing `Item / Detail` per-task table headers with translated versions | The literal `Item / Detail` is required |
430
- | Silently omitting `## Gantt Chart` without inserting the `> _Gantt Chart 생략: …_` blockquote in its position | Without the skip-reason line the reader cannot tell whether the section was forgotten, suppressed by a bug, or correctly skipped — every output must either render the chart or explicitly account for the skip |
431
- | Skipping `## Gantt Chart` because the data is "wide range / single task / part-day allocation pending" | Per the render-by-default rule, these are reasons to render an **estimate-tagged** chart, not to skip. Skip is only legitimate when day data literally does not exist (all-XXL or no effort sizing anywhere) |
432
- | Rendering Gantt as a mermaid (`` ```mermaid `` / `gantt` block) or any other graph DSL (PlantUML, Graphviz, etc.) | This skill renders ASCII only. Mermaid output is forbidden — use the ASCII Gantt format described above. Validators may reject mermaid blocks under this heading |
433
-
434
- ## Known Validator Gaps (out of scope here)
435
-
436
- - The ambiguous-classification rationale required by Step 5 (`> _workCategory '<raw>' 미정의 …_` / nearest-phase rationale) is **not** currently enforced by `validators/validate-schedule.py`. The skill is responsible for emitting it; closing the validator gap requires a validator change and is tracked as a follow-up.
437
-
438
- ## Self-Validation Before Reporting Completion
439
-
440
- After writing the file and before printing the completion message in Step 7, you MUST:
441
-
442
- 1. **Re-read the file** you just wrote.
443
- 2. **Run the validator**:
444
- ```bash
445
- python3 ~/.okstra/lib/validators/validate-schedule.py <output-path>
446
- ```
447
- 3. If the validator exits non-zero, **fix the file and re-validate**. Do not print the completion message until the validator passes.
448
- 4. If `~/.okstra/lib/validators/validate-schedule.py` is not present, fall back to a manual checklist: confirm each of the 10 mandatory headings from `### MUST — exact ordered heading list` (above) appears in the file with that exact spelling, the title ends with `— Work Schedule`, and the metadata `> Generated:` block is present.
449
-
450
- ## Schedule Document Template
451
-
452
- The installed template `~/.okstra/templates/reports/schedule.template.md` is the byte-for-byte SSOT for section ordering, heading spelling, table columns, and per-section HTML-comment guidance (Dependency Graph Shape A/B, Gantt rules, optional Glossary gate). Do not omit sections; if data is unavailable, render the section with `_없음_` rather than skipping it.
453
-
454
- The two rules below complement the template — they encode COMPUTATION that the template scaffold cannot carry inline.
455
-
456
- ### Effort-to-Day mapping (At a Glance total)
457
-
458
- Day-range values per size are defined once in the template's `### Effort Sizing 기준` table (`~/.okstra/templates/reports/schedule.template.md`) — that table is the SSOT. To compute the At a Glance total, read its `Day(s)` column and sum the lower bounds across all in-scope tasks for the lower total, the upper bounds for the upper total.
459
-
460
- ### `[NEEDS-OKSTRA-RUN]` / `[PARSE-ERROR: <section>]` placement
461
-
462
- When a task entry is tagged with one of these markers, place the marker immediately under the per-task heading inside the corresponding Phase section and fill only the available fields. Do NOT relocate it to a sibling section.
463
-
464
130
  ## Edge Cases
465
131
 
466
132
  | Case | Handling |
467
133
  |------|----------|
468
- | `workStatus` field absent or empty | Resolve via the `okstra-inspect status.4` inference table; include unless the resolved value is `done` |
134
+ | `workStatus` absent or empty | Resolve via the `okstra-inspect status.4` inference table; include unless resolved `done` |
469
135
  | Filtered task count is 0 | Emit "모든 task가 done" message; do NOT create a file |
470
- | `latestReportPath` missing or unreadable | Tag entry with `[NEEDS-OKSTRA-RUN]`; include manifest metadata only |
471
- | Report parsing fails for a specific section | Tag with `[PARSE-ERROR: <section>]`; continue with the rest |
472
- | `task-group` argument matches no tasks | Output "해당 task-group을 찾을 수 없습니다." and stop |
136
+ | `latestReportPath` missing or unreadable | Staleness fallback, then `[NEEDS-OKSTRA-RUN]` with manifest metadata only |
137
+ | Report parsing fails for one section | `[PARSE-ERROR: <section>]`; continue with the rest |
138
+ | `task-group` matches no tasks | "해당 task-group을 찾을 수 없습니다." and stop |
473
139
  | Catalog and manifest disagree on `workStatus` | Manifest wins (catalog may be stale) |
474
- | Multiple `task-group` casings / punctuation variants | Normalise both sides (lowercase + strip non-`[a-z0-9]`) then compare against `taskGroupPathSegment` only. Use the manifest's `taskGroupPathSegment` verbatim for path output. |
140
+ | task-group casing / punctuation variants | Normalise both sides (lowercase + strip non-`[a-z0-9]`), compare against `taskGroupPathSegment` only; use the manifest's segment verbatim for path output |
475
141
 
476
142
  ## Output Rules
477
143
 
478
- - All user-facing messages in Korean.
479
- - Schedule file body uses Korean for prose, English for technical identifiers.
144
+ - All user-facing messages in Korean; schedule body prose Korean, identifiers/headings/field labels English (template literals).
480
145
  - Use project-relative paths in completion messages.
481
- - Never overwrite an existing schedule file: if the timestamped target somehow exists (collision in same second), append `-2`, `-3`, etc.
482
-
483
- ## Common Mistakes
484
-
485
- | Mistake | Fix |
486
- |---------|-----|
487
- | Reading only `task-catalog.json` (stale data) | Always re-read each `task-manifest.json` directly |
488
- | Skipping a task because its report cannot be parsed | Tag it `[NEEDS-OKSTRA-RUN]` or `[PARSE-ERROR]` and continue |
489
- | Omitting the At a Glance section | This section is mandatory; place it immediately after the header |
490
- | Using `workStatus` for the At a Glance status column | Use `taskType` instead — it carries lifecycle phase information |
491
- | Creating a file when 0 tasks are included | Do not create any file in this case; just print the message |
492
- | Skipping the Effort-to-Day total computation | Always compute and surface `<X.X> ~ <Y.Y> days` |
493
- | Translating `## Risk Mitigation Strategy` → `## 위험 완화 전략` / `## Stratégie de Mitigation des Risques` (or similar) | Heading literals are part of the Section Contract — keep English |
494
- | Translating the optional Gantt heading (`## Gantt 다이어그램`, `## Diagramme de Gantt`) | Use `## Gantt Chart` literally |
495
- | Adding `## Cumulative Timeline` (or any translated form) | Removed from the contract — do not emit |
496
- | Inventing extra top-level sections (`## Effort-to-Day 매핑`, `## Référentiel …`) when source data is rich | Sizing tables belong in `### Effort Sizing 기준`; `## Gantt Chart` is the only allowed extra `##` section (English heading) |
497
- | Rendering the Gantt axis with calendar dates / today-anchored labels | Axis MUST be relative day-counts (`Day 1`, `J1`, …); never `2026-05-04` or `오늘 + 3일` |
498
- | Mixing French (`Charge estimée`, `Catégorie`, `Risque`, `Dépôt`) into per-task blocks | Use `**Category**`/`**Risk**`/`**Repo**` etc. — English field labels only |
499
- | Mirroring source-report language into headings (French source → French headings, Korean source → Korean headings) | Headings + per-task field labels are English literals regardless of source language |
500
- | Emitting a Consolidated User Decision Checklist or per-task 사용자 확인 필요 항목 from old templates | Forbidden — schedule is client-facing; surface those items only in internal reports |
501
- | Title written as `작업 일정 계획서` instead of `— Work Schedule` | The English suffix is part of the contract — fix and re-run validator |
146
+ - Use `taskType` (not `workStatus`) for the At a Glance status column it carries lifecycle phase information.