okstra 0.152.0 → 0.153.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -184,7 +184,7 @@ Use these slash commands inside a Claude Code session:
184
184
  | `/okstra-inspect` | Unified read side. Subcommands: `status` (phase/state and workStatus updates), `history` (past tasks, reruns, resumes), `report` (find/read final reports), `time` (elapsed-time breakdown), `logs` (wrapper log sidecar inventory and cleanup suggestions), `cost` (task bundle context/read cost), `errors` (aggregate run error logs into a report), `error-zip` (collect cross-project error logs into an anonymized zip and summarize clusters), and `recap` (run-to-run before/after summary plus free-form Q&A over a task's `.okstra` artifacts) |
185
185
  | `/okstra-rollup` | Aggregate every task run in a task group or project, including per-task run counts, duration, errors, group totals, and a cross-task report digest |
186
186
  | `/okstra-usage` | Show the current project's recent run coverage, raw and billable-equivalent tokens, known USD cost, CPU time, and wall-clock time grouped by task type (default: last 30 days) |
187
- | `/okstra-schedule-gen` | Generate a work schedule for an entire task group. Stages are sourced from each non-done task's `implementation-planning` final-report **Stage Map**; you pick which stages to include, and the final file is written only after an independent verifier subagent confirms the draft covers exactly the selected stages |
187
+ | `/okstra-schedule-gen` | Invoke as `/okstra-schedule-gen [task-group]` to generate a work schedule for an entire task group. Each non-done task is resolved through the source-aware `stage-map` response; your unfinished-stage choices are captured in a temporary selection contract, and only the same draft that passes deterministic selection validation followed by independent narrative verification is published |
188
188
  | `/okstra-container-build` | Deploy a verified task's code as a local Docker Compose group and monitor per-container logs (subcommands: `up` / `status` / `logs` / `stop-watcher` / `down`) |
189
189
  | `/okstra-manager` | Coordinate cross-project okstra tasks through manager-owned plans, assignments, one-way project sync snapshots, status, and child launch context packets |
190
190
  | `/okstra-pr-gen` | Register PR body templates under `~/.okstra/template/pr/` and generate a PR description from a branch diff (subcommands: `template` / `branches` / `gen`). Global skill—needs a Git repo, not a registered okstra project |
package/bin/okstra CHANGED
@@ -18,6 +18,13 @@ async function main(argv) {
18
18
  const [cmd, ...rest] = args;
19
19
  const loader = COMMANDS.get(cmd);
20
20
  if (!loader) {
21
+ if (cmd === "schedule") {
22
+ process.stderr.write(
23
+ "unknown command: schedule\n" +
24
+ "Schedule generation runs in an agent host: /okstra-schedule-gen [task-group]\n",
25
+ );
26
+ return 2;
27
+ }
21
28
  process.stderr.write(
22
29
  `unknown command: ${cmd}\n` +
23
30
  "hint: this okstra binary may predate the skill calling it — update it " +
package/docs/cli.md CHANGED
@@ -324,6 +324,10 @@ Example:
324
324
  scripts/okstra.sh --task-type error-analysis --project-id jobs --project-root /Volumes/Workspaces/workspace/projects/jobs ...
325
325
  ```
326
326
 
327
+ ### Schedule generation skill
328
+
329
+ The public schedule entry point is the host skill `/okstra-schedule-gen [task-group]`; `stage-map` and `validate-schedule.py` are its backend contracts, not an additional schedule-generation shell command. For each candidate task, the skill branches on the `stage-map` `state` and `sourcePlanPath`, records the selected, completed, and full stage sets in a temporary `.selection.json`, and runs deterministic `--selection-json` validation before an independent narrative verifier. Only the same draft that passes both gates is promoted, and the temporary selection contract is removed after final validation.
330
+
327
331
  ### `--directive`
328
332
 
329
333
  A free-text channel for passing user intent to the lead, workers, and downstream skills. The value is embedded in a `## Directive` section at the end of `instruction-set/analysis-material.md` and in `instruction-set/analysis-packet.md`, the analysis workers' primary input. It is also backed up to `instruction-set/directive.txt`.
@@ -737,7 +741,7 @@ The `okstra` Node CLI (`bin/okstra`) provides both installer/admin commands and
737
741
  | `okstra migrate [--apply] [--cwd <dir>] [--quiet]` | One-time migration of the project artifact root from `.project-docs/okstra/` to `.okstra/`. It is a dry run by default; `--apply` performs the move with `git mv` in a Git worktree, removes an empty `.project-docs/`, and synchronizes the `<PROJECT>/CLAUDE.md` import line, `.gitignore`, the project's rows in `~/.okstra/{recent,active}.jsonl`, and `~/.okstra/worktrees/registry.json`. It exits 1 if `.okstra/` already exists or the legacy directory is absent. Scheduled for removal by the end of v0.x |
738
742
  | `okstra task-list [--project-root <path>]` | Combine `list_project_tasks` and `read_latest_task` into JSON containing the task catalog and latest task |
739
743
  | `okstra task-show <task-key> [--project-root <path>]` | Summarize workflow, phase, status, and artifacts from the Task Read-Side Snapshot |
740
- | `okstra stage-map <task-key> [--cwd <dir>\|--project <dir>]` | Dump the task's implementation-planning Stage Map as JSON: `{ ok, taskKey, taskRoot, stages:[{stage_number,title,depends_on,step_count}], doneStages:[int] }`. `stages` is `[]` when the task has no implementation-planning Stage Map. `doneStages` is read from the implementation-planning stage consumer state (with carry recovery). This is the read-side source the `okstra-schedule-gen` skill uses to derive stage units and their dependency closure |
744
+ | `okstra stage-map <task-key> [--cwd <dir>\|--project <dir>]` | Dump the task's implementation-planning Stage Map as JSON: `{ ok, taskKey, taskRoot, state, sourcePlanPath, stages:[{stage_number,title,depends_on,step_count}], doneStages:[int] }`. `state` is `ready` for one resolved source and `missing` when no Stage Map exists; corrupt or conflicting sources return structured non-zero errors instead of silently selecting another report. `doneStages` is read from the implementation-planning stage consumer state (with carry recovery). This is the read-side source `/okstra-schedule-gen [task-group]` uses to derive selectable unfinished stages and their completed dependency closure |
741
745
  | `okstra incremental-scope <args…>` | Decide re-verify vs carry-forward scope for an `implementation-planning` clarification re-run. Thin shim into `scripts/okstra_ctl/incremental_scope.py` (deterministic pure function): it reads the dependency graph from the prior run `data.json`'s `implementationPlanning.stageMap` and returns `mode:"incremental"` only when the base-ref SHA is unchanged and the affected stages' `downstream_stage_closure` covers at most half of all stages; otherwise it signals a full re-run. Used to bound the cost of a clarification re-run |
742
746
  | `okstra incremental-carry <args…>` | Merge carried-forward plan-item verdicts into an incremental re-run. Thin shim into `scripts/okstra_ctl/incremental_carry.py`: it takes the prior run's plan-item verdicts that the current run does not re-verify and merges them into the current `data.json` tagged with `carriedForwardFromSeq`. A `schemaVersion` drift raises `CarryError` and exits non-zero to force a full fallback. Runs after `incremental-scope` returns `mode:"incremental"` |
743
747
  | `okstra code-review target --task-key <k> --stage <N> [--project-root <dir>] [--cwd <dir>] [--json]` / `okstra code-review target --branch <name> [--base <ref>] [--date <YYYY-MM-DD>] [--project-root <dir>] [--cwd <dir>] [--json]` | Resolve what a code review reads and where its result file goes. Output is always JSON, so `--json` only makes that explicit. `--project-root` and `--cwd` are shared pre-dispatch arguments and apply to both modes; `--cwd` is only consulted when `--project-root` is absent. Both modes return `{ ok, projectRoot, mode, worktreePath, branch, baseCommit, headCommit, reviewPath, round }`; stage mode additionally returns `taskKey`, `taskRoot`, and `stage`. Stage mode takes the diff base from the `base_ref` recorded on that stage's worktree-registry row when it was provisioned — not from a rule re-applied at review time — and names the result `.okstra/tasks/<task-group>/<task-id>/code-reviews/stage-<NN>.md`, where a re-review of the same stage becomes `-r2`, `-r3`, … (the `round` field). Only a legacy row provisioned before `base_ref` was recorded falls back to re-deriving the base through `stage_targets`, and a failure there is reported as `stage_base_unresolved`. `worktreePath` comes back empty whenever the stage worktree is not usable as a live checkout — the registry row is no longer `active` (whole-task final-verification released it), the row never carried a path, or the recorded directory is gone — and the review then reads the `branch` ref instead. Branch mode uses `--base` when given, otherwise the merge-base with the default branch (`refs/remotes/origin/HEAD`, else `main`/`master`), and names the result `.project-docs/code-reviews/<branch>/<YYYY-MM-DD>-<NN>.md`, where `<NN>` (the `round` field) is the next sequence number for that date — the highest already on disk plus one. Read-only: it resolves paths and creates no directory and no file, so the review directory does not exist until the caller writes the report. Backend for the okstra-code-review skill |
@@ -3,318 +3,238 @@
3
3
  ## Source
4
4
 
5
5
  - Skill source: [`skills/okstra-schedule-gen/SKILL.md`](../../../skills/okstra-schedule-gen/SKILL.md)
6
- - schedule template: [`templates/reports/schedule.template.md`](../../../templates/reports/schedule.template.md)
7
- - schedule validator: [`validators/validate-schedule.py`](../../../validators/validate-schedule.py)
6
+ - Schedule template: [`templates/reports/schedule.template.md`](../../../templates/reports/schedule.template.md)
7
+ - Schedule validator: [`validators/validate-schedule.py`](../../../validators/validate-schedule.py)
8
+ - Stage Map read side: [`scripts/okstra_project/state.py`](../../../scripts/okstra_project/state.py)
9
+ - Selection semantics: [`scripts/okstra_ctl/schedule_semantics.py`](../../../scripts/okstra_ctl/schedule_semantics.py)
10
+ - Work-category source of truth: [`scripts/okstra_ctl/work_categories.py`](../../../scripts/okstra_ctl/work_categories.py)
8
11
  - workStatus inference reference: [`skills/okstra-inspect/SKILL.md`](../../../skills/okstra-inspect/SKILL.md)
9
12
 
10
- ## Purpose
13
+ ## Purpose and invocation
11
14
 
12
- `okstra-schedule-gen` gathers the non-done tasks within a task-group and produces a client-facing work-schedule Markdown. It is not a skill for starting an execution task, and it is not a single-task analysis.
15
+ `okstra-schedule-gen` gathers non-done tasks in a task group and produces one client-facing work schedule from user-selected unfinished implementation stages.
13
16
 
14
- Output location:
17
+ Public invocation:
15
18
 
16
19
  ```text
17
- <PROJECT_ROOT>/.okstra/tasks/<task-group-segment>/schedule/<task-group-segment>-plan-<YYYY-MM-DD_HH-MM-SS>.md
20
+ /okstra-schedule-gen [task-group]
18
21
  ```
19
22
 
20
- ## When to Use
23
+ This is a host skill, not a schedule-generation shell command. Use `stage-map` and `validate-schedule.py` only as backend contracts inside the skill.
21
24
 
22
- Use it when:
23
-
24
- - The user requests a "schedule", "work plan", or work-schedule table for an entire task-group.
25
- - `.okstra/discovery/task-catalog.json` contains that task-group and it has at least one task that is not `done`.
25
+ Output location:
26
26
 
27
- Do not use it when:
27
+ ```text
28
+ <PROJECT_ROOT>/.okstra/tasks/<task-group-segment>/schedule/<task-group-segment>-plan-<YYYY-MM-DD_HH-MM-SS>.md
29
+ ```
28
30
 
29
- - Single-task status/analysis: `okstra-inspect status`
30
- - Actual phase execution: `okstra-run`
31
- - An already-completed task-group: do not create a file; state that all tasks are done.
31
+ Do not use it for single-task status analysis or phase execution. Use `okstra-inspect status` and `okstra-run` for those jobs.
32
32
 
33
- ## Preflight
33
+ ## Preflight and task-group resolution
34
34
 
35
- A single Bash call:
35
+ Run one literal-token preflight call:
36
36
 
37
37
  ```bash
38
38
  okstra preflight --runtime claude-code --json
39
39
  ```
40
40
 
41
- If there is no runtime or project setup, point the user to `/okstra-setup` and stop.
42
-
43
- ## Resolving the task-group
41
+ On `ok: false`, point the user to `/okstra-setup` and stop. Otherwise:
44
42
 
45
43
  1. Read `.okstra/discovery/task-catalog.json`.
46
- 2. Lowercase the user-supplied task-group and strip characters outside `[a-z0-9]`.
47
- 3. Apply the same transform to each catalog entry's `taskGroupPathSegment` and compare.
48
- 4. Do not fall back to the raw `taskGroup`.
49
- 5. Read each matched task's `task-manifest.json` directly. The catalog may be stale; the manifest is authoritative.
44
+ 2. Resolve an explicit task-group from the invocation or host request. If none is unambiguous, ask the user to choose; never guess.
45
+ 3. Lowercase the token and strip characters outside `[a-z0-9]`.
46
+ 4. Apply the same transform to each catalog `taskGroupPathSegment` and compare only those normalized values.
47
+ 5. Read each matched `task-manifest.json`; it is authoritative when the catalog is stale.
50
48
 
51
- On zero matches, output `That task-group could not be found.` and stop.
49
+ On zero matches, report that the task group was not found and do not create a file.
52
50
 
53
- ## workStatus filter
51
+ ## Candidate filter
54
52
 
55
- Check the workStatus in each manifest. When it is missing or empty, apply `okstra-inspect`'s `status.4` inference table.
53
+ `workStatus` is used only to decide which tasks are candidates. When it is missing or empty, use the `okstra-inspect` `status.4` inference table.
56
54
 
57
- Exclude:
55
+ - Exclude resolved `done` tasks.
56
+ - Include every other resolved state.
57
+ - If no task remains, report that all tasks are done and do not create a file.
58
58
 
59
- - explicit `done`
60
- - inferred `done`
59
+ Do not render `workStatus` as the detailed task status. The per-task `Status` value is `<taskType> / <currentPhase>`.
61
60
 
62
- Include:
61
+ ## Source-aware Stage Map resolution
63
62
 
64
- - `todo`
65
- - `in-progress`
66
- - `blocked`
67
- - `phase-done`
68
- - other non-done inferred/display states
63
+ For every candidate task, call:
69
64
 
70
- If 0 remain after filtering, do not create a schedule file; output `All tasks in this task-group are done. There is no schedule to generate.`
65
+ ```bash
66
+ okstra stage-map <task-key> --json
67
+ ```
71
68
 
72
- ## per-task data extraction
69
+ The successful response has this boundary shape:
70
+
71
+ ```text
72
+ { ok, taskKey, taskRoot, state, sourcePlanPath,
73
+ stages:[{stage_number,title,depends_on,step_count}], doneStages:[int] }
74
+ ```
73
75
 
74
- Read from the manifest:
76
+ Handle each result explicitly:
75
77
 
76
- - `taskId`, `taskGroup`, `taskKey`
77
- - `workCategory`
78
- - `workflow.currentPhase`
79
- - `workflow.currentPhaseState`
80
- - `taskType`
81
- - `workStatus`
82
- - `latestReportPath`
78
+ - `state: ready`: use exactly `sourcePlanPath`; do not pick a report by mtime or `latestReportPath`. Select only `stages − doneStages`.
79
+ - `state: missing`: record an empty source and empty stage sets, mark the task `[NEEDS-PLANNING]`, and emit no forward Work Breakdown, Gantt, or day total for it.
80
+ - `ok: false` or another state: stop before drafting and report the structured `stage` and `reason`. A corrupt or conflicting source must never fall back to a guessed report.
83
81
 
84
- Parse from the report:
82
+ A valid selected set is dependency-closed: every transitive prerequisite of a selected stage is either also selected or present in `doneStages`. Completed prerequisites remain evidence only and are never scheduled forward.
85
83
 
86
- - Title / Problem statement
87
- - Solution / Architecture
88
- - Work Breakdown
89
- - Verification Commands
90
- - Rollback strategy
91
- - Effort, Risk, Priority, Scope, Repos
84
+ If a ready task has no unfinished stages, render `_Complete — no remaining stage_` and omit forward effort.
92
85
 
93
- If there is no report or the manifest pointer is stale, fall back:
86
+ ## Stage selection
94
87
 
95
- 1. From `taskRootPath`, find the newest-mtime file under `runs/<taskType>/reports/final-report-*.md`.
96
- 2. If none, also look at `runs/*/reports/final-report-*.md`.
97
- 3. If found, parse it and put a fallback note in the schedule task block.
98
- 4. If still nothing, mark it `[NEEDS-OKSTRA-RUN]` and use only the manifest metadata.
88
+ Offer up to three dependency-closed cumulative bundles in topological order, plus all remaining stages. A custom set is accepted only after closing it over unfinished prerequisites; completed prerequisites are preserved separately.
99
89
 
100
- Mark a specific section's parse failure as `[PARSE-ERROR: <section>]` and continue.
90
+ Skip the picker when the remaining work has only one possible bundle and use all unfinished stages. Record the final stage numbers as `selectedStages`.
101
91
 
102
- ## client-facing audience rule
92
+ ## Temporary selection contract
103
93
 
104
- The schedule is a client-facing work plan. Do not surface the internal report's approval, blocking decisions, or items requiring user confirmation in the schedule.
94
+ Write a paired draft and selection input with one timestamp:
105
95
 
106
- Remove from the output:
96
+ ```text
97
+ .okstra/tasks/<task-group-segment>/schedule/.draft/<timestamp>.md
98
+ .okstra/tasks/<task-group-segment>/schedule/.draft/<timestamp>.selection.json
99
+ ```
107
100
 
108
- - permission/authority confirmation steps
109
- - approval waiting buffer
110
- - stakeholder coordination
111
- - legal/organizational sign-off
112
- - `Consolidated User Decision Checklist`
113
- - `#### Items requiring user confirmation`
114
- - `Done`, `Ready?`, `Blocking Decisions` column
115
- - checkbox list/cell
101
+ The selection file is a temporary verification input. It freezes the exact source, full stage map, completed stages, and user-selected forward work so both validators judge the same facts instead of re-resolving mutable task state.
102
+
103
+ Schema version 1:
104
+
105
+ ```json
106
+ {
107
+ "schemaVersion": 1,
108
+ "tasks": [
109
+ {
110
+ "taskKey": "demo:group:DEV-1",
111
+ "taskId": "DEV-1",
112
+ "state": "ready",
113
+ "sourcePlanPath": "/absolute/path/final-report-implementation-planning-001.md",
114
+ "selectedStages": [2, 3],
115
+ "doneStages": [1],
116
+ "stages": [
117
+ {
118
+ "stageNumber": 1,
119
+ "title": "Prepare port",
120
+ "dependsOn": [],
121
+ "stepCount": 2
122
+ },
123
+ {
124
+ "stageNumber": 2,
125
+ "title": "Build adapter",
126
+ "dependsOn": [1],
127
+ "stepCount": 3
128
+ },
129
+ {
130
+ "stageNumber": 3,
131
+ "title": "Wire consumer",
132
+ "dependsOn": [2],
133
+ "stepCount": 2
134
+ }
135
+ ]
136
+ }
137
+ ]
138
+ }
139
+ ```
116
140
 
117
- effort and Gantt reflect engineering duration only.
141
+ Include every candidate task. A `missing` task has an empty `sourcePlanPath`, `selectedStages`, `doneStages`, and `stages`. Convert the CLI stage-row keys to the camel-case selection boundary exactly as shown.
118
142
 
119
- ## phase classification
143
+ ## Phase classification
120
144
 
121
- Default mapping:
145
+ Only these canonical categories are valid:
122
146
 
123
- | workCategory | phase |
147
+ | workCategory | Default phase |
124
148
  |---|---|
125
- | `bugfix` | Phase 1 when High/Med-High risk, otherwise Phase 2 |
149
+ | `bugfix` | Phase 1 for High or Med-High risk; otherwise Phase 2 |
126
150
  | `feature` | Phase 2 |
127
151
  | `improvement` | Phase 2 |
128
152
  | `refactor` | Phase 3 |
129
153
  | `ops` | Phase 3 |
130
- | `docs` / `doc` | Phase 2 |
131
- | `unknown` or undefined | Phase 2, add a rationale note |
132
154
 
133
- priority override:
155
+ Priority overrides category: P0 maps to Phase 1, P1/P2 to Phase 2, and P3 to Phase 3. An unknown or missing raw category falls back to Phase 2 with a one-line rationale naming the raw value. Do not invent another category.
134
156
 
135
- - `P0`: Phase 1
136
- - `P1`, `P2`: Phase 2
137
- - `P3`: Phase 3
157
+ ## Template contract
138
158
 
139
- When ambiguous, place it in the closest phase and write the rationale at the top of the phase section. This rationale is a current validator gap, so the document owns it.
140
-
141
- ## section contract
142
-
143
- Follow the template's heading order and spelling. `validate-schedule.py` checks section order, title suffix, metadata, field labels, enum, Gantt axis, and more.
144
-
145
- top-level contract sections:
159
+ Follow `schedule.template.md` exactly. The required top-level order is:
146
160
 
147
161
  1. `## At a Glance`
148
- 2. `## Executive Summary` (includes the mandatory subsection `### Effort Sizing Criteria` — checked by `validate-schedule.py`)
162
+ 2. `## Executive Summary`
149
163
  3. `## Task Dependency Graph`
150
- 4. `## Phase 1: Critical Fixes`
151
- 5. `## Phase 2: Enhancements`
152
- 6. `## Phase 3: Architecture`
153
- 7. `## Execution Priority Matrix`
154
- 8. `## Cross-Task Dependencies & Shared Concerns`
155
- 9. `## Risk Mitigation Strategy`
156
- 10. `## Recommended Immediate Actions`
157
-
158
- optional sections:
159
-
160
- - `## Gantt Chart`: between `Task Dependency Graph` and `Phase 1`.
161
- - `## Glossary`: last section. Use only when opaque codes remain in the body.
162
-
163
- Keep the heading even when a phase has no task, and write `_none_`.
164
-
165
- ## top header
166
-
167
- Shape:
168
-
169
- ```markdown
170
- # <Title> — Work Schedule
171
-
172
- > Generated: <YYYY-MM-DD HH:MM> | Project: <project-id> | Task Group: <task-group>
173
- > Source: okstra <mode> (<N> tasks included, <M> done excluded)
174
- ```
175
-
176
- The validator checks the title suffix `— Work Schedule`. For `<project-id>`, prefer `task-catalog.json`'s top-level `projectId`, and if absent use the first matched manifest's `projectId`. Do not invent a value.
177
-
178
- ## At a Glance
179
-
180
- This totals line must be present exactly.
181
-
182
- ```markdown
183
- **<N> tasks total / estimated effort: <X.X> ~ <Y.Y> days (Effort sum)**
184
- ```
185
-
186
- The Effort-to-day mapping's SSOT is the template's `### Effort Sizing Criteria` table. Build the total day range by summing the lower/upper bounds.
187
-
188
- enum values:
189
-
190
- | Field | value |
191
- |---|---|
192
- | Effort | `S`, `M`, `L`, `XL`, `XXL` |
193
- | Priority | `P0`, `P1`, `P2`, `P3` |
194
- | Risk | `Very Low`, `Low`, `Medium`, `Med-High`, `High` |
195
- | Phase | `1`, `2`, `3` |
196
-
197
- `Med-High` is canonical.
198
-
199
- ## per-task block
164
+ 4. optional `## Gantt Chart`
165
+ 5. `## Phase 1: Critical Fixes`
166
+ 6. `## Phase 2: Enhancements`
167
+ 7. `## Phase 3: Architecture`
168
+ 8. `## Execution Priority Matrix`
169
+ 9. `## Cross-Task Dependencies & Shared Concerns`
170
+ 10. `## Risk Mitigation Strategy`
171
+ 11. `## Recommended Immediate Actions`
172
+ 12. optional final `## Glossary`
200
173
 
201
- Every task block has a `| Item | Detail |` table. field row order:
174
+ Keep an empty required section and render `_none_`. Headings and field labels stay as English template literals; body prose is Korean.
202
175
 
203
- 1. `**Category**`
204
- 2. `**Priority**`
205
- 3. `**Effort**`
206
- 4. `**Status**`
207
- 5. `**Risk**`
208
- 6. `**Scope**`
209
- 7. `**Repo**`
210
-
211
- Then the subsection order:
212
-
213
- 1. `**Problem**:`
214
- 2. `**Solution**:`
215
- 3. `**Work Breakdown**:` — followed by a `| Step | File | Action | Detail |` table
216
- 4. `**Verification Commands**:` — followed by a ` ```bash ` fenced block
217
- 5. `**Rollback**:`
218
-
219
- For a `[NEEDS-OKSTRA-RUN]` or `[PARSE-ERROR: <section>]` task, fill only the available fields but place the marker right below the task heading.
220
-
221
- ## Task Dependency Graph
222
-
223
- When there is no dependency, literal:
176
+ Each scheduled task uses this stage-level Work Breakdown shape:
224
177
 
225
178
  ```markdown
226
- _none_
227
- ```
228
-
229
- When dependencies exist, use a plain fenced block. Do not attach a language tag.
230
-
231
- ````
179
+ | Stage | Title | Steps | Depends On | Days |
180
+ |---:|---|---:|---|---:|
181
+ | 2 | Build adapter | 3 | 1 (done) | 2.0 ~ 3.0 |
182
+ | 3 | Wire consumer | 2 | 2 | 1.0 ~ 2.0 |
232
183
  ```
233
- DEV-1 -> DEV-2, DEV-3
234
- DEV-2 -> DEV-4
235
- ```
236
- ````
237
-
238
- Use only the ASCII arrow `->`.
239
-
240
- ## Gantt Chart
241
-
242
- **directive override (highest priority):** before applying the heuristic below, first check the directive source's `## Directive` section. Resolution order (first hit): (1) the `--directive-file <abs-path>` argument, (2) `<PROJECT_ROOT>/.okstra/tasks/<task-group-segment>/schedule/instruction-set/analysis-material.md`, (3) if none, the heuristic as-is (normal path — no warning·stop). When the directive instructs Gantt render/skip, it overrides the heuristic·skip rules, and leave one line in that section: `> _Per Directive directive: <verbatim short excerpt>._`. When the directive supplies day allocation·phase weight·sub-task decomposition, reflect it verbatim in bar length.
243
184
 
244
- By default, render. If there is any day signal, produce a chart even as a rough estimate.
185
+ Use the template's Effort Sizing Criteria values without redefining them. Allocate a task's range across selected stages in `stepCount` proportion: round every stage except the last to 0.5 day and let the last absorb the remainder. The stage ranges must sum to the task range, and finite task ranges must sum to the displayed total. XXL, missing, and complete tasks contribute no forward total.
245
186
 
246
- Render-condition examples:
187
+ An unrepresentable half-day allocation is a validation error. Do not substitute a fallback allocation algorithm; revise the task sizing or selected-stage scope.
247
188
 
248
- - 2+ tasks have effort sizing.
249
- - Even 1 task has an effort range.
250
- - The source report has Part/Phase/Step decomposition.
251
- - Total effort is 3 days or more.
252
-
253
- Skip only when every task is XXL with no decomposition, or when all tasks lack both effort and decomposition. On skip, place the following blockquote at the `Gantt Chart` position.
254
-
255
- ```markdown
256
- > _Gantt Chart skipped: <concrete reason referencing the actual data>._
257
- ```
189
+ ## Gantt contract
258
190
 
259
- The Gantt is plain fenced ASCII. Do not use Mermaid, PlantUML, Graphviz, or a date axis.
191
+ Render a plain fenced relative-day Gantt when the selected stages have finite day ranges. Every forward row is identified by stage and repeats its Work Breakdown range:
260
192
 
261
- ````
262
- ```
263
- Day: 1 5 10 15 20
264
- | | | | |
265
- Phase 1
266
- DEV-1 (M) ██████ ! crit
267
- Phase 2
268
- DEV-2 (L) ██████░░ est
193
+ ```text
194
+ DEV-1/S2 ████ days=2.0~3.0
195
+ DEV-1/S3 ████ days=1.0~2.0
269
196
  ```
270
- ````
271
197
 
272
- The axis uses relative day count only. Do not put in a calendar date, weekday, or "today + N".
198
+ The identifier format is `<TASK-ID>/S<stage-number>` and the annotation is `days=<lower>~<upper>`. Do not emit a row for a completed, unselected, missing, or unknown stage. Bar length is illustrative; `days=` is the validated duration contract.
273
199
 
274
- ## handling opaque code
200
+ Skip the chart only when no forward task has a finite day signal, and state the concrete reason. Do not use calendar dates, Mermaid, PlantUML, Graphviz, or another graph language.
275
201
 
276
- Do not surface internal report codes (`FC-5`, `UC-3`, `M2`, etc.) as-is.
202
+ A host-supplied directive or the first `## Directive` in the configured analysis material may override the render/skip heuristic, but it cannot override stage selection, dependency closure, or validated day arithmetic.
277
203
 
278
- Option A: inline replacement with a short description.
204
+ ## Client-facing boundary
279
205
 
280
- Option B: when many codes recur, resolve every code in a final `## Glossary`. The glossary table header is exactly `| Code | Description |` (English literal checked by the validator). Milestone codes (`M1`, `M2` …) also fail the validator when left unresolved in the glossary, just like `FC-N`.
206
+ Assume the team has the required authority. Exclude approval waits, permission checks, stakeholder coordination, decision checklists, and internal blocker codes from forward engineering work. Gantt duration and totals represent engineering work only.
281
207
 
282
- Do not put decision-item letter codes (`A1`, `B2`, `C3`, `D4`) in the schedule.
208
+ Resolve opaque source-report codes inline or in the optional final Glossary. Decision-item codes do not belong in the schedule.
283
209
 
284
- ## validation
210
+ ## Two validation gates
285
211
 
286
- After writing, re-read it and run the validator before the completion message.
212
+ Run both gates against the same draft and temporary selection contract.
287
213
 
288
- ```bash
289
- python3 ~/.okstra/lib/validators/validate-schedule.py <output-path>
290
- ```
214
+ 1. Deterministic gate:
291
215
 
292
- If the installed validator is absent, use the repo validator.
216
+ ```bash
217
+ python3 ~/.okstra/lib/validators/validate-schedule.py <draft> --selection-json <selection>
218
+ ```
293
219
 
294
- ```bash
295
- python3 validators/validate-schedule.py <output-path>
296
- ```
220
+ 2. Only after that command passes, dispatch an independent LLM verifier with the draft and selection JSON, but not the lead's reasoning. It checks narrative coherence, phase rationale, executable order, engineering-only scope, and contradictions with the structured rows.
297
221
 
298
- On failure, fix the file and re-validate. Fall back to a manual checklist only when there is no validator.
222
+ If either gate finds a defect, revise the same draft in place and restart from the deterministic gate. Allow at most two revision rounds across both gates. Never publish a draft that has not passed both gates in that order.
299
223
 
300
- ## completion message
224
+ After both gates pass:
301
225
 
302
- ```text
303
- Schedule generated: <relative-path>
304
- - Included tasks: N
305
- - Excluded (done) tasks: M
306
- - Estimated effort: X.X ~ Y.Y days (Effort sum)
307
- - Mode: lightweight
308
- ```
226
+ 1. Move the same draft content to the collision-safe final path; do not re-render it.
227
+ 2. Re-read it and run the installed format validator on the final path, falling back to the repository validator only when needed.
228
+ 3. Delete the temporary selection file only after final validation passes.
229
+ 4. Report completion in Korean with the output path, included/excluded counts, finite total range, and lead-plus-verifier mode.
309
230
 
310
231
  ## Forbidden patterns
311
232
 
312
- - Translating a heading into Korean/French/etc.
313
- - Adding `## Cumulative Timeline`.
314
- - Adding an extra top-level section such as `## Effort-to-Day mapping`.
315
- - Rendering the Gantt as mermaid.
316
- - Putting a calendar date on the Gantt axis.
317
- - Creating a checkbox, Done, Ready, or Blocking Decisions column.
318
- - Surfacing internal approval/blocker/user-decision items in the client schedule.
319
- - Excluding a task because it has no report. Include it as `[NEEDS-OKSTRA-RUN]`.
320
- - Overwriting an existing file on a timestamp collision. Append a `-2`, `-3` suffix.
233
+ - Guessing a planning report after `stage-map` reports a structured error.
234
+ - Treating `workStatus` as the detailed schedule status.
235
+ - Scheduling completed or non-selected stages.
236
+ - Publishing a task-level Gantt row without the `/S<stage-number>` suffix and `days=` range.
237
+ - Dispatching narrative validation before deterministic `--selection-json` validation.
238
+ - Re-rendering after validation instead of promoting the same draft.
239
+ - Deleting the selection contract before final validation.
240
+ - Publishing after more than two unsuccessful revision rounds.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okstra",
3
- "version": "0.152.0",
3
+ "version": "0.153.0",
4
4
  "description": "Host-aware multi-provider cross-verification orchestrator runtime and agent skills.",
5
5
  "license": "MIT",
6
6
  "author": "devonshin",
@@ -1,5 +1,5 @@
1
1
  {
2
- "package": "0.152.0",
3
- "builtAt": "2026-08-05T09:28:14.647Z",
2
+ "package": "0.153.0",
3
+ "builtAt": "2026-08-05T13:40:55.050Z",
4
4
  "repoRoot": "/home/runner/work/okstra/okstra"
5
5
  }
@@ -245,11 +245,15 @@ fi
245
245
  # non-interactive dispatch from blocking on tool-permission prompts (the worker
246
246
  # is sandboxed to the supplied `--add-dir` workspace).
247
247
  #
248
- # stdout: tee'd to both the live log (for `tail -f`) AND the wrapper's own
249
- # stdout (so the subagent's `BashOutput` still captures the final
250
- # text verbatim for Phase 5 synthesis). Implemented via process
251
- # substitution so agy itself stays a single addressable PID we
252
- # can SIGTERM from the watchdog.
248
+ # stdout: agy runs in `stream-json` so the live log records the tool calls it
249
+ # made in the default `text` format agy prints only its closing
250
+ # summary, leaving ~2KB of self-report where codex leaves ~487KB of
251
+ # trace, and no way to check from outside whether the worker opened the
252
+ # evidence it was asked to verify. `okstra-wrapper-agy-stream.py`
253
+ # appends every event to the log and forwards ONLY the final response
254
+ # text to the wrapper's own stdout, so what the subagent's `BashOutput`
255
+ # captures for Phase 5 synthesis is unchanged by the format switch.
256
+ # agy stays a single addressable PID we can SIGTERM from the watchdog.
253
257
  # stderr: appended to the live log only — keeps the wrapper's stderr clean.
254
258
  # exit: agy's own exit code is preserved by `wait`.
255
259
  # stdout mirror via a named FIFO instead of a `> >(tee …)` process substitution:
@@ -261,7 +265,7 @@ fi
261
265
  stdout_fifo="${log_path}.stdout.fifo"
262
266
  rm -f "$stdout_fifo"
263
267
  mkfifo "$stdout_fifo"
264
- tee -a "$log_path" < "$stdout_fifo" &
268
+ python3 "$script_dir/okstra-wrapper-agy-stream.py" "$log_path" < "$stdout_fifo" &
265
269
  stdout_tee_pid=$!
266
270
 
267
271
  # Disable git's fsmonitor for every git command agy runs in this process tree —
@@ -275,6 +279,7 @@ export "GIT_CONFIG_VALUE_${_gc_idx}=false"
275
279
  export GIT_CONFIG_COUNT="$(( _gc_idx + 1 ))"
276
280
 
277
281
  agy --print "$(cat "$prompt_path")" --model "$model" "${add_dir_args[@]}" \
282
+ --output-format stream-json \
278
283
  --print-timeout "$PRINT_TIMEOUT" --dangerously-skip-permissions \
279
284
  2>> "$log_path" \
280
285
  > "$stdout_fifo" &