okstra 0.2.0 → 0.4.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.
@@ -0,0 +1,627 @@
1
+ ---
2
+ name: okstra-schedule
3
+ description: Use when the user asks for a task-group work schedule, a consolidated implementation plan across multiple tasks in a task-group, or wants to generate a "schedule" / "일정" / "작업 계획표" for non-done tasks. Trigger words include "okstra schedule", "<task-group> 일정 만들어", "<task-group> schedule 생성", "task-group 작업 계획".
4
+ model: opus
5
+ ---
6
+
7
+ # OKSTRA Schedule
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 `.project-docs/okstra/tasks/<task-group>/schedule/`.
12
+
13
+ The default mode is lightweight (single Claude lead synthesis). The `--cross-verify` option triggers the full okstra multi-agent flow on the schedule itself.
14
+
15
+ ## When to Use
16
+
17
+ - User asks to generate a work schedule / plan / "일정" for an entire `task-group`
18
+ - User wants a single document that summarizes all non-done tasks with effort, risk, and dependencies
19
+ - A `task-group` exists in `.project-docs/okstra/discovery/task-catalog.json` with at least one task whose `workStatus` is not `done`
20
+
21
+ **Do NOT use** for single-task analysis (use `okstra-status` instead) or when the user wants to actually execute one task (use the parent `okstra` skill).
22
+
23
+ ## Trigger Patterns (recognize both)
24
+
25
+ **Natural language (Korean / English)**:
26
+ - "uploadFont 작업 일정 만들어줘"
27
+ - "uploadFont schedule 생성"
28
+ - "uploadFont 일정 계획표"
29
+ - "Generate schedule for uploadFont"
30
+
31
+ **Explicit command**:
32
+ - `okstra schedule <task-group>`
33
+ - `okstra schedule <task-group> --cross-verify`
34
+ - `okstra schedule <task-group> --title "<custom title>"`
35
+
36
+ If `--title` is omitted, derive a default title from `task-group` (e.g. `uploadFont` → `uploadFont — Work Schedule`).
37
+
38
+ ## Step 0: Verify okstra runtime + project setup
39
+
40
+ Run before anything else in this skill:
41
+
42
+ ```bash
43
+ npx -y okstra@latest ensure-installed >/dev/null 2>&1 || {
44
+ echo "FAIL: okstra not installed; tell the user to run: npx okstra@latest install" >&2
45
+ exit 1
46
+ }
47
+ eval "$(npx -y okstra@latest paths --shell)"
48
+ export PYTHONPATH="$OKSTRA_PYTHONPATH"
49
+ OKSTRA_PROJECT_INFO="$(npx -y okstra@latest check-project --json)" || {
50
+ echo "FAIL: this project has no okstra setup. Tell the user to run /okstra-setup first." >&2
51
+ echo "$OKSTRA_PROJECT_INFO" >&2
52
+ exit 1
53
+ }
54
+ ```
55
+
56
+ `$OKSTRA_PROJECT_INFO` is JSON `{ok, projectRoot, projectJsonPath, projectId}` —
57
+ use `projectRoot` to locate `.project-docs/okstra/discovery/task-catalog.json`
58
+ and the task-group directory.
59
+
60
+ ## Process Procedure
61
+
62
+ ### Step 0: Verify model (HARD GATE)
63
+
64
+ 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.
65
+
66
+ 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-opus-4-7`, `claude-sonnet-4-6`, `claude-haiku-4-5-*`).
67
+ 2. If the active model is **Opus-class** (`claude-opus-*`): proceed to Step 1.
68
+ 3. If the active model is **Sonnet or Haiku-class**: STOP and output the following message verbatim, then wait for user response:
69
+ ```
70
+ okstra-schedule는 Opus-class 모델에서 실행하는 것을 권장합니다 (현재: <active-model>).
71
+ /model opus 로 전환 후 다시 호출하시거나, 'sonnet으로 진행' 이라고 명시하시면 그대로 실행합니다.
72
+ ```
73
+ 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.`
74
+ 5. Skip this gate ONLY when the harness has clearly enforced `model: opus` from the frontmatter — verifiable by the active model already being Opus-class without manual switching.
75
+
76
+ ### Step 1: Resolve task-group and collect tasks
77
+
78
+ 1. Read `.project-docs/okstra/discovery/task-catalog.json`.
79
+ 2. Filter entries where `taskGroupPathSegment == <task-group lowercased>` OR `taskGroup == <task-group>` (case-insensitive match).
80
+ 3. If no tasks found, output `해당 task-group을 찾을 수 없습니다.` and stop.
81
+ 4. For each matched task, read `.project-docs/okstra/tasks/<task-group-segment>/<task-id-segment>/task-manifest.json` directly. Catalog data may be stale; the manifest is authoritative.
82
+ 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.
83
+
84
+ ### Step 2: Filter by workStatus
85
+
86
+ For each task-manifest:
87
+ - If `workStatus` field is missing or empty → treat as `in-progress` (include).
88
+ - If `workStatus == "done"` → exclude.
89
+ - Otherwise (`todo` / `in-progress` / `blocked`) → include.
90
+
91
+ If after filtering 0 tasks remain, output:
92
+ ```
93
+ 해당 task-group의 모든 task가 done 상태입니다. 생성할 schedule이 없습니다.
94
+ ```
95
+ Then stop without creating a file.
96
+
97
+ ### Step 3: Per-task data extraction
98
+
99
+ For each included task, gather:
100
+
101
+ **From `task-manifest.json`**:
102
+ - `taskId`, `taskGroup`, `taskKey`
103
+ - `workCategory`
104
+ - `workflow.currentPhase`, `workflow.currentPhaseState`
105
+ - `taskType`
106
+ - `workStatus`
107
+ - `latestReportPath`
108
+
109
+ **From the report file (`latestReportPath`)**, parse:
110
+ - Title / Problem statement
111
+ - Solution / Architecture
112
+ - Work Breakdown (table)
113
+ - Verification Commands
114
+ - Rollback strategy
115
+ - Effort, Risk, Priority, Scope, Repos
116
+
117
+ 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.
118
+
119
+ If the report file does not exist or cannot be parsed:
120
+ - **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):
121
+ 1. Resolve `<task-root>` from `task-manifest.json` → `taskRootPath`.
122
+ 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.
123
+ 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:
124
+ `> _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._`
125
+ 4. If no candidate file exists either, only then fall through to `[NEEDS-OKSTRA-RUN]`.
126
+ - If still unresolvable: use only manifest metadata, mark the task entry with `[NEEDS-OKSTRA-RUN]`, and do not abort — continue with remaining tasks.
127
+
128
+ If parsing fails for a specific section:
129
+ - Mark the entry with `[PARSE-ERROR: <section>]` and continue.
130
+
131
+ ### Step 4: Mode branching
132
+
133
+ - **Default (lightweight)**: Claude lead synthesizes the schedule directly using collected data. Proceed to Step 5.
134
+ - **`--cross-verify`**: Invoke the parent `okstra` skill flow with the schedule synthesis itself as the analysis target. The schedule generation becomes the cross-verified deliverable.
135
+
136
+ ### Step 5: Phase classification heuristic
137
+
138
+ Classify each task into one of three phases based on report data:
139
+
140
+ - **Phase 1 (안정성/Critical)**: priority `P0`, OR `workCategory == "bugfix"` with high risk
141
+ - **Phase 2 (개선/기능)**: priority `P1` or `P2`, `workCategory in {"improvement", "feature"}`
142
+ - **Phase 3 (확장/아키텍처)**: priority `P3`, OR multi-repo + infrastructure scope
143
+
144
+ When the classification is ambiguous, place the task in the closest phase and add a one-line rationale at the top of that phase section.
145
+
146
+ ### Step 6: Write the schedule file
147
+
148
+ Output path:
149
+ ```
150
+ .project-docs/okstra/tasks/<task-group-segment>/schedule/<task-group-segment>-plan-<YYYY-MM-DD_HH-MM-SS>.md
151
+ ```
152
+
153
+ - `<task-group-segment>`: from manifest's `taskGroupPathSegment` (lowercase, hyphenated).
154
+ - Timestamp: current local time at generation, format `YYYY-MM-DD_HH-MM-SS`.
155
+ - Auto-create the parent directory if it does not exist.
156
+
157
+ ### Step 7: Completion message (Korean)
158
+
159
+ After writing the file, reply briefly:
160
+
161
+ ```
162
+ ✓ Schedule 생성 완료: <relative-path>
163
+ - 포함 task: N개
164
+ - 제외(done) task: M개
165
+ - 예상 소요: X.X ~ Y.Y days (Effort 합산)
166
+ - 모드: lightweight | cross-verify
167
+ ```
168
+
169
+ ## Audience (READ FIRST — drives everything below)
170
+
171
+ **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.
172
+
173
+ ### Authority & permissions assumption (HARD RULE — schedule sizing)
174
+
175
+ **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.
176
+
177
+ Concretely, when computing this schedule:
178
+
179
+ - **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.
180
+ - **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.
181
+ - **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.
182
+ - **Recommended Immediate Actions** and **Execution Priority Matrix → Next Action**: Every action is a concrete engineering step (write test, modify file, run command). "권한 확인", "승인 요청", "이해관계자 정렬" 같은 항목은 출력 금지.
183
+ - **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.
184
+
185
+ 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.
186
+
187
+ Concretely:
188
+ - No "Consolidated User Decision Checklist" section.
189
+ - No per-task "사용자 확인 필요 항목" sub-section.
190
+ - No `Done` / `Ready?` / `Blocking Decisions` columns anywhere.
191
+ - No checkboxes (`[ ]` / `[x]`) — bullet lists only.
192
+ - "Status" field reflects work phase only; never bubble up "blocked" rationale prose.
193
+
194
+ **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.
195
+
196
+ **Two acceptable forms — choose one per identifier:**
197
+
198
+ **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.
199
+
200
+ | Source report | ❌ Forbidden | ✅ Form A |
201
+ |---------------|--------------|-----------|
202
+ | `FC-5: 결제 게이트웨이 타임아웃 처리 누락` | `FC-5 수정` / `FC-5 (결제 타임아웃)` | `결제 게이트웨이 타임아웃 처리` |
203
+ | `M2: 1차 베타 배포` | `M2 도달` | `1차 베타 배포` |
204
+ | `UC-3: 폰트 업로드 흐름` | `UC-3 검증` | `폰트 업로드 흐름 검증` |
205
+
206
+ **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:
207
+
208
+ ```markdown
209
+ ## Glossary
210
+
211
+ | Code | Description |
212
+ |------|-------------|
213
+ | FC-5 | 결제 게이트웨이 타임아웃 처리 누락 |
214
+ | M2 | 1차 베타 배포 |
215
+ | UC-3 | 폰트 업로드 흐름 |
216
+ ```
217
+
218
+ Rules for Form B:
219
+ - Header literal `| Code | Description |` is required.
220
+ - Every opaque code referenced anywhere in the body MUST have a glossary row. The validator flags any unresolved code.
221
+ - 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.
222
+
223
+ 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.
224
+
225
+ The validator hard-rejects any of these patterns.
226
+
227
+ ## Section Contract (HARD RULES — non-negotiable)
228
+
229
+ The canonical template lives at `templates/reports/schedule.template.md`. **Read it before writing the schedule** and follow it byte-for-byte for headings.
230
+
231
+ ### MUST — exact ordered heading list
232
+
233
+ 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):
234
+
235
+ 1. `## At a Glance`
236
+ 2. `## Executive Summary` (with subsection `### Effort Sizing 기준`)
237
+ 3. `## Task Dependency Graph`
238
+ 4. `## Phase 1: Critical Fixes`
239
+ 5. `## Phase 2: Enhancements`
240
+ 6. `## Phase 3: Architecture`
241
+ 7. `## Execution Priority Matrix`
242
+ 8. `## Cross-Task Dependencies & Shared Concerns`
243
+ 9. `## Risk Mitigation Strategy`
244
+ 10. `## Recommended Immediate Actions`
245
+
246
+ **Optional sections** (allowed at fixed positions only):
247
+ - `## Gantt Chart` — between `## Task Dependency Graph` and `## Phase 1: Critical Fixes`.
248
+ - `## 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.
249
+
250
+ If a phase has zero tasks, keep its heading and write `_없음_` underneath. **Never delete a heading.** **Never reorder.**
251
+
252
+ ### SHOULD — graphic section (English heading only)
253
+
254
+ 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:
255
+
256
+ - `## Gantt Chart` — render as a fenced **ASCII** chart inside a plain ```` ``` ```` block (no language tag, no `mermaid`). See "ASCII Gantt format" below.
257
+
258
+ #### ASCII Gantt format (REQUIRED — mermaid is forbidden)
259
+
260
+ 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:
261
+
262
+ ````
263
+ ```
264
+ Day: 1 5 10 15 20 25 30
265
+ | | | | | | |
266
+ Phase 1
267
+ <TASK-ID> (<S/M/L/XL>) ██████ ! crit
268
+ <TASK-ID> (<S/M/L/XL>) ████████
269
+ Phase 2
270
+ <TASK-ID> (<S/M/L/XL>) ██████░░ est
271
+ Phase 3
272
+ <TASK-ID> (<S/M/L/XL>) ████
273
+ ```
274
+ ````
275
+
276
+ Rules:
277
+ - 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.
278
+ - 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.
279
+ - 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.
280
+ - 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.
281
+ - 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.
282
+ - 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.
283
+ - Append annotations after the bar separated by spaces:
284
+ - `! crit` for critical-path items (replaces mermaid's `:crit,`).
285
+ - `est` when day allocations are estimated (replaces `:est,`).
286
+ - `(after <TASK-ID>)` to mark a sequential dependency when not visually obvious.
287
+ - Keep total width ≤ 100 columns. If span exceeds the axis, increase the tick interval rather than wrapping.
288
+ - When effort is XXL ("세분화 필요") and no decomposition exists, follow the skip-reason rule instead of inventing bars.
289
+
290
+ #### Render-by-default rule (REVERSED from prior contract)
291
+
292
+ These sections are **rendered by default**. Skip them ONLY when literally no usable day data exists anywhere in the source. Specifically:
293
+
294
+ **`## Gantt Chart` — render whenever ANY of these hold:**
295
+ - 2+ tasks with any effort sizing (S/M/L/XL — all map to day ranges via the Effort Sizing 기준 table)
296
+ - 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)
297
+ - 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
298
+ - Any case where total estimated effort is ≥ 3 days
299
+
300
+ 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.
301
+
302
+ **Skip is permitted ONLY when:**
303
+ - Every task has effort=XXL ("세분화 필요") AND no decomposition is visible in the source — i.e. there is genuinely no day signal to plot
304
+ - All included tasks lack BOTH effort sizing AND any decomposition
305
+
306
+ 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.
307
+
308
+ #### Directive override (highest priority)
309
+
310
+ Before applying the heuristics above, **check `instruction-set/analysis-material.md` for a `## Directive` section** (sourced from the `--directive` CLI flag). If that section is present and expresses any directive 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.
311
+
312
+ `## Gantt Chart` is the only `##` section that MAY be added beyond the 11 mandatory ones. Any other extra `##` heading is forbidden.
313
+
314
+ #### MUST — emit a skip-reason note when omitting Gantt Chart (rare case)
315
+
316
+ 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:
317
+
318
+ ```
319
+ > _Gantt Chart 생략: <concrete reason referencing the actual data>._
320
+ ```
321
+
322
+ The reason MUST reference the actual data and must be one of the legitimate skip cases. Acceptable example:
323
+
324
+ - `> _Gantt Chart 생략: 모든 task가 effort=XXL (세분화 필요), day 추정 산출 불가._`
325
+
326
+ **NOT acceptable as skip reasons** (these mean you should render an estimate-tagged chart instead):
327
+ - "단일 task" — render with the task's effort range as bars
328
+ - "effort range가 넓음 (XL 5-15d)" — render mid-point or `lo`/`hi` two-bar form
329
+ - "사용자 결정 전 확정 불가" — render with `est` annotation and a `> 일별 배분은 추정치` note
330
+ - "Part/Phase 내부 day 배분 미정" — split the parent range across visible decomposition units
331
+
332
+ This rule exists because rendering an estimated chart with a clear "estimate" tag is **always** more useful than no chart at all.
333
+
334
+ ### MUST — top header (verbatim shape)
335
+
336
+ ```markdown
337
+ # <Title> — Work Schedule
338
+
339
+ > Generated: <YYYY-MM-DD HH:MM> | Project: <project-id> | Task Group: <task-group>
340
+ > Source: okstra <mode> (<N> tasks included, <M> done excluded)
341
+ ```
342
+
343
+ The title suffix MUST be `— Work Schedule` (em dash, English). Korean titles like `작업 일정 계획서` are forbidden.
344
+
345
+ ### MUST — per-task block field set
346
+
347
+ 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.
348
+
349
+ ### MUST — per-task block sub-section order
350
+
351
+ 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: …]`):
352
+
353
+ 1. `**Problem**:`
354
+ 2. `**Solution**:`
355
+ 3. `**Work Breakdown**:` (followed by the `| Step | File | Action | Detail |` table)
356
+ 4. `**Verification Commands**:` (followed by a fenced ` ```bash ` block)
357
+ 5. `**Rollback**:`
358
+
359
+ The validator enforces order by scanning each `### N-i.` block. A missing section or an out-of-order section is a hard violation. **Never emit `#### 사용자 확인 필요 항목`** — this is a client-facing schedule (see "Audience" above).
360
+
361
+ ### MUST — controlled vocabulary (enums)
362
+
363
+ 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`).
364
+
365
+ | Field | Allowed values |
366
+ |-------|----------------|
367
+ | **Effort** | `S`, `M`, `L`, `XL`, `XXL` (leading token only — trailing scope hint like `L (5-15 files)` is allowed inside parens) |
368
+ | **Priority** | `P0`, `P1`, `P2`, `P3` |
369
+ | **Risk** | `Very Low`, `Low`, `Medium`, `Med-High`, `High` |
370
+ | **Phase** | `1`, `2`, `3` |
371
+
372
+ `Med-High` (hyphenated) is the canonical form — `Medium-High` / `중상` / `Moyen-Élevé` are all rejected.
373
+
374
+ ### MUST — At a Glance totals line
375
+
376
+ The At a Glance section MUST contain exactly one totals line in this shape (single line, all bold):
377
+
378
+ ```
379
+ **총 <N>개 task / 예상 소요: <X.X> ~ <Y.Y> days (Effort 합산)**
380
+ ```
381
+
382
+ `<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.
383
+
384
+ ### MUST — table & list shapes (no checkboxes anywhere)
385
+
386
+ 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.**
387
+
388
+ Required shapes:
389
+
390
+ - `## Execution Priority Matrix` — header `| Priority | Task | Effort | Risk | Next Action |`. No leading `Done` column, no `Ready?`, no `Blocking Decisions`.
391
+ - `## Recommended Immediate Actions` — markdown bullet list (`- <action>`). Numbered lists and checkbox lists are both rejected.
392
+ - `## Risk Mitigation Strategy` — numbered list (it states *strategies*, not actions to tick off).
393
+
394
+ ### MUST NOT — forbidden patterns
395
+
396
+ | Pattern | Why forbidden |
397
+ |---------|---------------|
398
+ | Translating any `##` heading to Korean (`전체 개요`, `요약`, `위험 완화 전략`, `즉시 권장 액션`, etc.) or any other language | Heading literals are stable identifiers used by the validator and downstream tooling |
399
+ | Adding sections not in the contract (`## Effort-to-Day 매핑`, `## Référentiel …`, etc. — anything beyond the 11 mandatory + the 1 optional `## Gantt Chart`) | Sizing tables belong inside `### Effort Sizing 기준` (Executive Summary). All other graphic detail belongs inside per-phase prose |
400
+ | Translating the optional graphic section (`## Gantt 다이어그램`, `## Diagramme de Gantt`) | Use `## Gantt Chart` literally — translation is forbidden |
401
+ | Adding `## Cumulative Timeline` (or any translated form: `## 누적 일정표`, `## Calendrier Cumulé`) | The Cumulative Timeline section has been removed from the contract — never emit it |
402
+ | 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 |
403
+ | 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 |
404
+ | **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 |
405
+ | 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 |
406
+ | Emitting `## Consolidated User Decision Checklist` or `#### 사용자 확인 필요 항목` | Forbidden — schedule is a client-facing deliverable; blocking/approval/decision items belong in the internal report |
407
+ | Adding `Done` / `Ready?` / `Blocking Decisions` columns to any table, or `[ ]` / `[x]` checkbox cells in any list | Forbidden for the same reason — see "Audience" section |
408
+ | Replacing `Item / Detail` per-task table headers with translated versions | The literal `Item / Detail` is required |
409
+ | 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 |
410
+ | 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) |
411
+ | 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 |
412
+
413
+ ## Self-Validation Before Reporting Completion
414
+
415
+ After writing the file and before printing the completion message in Step 7, you MUST:
416
+
417
+ 1. **Re-read the file** you just wrote.
418
+ 2. **Run the validator**:
419
+ ```bash
420
+ python3 validators/validate-schedule.py <output-path>
421
+ ```
422
+ 3. If the validator exits non-zero, **fix the file and re-validate**. Do not print the completion message until the validator passes.
423
+ 4. If `validators/validate-schedule.py` is not present in the repo, fall back to a manual checklist: confirm each of the 11 mandatory headings appears in the file with the exact spelling above, the title ends with `— Work Schedule`, and the metadata `> Generated:` block is present.
424
+
425
+ ## Schedule Document Template
426
+
427
+ The generated Markdown file MUST follow this structure exactly, mirroring `templates/reports/schedule.template.md`. Do not omit sections; if data is unavailable for a section, include the section with the placeholder `_없음_` rather than skipping it.
428
+
429
+ ### Required Top Header
430
+
431
+ ```markdown
432
+ # <Title> — Work Schedule
433
+
434
+ > Generated: <YYYY-MM-DD HH:MM> | Project: <project-id> | Task Group: <task-group>
435
+ > Source: okstra <mode> (<N> tasks included, <M> done excluded)
436
+
437
+ ---
438
+ ```
439
+
440
+ ### Section: At a Glance (FIRST content section after header)
441
+
442
+ This section gives the reader the entire schedule scope at a single screen.
443
+
444
+ ```markdown
445
+ ## At a Glance
446
+
447
+ **총 <N>개 task / 예상 소요: <X.X> ~ <Y.Y> days (Effort 합산)**
448
+
449
+ | # | Task ID | Title | Category | Priority | Effort | Days | taskType | Risk | Phase |
450
+ |---|---------|-------|----------|----------|--------|------|----------|------|-------|
451
+ | 1 | <TASK-ID> | <Title> | <category> | <P0~P3> | <S/M/L/XL> | <range> | <taskType> | <risk> | <1/2/3> |
452
+ | ... |
453
+
454
+ **Effort 분포**: S × <n> / M × <n> / L × <n> / XL × <n>
455
+ **Risk 분포**: Very Low × <n> / Low × <n> / Medium × <n> / Med-High × <n> / High × <n>
456
+ **Repo별 영향**: <repo1> (<n>) / <repo2> (<n>) / ...
457
+
458
+ ---
459
+ ```
460
+
461
+ Effort-to-Day mapping (used to compute the total day range):
462
+
463
+ | Size | Day(s) |
464
+ |------|----------|
465
+ | S | 0.5 - 1 |
466
+ | M | 2 - 3 |
467
+ | L | 3 - 5 |
468
+ | XL | 5 - 10 |
469
+ | XXL | 10 - |
470
+ Sum the lower bounds for the lower total and the upper bounds for the upper total.
471
+
472
+ ### Section: Executive Summary
473
+
474
+ ```markdown
475
+ ## Executive Summary
476
+
477
+ <Phase 분류 요약 + 진행 전략 — 2~4문장>
478
+
479
+ - **Phase 1 (Critical Fixes)**: <n>개 task — <summary>
480
+ - **Phase 2 (Enhancements)**: <n>개 task — <summary>
481
+ - **Phase 3 (Architecture)**: <n>개 task — <summary>
482
+
483
+ ### Effort Sizing 기준
484
+
485
+ | Size | 기준 | Day(s) |
486
+ |------|------|--------|
487
+ | **S** | 1-2 files, 1 repo, 테스트 수정만, DB 변경 없음 | 0.5 - 1 |
488
+ | **M** | 3-5 files, 1 repo, 신규 테스트 포함, DB 변경 없음 | 2 - 3 |
489
+ | **L** | 5-15 files, 2-3 repos, 순차 배포, 패키지 릴리스 포함 가능 | 3 - 5 |
490
+ | **XL** | 15+ files, 3+ repos + infra, frontend 동시 변경, 아키텍처 전환 | 5 - 10 |
491
+ | **XXL** | 작업 더 세분화 필요 | 10 - |
492
+
493
+ ---
494
+ ```
495
+
496
+ ### Section: Task Dependency Graph
497
+
498
+ This section MUST follow ONE of two exact shapes — free-form ASCII art, mermaid, PlantUML, or Graphviz are all forbidden.
499
+
500
+ **Shape A — no dependency data.** Single italicized blockquote-line under the heading, literal Korean:
501
+
502
+ ```
503
+ _의존 정보 없음_
504
+ ```
505
+
506
+ **Shape B — adjacency-list inside a plain fence.** When dependency edges are extracted from per-task reports, emit them as a fenced block with NO language tag. Each non-empty line is one of:
507
+
508
+ - An adjacency line: `<TASK-ID> -> <TASK-ID>[, <TASK-ID>]*`
509
+ - A comment line starting with `#` (free prose, max one per group)
510
+ - A blank line (group separator)
511
+
512
+ ```
513
+ DEV-1 -> DEV-2, DEV-3
514
+ DEV-2 -> DEV-4
515
+ # Phase 3 fan-out
516
+ DEV-5 -> DEV-6
517
+ ```
518
+
519
+ Rules:
520
+ - Use ASCII arrow `->` only. Unicode `→` is rejected so downstream tooling does not have to handle both.
521
+ - Both endpoints MUST be `TASK-ID` literals (the same identifiers used in the At a Glance table). Free text is forbidden.
522
+ - Fence MUST be plain ` ``` ` with no language tag. ` ```mermaid `, ` ```plantuml `, ` ```graphviz `, ` ```dot ` are all rejected.
523
+ - If only one task is in scope (no dependencies), use Shape A — do NOT emit an empty fence.
524
+
525
+ ### Section: Phase 1 / Phase 2 / Phase 3
526
+
527
+ For each phase, repeat the per-task block:
528
+
529
+ ````markdown
530
+ ## Phase <N>: <Name>
531
+
532
+ ### <N>-<i>. <TASK-ID> — <Title>
533
+
534
+ | Item | Detail |
535
+ |------|--------|
536
+ | **Category** | <category> |
537
+ | **Priority** | <P0~P3> |
538
+ | **Effort** | **<S/M/L/XL>** (<scope summary>) |
539
+ | **Status** | <workStatus + currentPhase summary> |
540
+ | **Risk** | <risk> |
541
+ | **Scope** | <files / repos summary> |
542
+ | **Repo** | <repos> |
543
+
544
+ **Problem**: <…>
545
+
546
+ **Solution**: <…>
547
+
548
+ **Work Breakdown**:
549
+
550
+ | Step | File | Action | Detail |
551
+ |------|------|--------|--------|
552
+ | 1 | <path> | CREATE/MODIFY/VERIFY | <detail> |
553
+ | ... |
554
+
555
+ **Verification Commands**:
556
+ ```bash
557
+ <commands>
558
+ ```
559
+
560
+ **Rollback**: <…>
561
+
562
+ ---
563
+ ````
564
+
565
+ If a task entry is `[NEEDS-OKSTRA-RUN]` or `[PARSE-ERROR: <section>]`, place the marker immediately under the task heading and fill only the available fields.
566
+
567
+ ### Section: Execution Priority Matrix
568
+
569
+ ```markdown
570
+ ## Execution Priority Matrix
571
+
572
+ | Priority | Task | Effort | Risk | Next Action |
573
+ |----------|------|--------|------|-------------|
574
+ ```
575
+
576
+ Each row summarizes the next concrete engineering action per task in priority order. No `Done` / `Ready?` / `Blocking Decisions` columns — schedule is a client-facing plan, not an internal todo board.
577
+
578
+ ### Section: Cross-Task Dependencies & Shared Concerns
579
+
580
+ Free-form prose listing inter-task overlaps, shared packages, release order, etc.
581
+
582
+ ### Section: Risk Mitigation Strategy
583
+
584
+ Enumerate phase-level risk strategies as numbered list.
585
+
586
+ ### Section: Recommended Immediate Actions
587
+
588
+ Markdown bullet list of next concrete engineering actions, one item per line: `- <action>`. Numbered lists (`1. …`) and checkbox lists (`- [ ] …`) are both forbidden — the schedule is a client-facing plan, not an internal todo.
589
+
590
+ ## Edge Cases
591
+
592
+ | Case | Handling |
593
+ |------|----------|
594
+ | `workStatus` field absent or empty | Treat as `in-progress` → include in schedule |
595
+ | Filtered task count is 0 | Emit "모든 task가 done" message; do NOT create a file |
596
+ | `latestReportPath` missing or unreadable | Tag entry with `[NEEDS-OKSTRA-RUN]`; include manifest metadata only |
597
+ | Report parsing fails for a specific section | Tag with `[PARSE-ERROR: <section>]`; continue with the rest |
598
+ | `task-group` argument matches no tasks | Output "해당 task-group을 찾을 수 없습니다." and stop |
599
+ | Catalog and manifest disagree on `workStatus` | Manifest wins (catalog may be stale) |
600
+ | Multiple `task-group` casings | Match case-insensitively; use the manifest's `taskGroupPathSegment` for path output |
601
+
602
+ ## Output Rules
603
+
604
+ - All user-facing messages in Korean.
605
+ - Schedule file body uses Korean for prose, English for technical identifiers.
606
+ - Use project-relative paths in completion messages.
607
+ - Never overwrite an existing schedule file: if the timestamped target somehow exists (collision in same second), append `-2`, `-3`, etc.
608
+
609
+ ## Common Mistakes
610
+
611
+ | Mistake | Fix |
612
+ |---------|-----|
613
+ | Reading only `task-catalog.json` (stale data) | Always re-read each `task-manifest.json` directly |
614
+ | Skipping a task because its report cannot be parsed | Tag it `[NEEDS-OKSTRA-RUN]` or `[PARSE-ERROR]` and continue |
615
+ | Omitting the At a Glance section | This section is mandatory; place it immediately after the header |
616
+ | Using `workStatus` for the At a Glance status column | Use `taskType` instead — it carries lifecycle phase information |
617
+ | Creating a file when 0 tasks are included | Do not create any file in this case; just print the message |
618
+ | Skipping the Effort-to-Day total computation | Always compute and surface `<X.X> ~ <Y.Y> days` |
619
+ | Translating `## Risk Mitigation Strategy` → `## 위험 완화 전략` / `## Stratégie de Mitigation des Risques` (or similar) | Heading literals are part of the Section Contract — keep English |
620
+ | Translating the optional Gantt heading (`## Gantt 다이어그램`, `## Diagramme de Gantt`) | Use `## Gantt Chart` literally |
621
+ | Adding `## Cumulative Timeline` (or any translated form) | Removed from the contract — do not emit |
622
+ | 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) |
623
+ | 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일` |
624
+ | Mixing French (`Charge estimée`, `Catégorie`, `Risque`, `Dépôt`) into per-task blocks | Use `**Category**`/`**Risk**`/`**Repo**` etc. — English field labels only |
625
+ | 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 |
626
+ | Emitting a Consolidated User Decision Checklist or per-task 사용자 확인 필요 항목 from old templates | Forbidden — schedule is client-facing; surface those items only in internal reports |
627
+ | Title written as `작업 일정 계획서` instead of `— Work Schedule` | The English suffix is part of the contract — fix and re-run validator |