okstra 0.26.0 → 0.27.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.kr.md +15 -0
- package/README.md +15 -0
- package/docs/kr/architecture.md +2 -6
- package/docs/kr/cli.md +40 -6
- package/docs/kr/performance-improvement-plan-v2.md +23 -0
- package/docs/kr/performance-improvement-plan.md +22 -0
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/bin/okstra.sh +0 -1
- package/runtime/prompts/profiles/_common-contract.md +25 -1
- package/runtime/prompts/profiles/error-analysis.md +12 -0
- package/runtime/prompts/profiles/implementation-planning.md +20 -0
- package/runtime/prompts/profiles/requirements-discovery.md +20 -0
- package/runtime/python/lib/okstra/cli.sh +1 -7
- package/runtime/python/lib/okstra/globals.sh +0 -1
- package/runtime/python/lib/okstra/usage.sh +1 -4
- package/runtime/python/okstra_ctl/render.py +3 -0
- package/runtime/python/okstra_ctl/run.py +0 -6
- package/runtime/python/okstra_ctl/run_context.py +1 -1
- package/runtime/python/okstra_ctl/wizard.py +25 -2
- package/runtime/python/okstra_token_usage/blocks.py +5 -1
- package/runtime/python/okstra_token_usage/claude.py +16 -1
- package/runtime/python/okstra_token_usage/collect.py +17 -3
- package/runtime/python/okstra_token_usage/pricing.py +159 -24
- package/runtime/skills/okstra-brief/SKILL.md +532 -65
- package/runtime/skills/okstra-context-loader/SKILL.md +25 -11
- package/runtime/skills/okstra-convergence/SKILL.md +37 -13
- package/runtime/skills/okstra-history/SKILL.md +68 -37
- package/runtime/skills/okstra-logs/SKILL.md +26 -4
- package/runtime/skills/okstra-report-finder/SKILL.md +49 -22
- package/runtime/skills/okstra-report-writer/SKILL.md +59 -64
- package/runtime/skills/okstra-run/SKILL.md +35 -34
- package/runtime/skills/okstra-schedule/SKILL.md +51 -20
- package/runtime/skills/okstra-setup/SKILL.md +31 -12
- package/runtime/skills/okstra-status/SKILL.md +20 -8
- package/runtime/skills/okstra-team-contract/SKILL.md +27 -15
- package/runtime/skills/okstra-time-summary/SKILL.md +53 -16
- package/runtime/templates/reports/settings.template.json +7 -4
- package/runtime/validators/lib/fixtures.sh +10 -2
- package/runtime/validators/lib/validate-assets.sh +50 -24
- package/runtime/validators/validate-brief.py +385 -0
- package/runtime/validators/validate-brief.sh +35 -0
- package/runtime/validators/validate-workflow.sh +7 -33
|
@@ -12,7 +12,9 @@ user-invocable: false
|
|
|
12
12
|
- When the user needs to know the okstra task bundle path
|
|
13
13
|
- When you need to derive all artifact paths based on `task-manifest.json`
|
|
14
14
|
|
|
15
|
-
## Step 1:
|
|
15
|
+
## Step 1: Resolve the Task Bundle Path
|
|
16
|
+
|
|
17
|
+
(Resolve which task-root path to use; Step 2 opens `task-manifest.json` at that path.)
|
|
16
18
|
|
|
17
19
|
### Default Location Rules
|
|
18
20
|
|
|
@@ -28,11 +30,12 @@ user-invocable: false
|
|
|
28
30
|
3. If the user attempts to find a task based on `task-group` + `task-id` or `task-id`, `.project-docs/okstra/discovery/task-catalog.json` is read to find candidates.
|
|
29
31
|
4. If multiple candidates are found based on `task-id` alone, the situation is ambiguous, so `task-group` or the full `taskKey` is required.
|
|
30
32
|
5. If the user has not provided an explicit task key/path, first read `.project-docs/okstra/discovery/latest-task.json` using the current-task convenience pointer.
|
|
31
|
-
6.
|
|
33
|
+
6. If the latest-task pointer is missing or corrupted but the task catalog exists, list candidates from the catalog. Do not use the legacy `CLAUDE.md`, project guide, or task scan fallback.
|
|
34
|
+
7. If **neither** `latest-task.json` **nor** `task-catalog.json` exists, ABORT Phase 1 with `OKSTRA_CONTEXT_NOT_INITIALIZED`. Suggest the user run `/okstra-setup` and `/okstra-brief` to bootstrap the project. Do NOT crawl `.project-docs/okstra/tasks/` directly — discovery pointers are the only supported entry path.
|
|
32
35
|
|
|
33
|
-
## Step 2:
|
|
36
|
+
## Step 2: Open and Parse task-manifest.json
|
|
34
37
|
|
|
35
|
-
task-manifest.json is the canonical metadata source.
|
|
38
|
+
`task-manifest.json` (found at the task-root resolved in Step 1) is the canonical metadata source. Extract the following fields:
|
|
36
39
|
|
|
37
40
|
| Field | Description |
|
|
38
41
|
|------|------|
|
|
@@ -53,7 +56,7 @@ task-manifest.json is the canonical metadata source. The following fields must b
|
|
|
53
56
|
| `workflow.awaitingApproval` | Approval wait marker |
|
|
54
57
|
| `workflow.routingStatus` | Routing decision status |
|
|
55
58
|
| `workflow.lastSafeCheckpoint` | Safe resume checkpoint metadata |
|
|
56
|
-
| `instructionSetPath` |
|
|
59
|
+
| `instructionSetPath` | Path to the `instruction-set/` **directory** containing `analysis-profile.md`, `analysis-material.md`, `reference-expectations.md`, `task-brief.md`, `final-report-template.md` (see Step 4). Not a single-file path. |
|
|
57
60
|
| `referenceExpectationsPath` | config/deployment expectation artifact path |
|
|
58
61
|
| `latestRunPath` | latest run path |
|
|
59
62
|
| `latestRunStatus` | latest run status |
|
|
@@ -63,7 +66,7 @@ task-manifest.json is the canonical metadata source. The following fields must b
|
|
|
63
66
|
| `historyTimelinePath` | timeline path |
|
|
64
67
|
| `resultContract` | team contract and expected artifact metadata |
|
|
65
68
|
| `resultContract.requiredWorkerRoles[*].promptPath` | worker prompt history path by role |
|
|
66
|
-
| `convergence` | convergence loop settings (enabled
|
|
69
|
+
| `convergence` | convergence loop settings (`enabled`, `maxRounds`, `verificationMode`). See [okstra-convergence](../okstra-convergence/SKILL.md) for the authoritative defaults — do not re-document the `maxRounds` value here. |
|
|
67
70
|
|
|
68
71
|
## Step 3: Directory Structure Rules
|
|
69
72
|
|
|
@@ -121,12 +124,21 @@ After verifying `task-manifest.json`, read the instruction set in the following
|
|
|
121
124
|
4. `instruction-set/task-brief.md` (task brief)
|
|
122
125
|
5. `instruction-set/final-report-template.md` (report template)
|
|
123
126
|
|
|
127
|
+
### Brief Reporter-Confirmation Precondition (BLOCKING)
|
|
128
|
+
|
|
129
|
+
After reading `task-brief.md`, extract the frontmatter `reporter-confirmations` field (`complete | partial | pending | skipped`). This precondition is shared across every consuming phase — see `prompts/profiles/_common-contract.md` "Brief consumption" block for the authoritative handling matrix.
|
|
130
|
+
|
|
131
|
+
- `complete` or `partial` → proceed to Step 5 and hand off to `okstra-team-contract`.
|
|
132
|
+
- `skipped` → proceed, but flag the unmarked `intent-check:` / `conversion-block:` rows for promotion by the phase profile.
|
|
133
|
+
- `pending` (or field missing) → emit `REPORTER_CONFIRMATION_PENDING` and STOP. Do not invoke `okstra-team-contract` or any analyser. The operator must rerun `okstra-brief` Step 6.5 before Phase 2 can start.
|
|
134
|
+
|
|
124
135
|
## Step 5: Read Run Manifest and Team State
|
|
125
136
|
|
|
126
|
-
1.
|
|
127
|
-
2.
|
|
128
|
-
3.
|
|
129
|
-
4.
|
|
137
|
+
1. Identify the active run by reading `runDateTimeSegment` from the latest `runs/<task-type>/manifests/run-manifest-*.json` (mtime order). That segment is the shared run identifier across all category subdirectories (`state/`, `prompts/`, `reports/`, `status/`, `sessions/`, `worker-results/`).
|
|
138
|
+
2. Resolve sibling artifacts for this run by matching the same `runDateTimeSegment`. Do NOT re-scan `<seq>` counters per category — they may diverge if an earlier run only wrote some categories.
|
|
139
|
+
3. Current team state: the team-state file whose `runDateTimeSegment` matches the active run manifest.
|
|
140
|
+
4. Extract the worker prompt directory path and per-worker prompt history paths from the current run manifest and team-state.
|
|
141
|
+
5. If an existing run report is available, use it solely as historical context.
|
|
130
142
|
|
|
131
143
|
## Output
|
|
132
144
|
|
|
@@ -137,4 +149,6 @@ Information to be obtained after executing this skill:
|
|
|
137
149
|
- Reference list of config files/deployment manifests and task-level expected values
|
|
138
150
|
- Current run status and presence of existing worker results
|
|
139
151
|
- Current run prompt history contract for attempted workers
|
|
140
|
-
-
|
|
152
|
+
- Candidate `teamName` for Phase 3 hand-off: `okstra-<task-key>` (with task-key slugified per Step 1's slug rule)
|
|
153
|
+
- Current Claude `lead.sessionId` (the in-flight Claude Code session) — required by `okstra-team-contract` when registering the lead in `team-state.json`
|
|
154
|
+
- Resume command path: from `task-manifest.json` → `latestResumeCommandPath` (fallback: latest `runs/<task-type>/sessions/claude-resume-*.sh` by mtime). Never reconstruct the filename — the `<seq>` counter is category-local and may diverge from `manifests/`.
|
|
@@ -6,6 +6,23 @@ user-invocable: false
|
|
|
6
6
|
|
|
7
7
|
# OKSTRA Convergence
|
|
8
8
|
|
|
9
|
+
## Index
|
|
10
|
+
|
|
11
|
+
- [Scope and Terminology (BLOCKING)](#scope-and-terminology-blocking)
|
|
12
|
+
- [When to Use](#when-to-use)
|
|
13
|
+
- [Configuration](#configuration)
|
|
14
|
+
- [Finding Category](#finding-category)
|
|
15
|
+
- [Convergence Algorithm](#convergence-algorithm)
|
|
16
|
+
- [Round 0: Parse worker results](#round-0-parse-worker-results)
|
|
17
|
+
- [Round 1-N: Re-verification Loop (queue-pruned)](#round-1-n-re-verification-loop-queue-pruned)
|
|
18
|
+
- [Convergence Test](#convergence-test)
|
|
19
|
+
- [Verification Mode](#verification-mode)
|
|
20
|
+
- [Re-verification Agent Dispatch](#re-verification-agent-dispatch)
|
|
21
|
+
- [Convergence State Artifact](#convergence-state-artifact)
|
|
22
|
+
- [Output](#output)
|
|
23
|
+
- [Convergence Disabled](#convergence-disabled)
|
|
24
|
+
- [Plan-body verification mode (implementation-planning only)](#plan-body-verification-mode-implementation-planning-only)
|
|
25
|
+
|
|
9
26
|
## Scope and Terminology (BLOCKING)
|
|
10
27
|
|
|
11
28
|
This skill governs **Phase 5.5 (Convergence loop)** — a *lead operating phase* inside a single okstra run, not a task-type lifecycle phase. The 6 task-type lifecycle phases (`requirements-discovery` → `error-analysis` → `implementation-planning` → `implementation` → `final-verification` → `release-handoff`, see [okstra/SKILL.md](../../SKILL.md) "Lifecycle Phase Boundaries") are unchanged by this skill. The lead operating phases (Phase 1 Intake → Phase 7 Persist, see [okstra/SKILL.md](../../SKILL.md) "Quick Reference") describe how the lead drives a *single* task-type run.
|
|
@@ -30,6 +47,8 @@ Configure this in the `convergence` block of `task-manifest.json`. If the block
|
|
|
30
47
|
| `maxRounds` | phase-aware: `1` for `requirements-discovery`, `2` otherwise (range 1–3) | Maximum number of re-verification rounds. Discovery's routing/missing-input outputs gain little from a second round; other phases (especially `error-analysis`) keep `2`. Lead resolves the effective value when the manifest omits the key and records it in `config.maxRounds` of the convergence state artifact. |
|
|
31
48
|
| `verificationMode` | `"lightweight"` | `"lightweight"` or `"full-reanalysis"` |
|
|
32
49
|
|
|
50
|
+
**Auto-disable rule (BLOCKING).** Convergence requires ≥2 analyser workers to produce a meaningful consensus tally. When the active profile's `Required workers:` block (see `prompts/profiles/*.md`) resolves to fewer than 2 analyser workers — e.g. `release-handoff` (zero analyser workers, lead-only) — the lead MUST treat `convergence.enabled` as `false` for that run regardless of manifest configuration, skip Phases 5.5 and the plan-body verification round, and record `finalState: "converged"` with `totalRounds: 0` and an explanatory note in `config` (e.g. `"autoDisabled": "fewer-than-two-analysers"`). The plan-body round inherits the same rule via its `gating=false` advisory path.
|
|
51
|
+
|
|
33
52
|
## Finding Category
|
|
34
53
|
|
|
35
54
|
| Category | Definition | Included in Report |
|
|
@@ -37,10 +56,12 @@ Configure this in the `convergence` block of `task-manifest.json`. If the block
|
|
|
37
56
|
| `full-consensus` | All participating workers agree | Required |
|
|
38
57
|
| `partial-consensus` | Majority of workers agree; dissenting opinions are recorded | Required |
|
|
39
58
|
| `contested` | Final classification only. Assigned to a finding that remains in the verification queue after the **last executed round** completes (round index = `effectiveMaxRounds`). Each worker's position across all executed rounds is recorded. NEVER used as an intermediate label. | Required |
|
|
40
|
-
| `worker-unique` | Only the discoverer confirms;
|
|
59
|
+
| `worker-unique` | Only the discoverer confirms and ALL other non-error votes are `DISAGREE`. `verification-error` votes are excluded from the tally per §"Worker failure handling in reverify"; a finding where every non-discoverer vote is `verification-error` is carried forward, never classified `worker-unique`. | Required |
|
|
41
60
|
|
|
42
61
|
## Convergence Algorithm
|
|
43
62
|
|
|
63
|
+
**Majority definition (BLOCKING).** "Majority" means *strictly greater than half* of the non-error votes for that finding (`verification-error` votes are excluded from both numerator and denominator). Ties — including the 1-AGREE / 1-DISAGREE case in a two-analyser roster — are NOT a majority: in intermediate rounds the finding is **carried forward**; in the final executed round the finding is classified `contested`. This rule applies identically to the plan-body verification round (§"Plan-body verification mode") where the same verdict tokens are reused.
|
|
64
|
+
|
|
44
65
|
### Round 0: Parse worker results
|
|
45
66
|
|
|
46
67
|
Read the worker result files generated in Phase 4/5 and extract individual findings.
|
|
@@ -132,7 +153,8 @@ The lead MUST construct the per-worker reverify prompt body from `items_for_W` o
|
|
|
132
153
|
|---|---|---|
|
|
133
154
|
| `effectiveMaxRounds >= 2` | true | `"max-rounds-1"` |
|
|
134
155
|
| `len(queue) > 0` after round 1 | true | `"queue-empty"` |
|
|
135
|
-
|
|
156
|
+
|
|
157
|
+
The third gate condition — "all reverify dispatches terminated as non-result" — is handled inline by the WHILE-loop body (see the `BREAK` on `all dispatches ... terminal non-result` and §"Worker failure handling in reverify" rule 4) which records `round2SkippedReason = "all-reverify-non-result"` and aborts before the predicate is re-evaluated. It is therefore not duplicated as a gate row here.
|
|
136
158
|
|
|
137
159
|
When all conditions hold the predicate returns `true` and `round2SkippedReason` is set to `"not-skipped"`. The field is mandatory on every convergence state artifact — write `"not-skipped"` rather than omitting the key.
|
|
138
160
|
|
|
@@ -152,11 +174,7 @@ The final classifier (`FOR each finding F still in queue` block) treats `verific
|
|
|
152
174
|
|
|
153
175
|
### Convergence Test
|
|
154
176
|
|
|
155
|
-
-
|
|
156
|
-
- Upon completing the **last executed round** (where round index == `effectiveMaxRounds`, OR where Round 2 was suppressed per the Round 2 gate below) → Apply final classification to remaining queue items:
|
|
157
|
-
- Majority agreement across executed rounds → `partial-consensus`
|
|
158
|
-
- Otherwise → `contested`
|
|
159
|
-
- The final classification step never runs while the queue is still being re-verified — confirmed items always exit the queue first.
|
|
177
|
+
The exit conditions and final-classification rules are defined by the §"Convergence Algorithm" pseudocode (the `WHILE` exit, the post-loop `FOR each finding F still in queue` block, and the `finalState` mapping in §"Convergence State Artifact"). This is the single source — no separate prose copy is maintained here to prevent drift.
|
|
160
178
|
|
|
161
179
|
## Verification Mode
|
|
162
180
|
|
|
@@ -383,9 +401,11 @@ Schema rules:
|
|
|
383
401
|
- `schemaVersion`: literal string `"1.1"` for new runs. Readers MUST accept `"1.0"` for historical artifacts and treat any missing v1.1 field as `null`.
|
|
384
402
|
- `config.effectiveMaxRounds`: the integer the lead actually used after resolving the phase-aware default (`1` for `requirements-discovery`, `2` otherwise). MUST equal `config.maxRounds` when the manifest explicitly set it.
|
|
385
403
|
- `findings[].ticketIds`: array of ticket keys from Phase 4 grouping (parsed per the Round 0 step 5 rule). MAY be empty when the discovering worker tagged the finding `unknown`.
|
|
404
|
+
- `findings[].rounds[].votes.<worker>.verdict`: enum, one of `agree | disagree | supplement | verification-error`. Lower-case tokens; map upper-case AGREE/DISAGREE/SUPPLEMENT verdicts emitted by workers to their lower-case form before persisting. `verification-error` is reserved for terminal non-result dispatches (§"Worker failure handling in reverify").
|
|
405
|
+
- `findings[].classification`: enum, one of `full-consensus | partial-consensus | worker-unique | contested`. No other value is permitted in v1.1.
|
|
386
406
|
- `roundHistory[].inputQueueSize`: queue size at the start of this round.
|
|
387
407
|
- `roundHistory[].resolvedCount`: number of findings that exited the queue this round (sum of full+partial+worker-unique classifications produced this round).
|
|
388
|
-
- `roundHistory[].carriedForwardCount`: queue size at the END of this round
|
|
408
|
+
- `roundHistory[].carriedForwardCount`: queue size at the END of this round — the single definition. In-round insertions into the queue are forbidden, so this always equals `inputQueueSize - resolvedCount`. The pseudocode's per-item `carriedForwardCount += 1` accumulator is a counting convenience that lands on the same value; persist the post-round queue length, not the loop accumulator, if the two ever diverge.
|
|
389
409
|
- `roundHistory[].dispatches[]`: one entry per worker that was actually dispatched in this round. Each entry is `{worker, status, durationMs}`. `status ∈ {completed, timeout, error, not-run}`. `durationMs` is integer milliseconds and is always present, even for terminal-non-result dispatches (use the elapsed time before the wrapper gave up).
|
|
390
410
|
- `roundHistory[].skippedWorkers[]`: per-worker `{worker, reason}` for workers with no items to verify OR with a non-result dispatch.
|
|
391
411
|
- `roundHistory[].verificationsRequested|verificationsCompleted|newConsensus|remainingInQueue|earlyExit`: legacy v1.0 aliases. New runs SHOULD populate them so existing parsers keep working: `verificationsRequested == len(dispatches)`, `verificationsCompleted == len(d for d in dispatches if d.status == "completed")`, `newConsensus == resolvedCount`, `remainingInQueue == carriedForwardCount`, `earlyExit == (round < effectiveMaxRounds AND carriedForwardCount == 0)`.
|
|
@@ -433,7 +453,7 @@ The finding queue (Phase 5.5) and the plan-item queue (this section) are **disjo
|
|
|
433
453
|
|
|
434
454
|
- A finding-convergence reverify prompt MUST NOT contain any `P-*` item.
|
|
435
455
|
- A plan-body verification prompt MUST NOT contain any `F-*` finding.
|
|
436
|
-
- The two rounds write to **different state files**: `runs/<task-type>/state/convergence-
|
|
456
|
+
- The two rounds write to **different state files**: `runs/<task-type>/state/convergence-<task-type>-<seq>.json` (findings, see §"Convergence State Artifact") vs. `runs/<task-type>/state/plan-body-verification-<task-type>-<seq>.json` (plan items, see §"`plan-body-verification.json` schema").
|
|
437
457
|
- Aggregation logic (verdict counting, classification) MUST NOT carry votes from one queue into the other.
|
|
438
458
|
|
|
439
459
|
Mixing the two queues — for example, parsing a Phase 6 draft's Stepwise Execution Order step as if it were an `F-*` finding — is a contract violation. Future Claude reading this skill: if you find yourself tempted to "just reuse the finding queue for plan items, they're similar enough", stop. They are not similar enough; the verdict semantics differ (see §"Plan-body verdict semantics" below).
|
|
@@ -493,15 +513,15 @@ Plan-body verification only supports **lightweight mode** (defined in §"Verific
|
|
|
493
513
|
4. After all dispatches return, lead aggregates verdicts per `P-*` item across workers and classifies each:
|
|
494
514
|
- `full-consensus` — all participating analysers `AGREE` (SUPPLEMENT counts as agree on the item itself).
|
|
495
515
|
- `partial-consensus` — majority `AGREE`, dissenting `DISAGREE` recorded.
|
|
496
|
-
- `
|
|
516
|
+
- `dissent-isolated` — only one worker `DISAGREE`s, others `AGREE` — treat as `partial-consensus` for gate purposes; record dissent. (Distinct from finding-convergence `worker-unique`, which means the *opposite*: only one worker AGREEs. Plan-body classifications use this dedicated label to avoid the collision.)
|
|
497
517
|
- `majority-disagree` — majority of analysers `DISAGREE` on this item. This is the only classification that **blocks the Approval marker**.
|
|
498
518
|
- `contested` only meaningful when `maxRounds > 1`; at default `maxRounds=1`, fold any unresolved item into `partial-consensus`.
|
|
499
519
|
5. Gate result resolution:
|
|
500
520
|
- any `majority-disagree` item present AND `gating=true` → `blocked-by-disagreement`
|
|
501
521
|
- all dispatches non-result → `aborted-non-result`
|
|
502
|
-
- any `partial-consensus` / `
|
|
522
|
+
- any `partial-consensus` / `dissent-isolated` present, no `majority-disagree` → `passed-with-dissent`
|
|
503
523
|
- all items `full-consensus` → `passed`
|
|
504
|
-
6. Lead writes `runs/<task-type>/state/plan-body-verification
|
|
524
|
+
6. Lead writes `runs/<task-type>/state/plan-body-verification-<task-type>-<seq>.json` (schema below) and populates `### 4.5.9 Plan Body Verification` in the final report (template at `templates/reports/final-report.template.md`).
|
|
505
525
|
7. For every `majority-disagree` item, lead adds a row to `## 5. Clarification Items` with:
|
|
506
526
|
- new `C-<N>` ID (numbering continues from any existing rows)
|
|
507
527
|
- `Statement` summarising the disagreement and the worker breakage `<kind>`
|
|
@@ -510,7 +530,7 @@ Plan-body verification only supports **lightweight mode** (defined in §"Verific
|
|
|
510
530
|
- the §4.5.9 verdict table's `Classification` column for that row reads `majority-disagree → C-<N>` (1:1 ID match — orphan on either side is a contract violation per `prompts/profiles/implementation-planning.md` self-review step 6).
|
|
511
531
|
8. The top-of-report `- [ ] Approved` marker line is rendered if and only if the Gate result is `passed` or `passed-with-dissent`. `validators/validate-run.py` `validate_phase_boundary` enforces this correspondence; manually adding the marker line when the gate did not pass is a contract violation.
|
|
512
532
|
|
|
513
|
-
### `plan-body-verification
|
|
533
|
+
### `plan-body-verification-<task-type>-<seq>.json` schema
|
|
514
534
|
|
|
515
535
|
```json
|
|
516
536
|
{
|
|
@@ -547,6 +567,10 @@ Plan-body verification only supports **lightweight mode** (defined in §"Verific
|
|
|
547
567
|
|
|
548
568
|
`dispatches[].terminalStatus` mirrors finding convergence (`completed | timeout | error | not-run | cli-failure`).
|
|
549
569
|
|
|
570
|
+
`planItems[].classification` enum: `full-consensus | partial-consensus | dissent-isolated | majority-disagree | contested`. `contested` only appears when `maxRounds > 1`; at default `maxRounds=1` any otherwise-unresolved item folds into `partial-consensus` per the round protocol above.
|
|
571
|
+
|
|
572
|
+
`planItems[].votes.<worker>` is the verbatim verdict token emitted by the worker — `AGREE | DISAGREE(<a|b|c|d|e>) | SUPPLEMENT` — or `verification-error` for terminal non-result dispatches. The `DISAGREE` token retains its `<kind>` suffix so the breakage class is recoverable from the state file alone.
|
|
573
|
+
|
|
550
574
|
### Plan-body reverify prompt
|
|
551
575
|
|
|
552
576
|
Required prompt anchor headers are identical to finding convergence (see §"Required reverify-prompt anchor headers"). The prompt body changes from F-* listing to P-* listing:
|
|
@@ -7,9 +7,22 @@ description: Use when the user asks to list past okstra runs, check execution hi
|
|
|
7
7
|
|
|
8
8
|
## When to Use
|
|
9
9
|
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
10
|
+
- List past okstra task executions (one row per task, with latest-run summary).
|
|
11
|
+
- Drill into the per-run history of a specific task.
|
|
12
|
+
- Build a new run from an old run's parameters (re-run), or continue an in-flight one (resume).
|
|
13
|
+
|
|
14
|
+
This skill is for **listing / re-dispatching**. For reading a single final report by task-key, use `okstra-report-finder` instead.
|
|
15
|
+
|
|
16
|
+
## Re-run vs Resume — decide upfront
|
|
17
|
+
|
|
18
|
+
Before invoking Step 3 or Step 4, classify the user's intent. The two paths are NOT interchangeable.
|
|
19
|
+
|
|
20
|
+
| Intent | Trigger phrases | Use |
|
|
21
|
+
|---|---|---|
|
|
22
|
+
| **Re-run** — start a fresh run (new run-seq, new manifest, new report) reusing an old run's parameters | "re-run", "다시 실행", "another pass", "rerun with same brief" | Step 3 |
|
|
23
|
+
| **Resume** — continue an interrupted Claude session for an existing run, no new run-seq | "resume", "continue", "이어서", "session ended" | Step 4 |
|
|
24
|
+
|
|
25
|
+
If the user is ambiguous, ask. Defaulting to the wrong one either wastes a fresh run-seq or silently abandons a recoverable session.
|
|
13
26
|
|
|
14
27
|
## Step 0: Verify okstra runtime + project setup
|
|
15
28
|
|
|
@@ -38,31 +51,45 @@ use `projectRoot` to locate `.project-docs/okstra/discovery/task-catalog.json`.
|
|
|
38
51
|
## Step 1: Read the Task Catalog
|
|
39
52
|
|
|
40
53
|
1. Read `.project-docs/okstra/discovery/task-catalog.json`.
|
|
41
|
-
2.
|
|
42
|
-
|
|
54
|
+
2. Apply filters from user input (all optional, AND-combined):
|
|
55
|
+
- `--task-type <type>` → keep entries whose `taskType` matches.
|
|
56
|
+
- `--latest-run-status <status>` → keep entries whose `latestRunStatus` matches (e.g. `completed`, `contract-violated`, `error`).
|
|
57
|
+
- `--task-group <group>` → keep entries whose `taskGroup` matches.
|
|
58
|
+
3. Sort the surviving `tasks` array by `updatedAt` descending.
|
|
59
|
+
4. Page: default `--limit 20`. After printing the table, if rows were truncated, add `... <N> more (pass --limit <N> to see all)`.
|
|
60
|
+
5. Extract the following fields from each task:
|
|
43
61
|
|
|
44
62
|
| Field | Description |
|
|
45
63
|
|------|------|
|
|
46
|
-
| `taskKey` | Task identifier
|
|
47
|
-
| `taskType` |
|
|
48
|
-
| `currentStatus` | Task-
|
|
49
|
-
| `latestRunStatus` |
|
|
50
|
-
| `
|
|
64
|
+
| `taskKey` | Task identifier — always 3 colon-separated segments: `<project-id>:<task-group>:<task-id>` (see `parse_task_key` in `okstra_project/state.py`). |
|
|
65
|
+
| `taskType` | `requirements-discovery` / `error-analysis` / `implementation-planning` / `implementation` / `final-verification` / `release-handoff` |
|
|
66
|
+
| `currentStatus` | Task lifecycle status written by the contract validator. Values: `todo` (seeded by spawn-followups), `completed`, `contract-violated`. Empty string = validator has not yet run. NOT the same as the user-managed `workStatus` (managed by `okstra-status`). |
|
|
67
|
+
| `latestRunStatus` | Status of the most recent run (`completed`, `contract-violated`, `error`, ...) |
|
|
68
|
+
| `latestRunManifestPath` | Run-manifest path of the most recent run — feed this into Step 3 to re-run from the latest parameters |
|
|
69
|
+
| `updatedAt` | Last update time (ISO 8601) |
|
|
51
70
|
| `latestReportPath` | Latest report path |
|
|
52
|
-
| `latestResumeCommandPath` | Resume command path |
|
|
71
|
+
| `latestResumeCommandPath` | Resume command path (Step 4) |
|
|
72
|
+
| `historyTimelinePath` | `<task-root>/history/timeline.json` (Step 2 reads from here) |
|
|
53
73
|
|
|
54
|
-
|
|
74
|
+
6. Output format:
|
|
55
75
|
|
|
56
76
|
```markdown
|
|
57
77
|
## okstra Task History — <project-id>
|
|
58
78
|
|
|
59
|
-
| # | Task Key | Type |
|
|
60
|
-
|
|
61
|
-
| 1 | proj:group:id | error-analysis | completed | 2026-04-05 22:59 | .project-docs/.../final-report-*.md |
|
|
62
|
-
| 2 | proj:group:id2 | final-verification |
|
|
79
|
+
| # | Task Key | Type | currentStatus | latestRunStatus | Last Run | Report |
|
|
80
|
+
|---|----------|------|---------------|------------------|----------|--------|
|
|
81
|
+
| 1 | proj:group:id | error-analysis | completed | completed | 2026-04-05 22:59 | .project-docs/.../final-report-*.md |
|
|
82
|
+
| 2 | proj:group:id2 | final-verification | todo | error | 2026-04-04 15:30 | -- |
|
|
63
83
|
```
|
|
64
84
|
|
|
65
|
-
|
|
85
|
+
### Catalog absent — fallback
|
|
86
|
+
|
|
87
|
+
If `.project-docs/okstra/discovery/task-catalog.json` does not exist, do NOT bail out. The catalog is a derived index — manifests on disk are the source of truth.
|
|
88
|
+
|
|
89
|
+
1. Glob `<projectRoot>/.project-docs/okstra/tasks/*/*/task-manifest.json`.
|
|
90
|
+
2. For each manifest, read `taskKey`, `taskGroup`, `taskType`, `currentStatus`, `latestRunStatus`, `updatedAt`, `latestReportPath`, `latestResumeCommandPath`, `latestRunManifestPath`, `historyTimelinePath`.
|
|
91
|
+
3. Apply the same filters/sort/limit and print the same table, prefixed with: `note: task-catalog.json missing; reconstructed from task manifests on disk.`
|
|
92
|
+
4. Only if the glob yields zero manifests: respond `There is no okstra execution history yet.`
|
|
66
93
|
|
|
67
94
|
## Step 2: Run History by Task
|
|
68
95
|
|
|
@@ -78,9 +105,10 @@ When a user selects a specific task or requests detailed history:
|
|
|
78
105
|
| `runDateTimeSegment` | YYYY-MM-DD_HH-MM-SS |
|
|
79
106
|
| `taskType` | `--task-type` argument value |
|
|
80
107
|
| `status` | Run status |
|
|
81
|
-
| `
|
|
82
|
-
| `
|
|
83
|
-
| `
|
|
108
|
+
| `runManifestPath` | This run's `run-manifest-*.json` — feed into Step 3 to re-run from this specific run's parameters |
|
|
109
|
+
| `reportPath` | Final report path |
|
|
110
|
+
| `resumeCommandPath` | Resume Claude session for this run (Step 4) |
|
|
111
|
+
| `relatedTasks` | List of related task-keys |
|
|
84
112
|
|
|
85
113
|
4. Output format:
|
|
86
114
|
|
|
@@ -93,23 +121,26 @@ When a user selects a specific task or requests detailed history:
|
|
|
93
121
|
| 2 | 2026-04-04 15:30 | error-analysis | error | -- |
|
|
94
122
|
```
|
|
95
123
|
|
|
96
|
-
## Step 3:
|
|
124
|
+
## Step 3: Re-run (build a NEW run from an old run's parameters)
|
|
97
125
|
|
|
98
|
-
|
|
126
|
+
This builds a **fresh run** — new run-seq, new manifest, new report — using the parameters captured in a previous `run-manifest-*.json`. It does NOT touch the old run's artifacts; use Step 4 if the user wants to continue an interrupted session instead.
|
|
99
127
|
|
|
100
|
-
1.
|
|
101
|
-
2.
|
|
128
|
+
1. Pick the source run-manifest: the `runManifestPath` from a Step 2 timeline entry, or the task's `latestRunManifestPath` from Step 1.
|
|
129
|
+
2. Read the run-manifest JSON and extract required arguments:
|
|
102
130
|
- `projectId` → `--project-id`
|
|
103
131
|
- `taskGroup` → `--task-group`
|
|
104
132
|
- `taskId` → `--task-id`
|
|
105
133
|
- `taskType` → `--task-type`
|
|
106
134
|
- `taskBriefPath` → `--task-brief`
|
|
107
|
-
3. Extract the
|
|
135
|
+
3. Extract optional arguments (include only when present in the source manifest):
|
|
108
136
|
- `recommendedWorkers` → `--workers` (comma-separated)
|
|
109
|
-
- `relatedTasks` → `--related-tasks`
|
|
110
|
-
- model overrides → `--claude-model`, `--codex-model`, `--gemini-model` (
|
|
111
|
-
- for `taskType: implementation`: `teamContract.executor.provider` → `--executor <claude|codex|gemini>` (
|
|
112
|
-
4.
|
|
137
|
+
- `relatedTasks` → `--related-tasks`
|
|
138
|
+
- model overrides → `--claude-model`, `--codex-model`, `--gemini-model` (when different from default)
|
|
139
|
+
- for `taskType: implementation`: `teamContract.executor.provider` → `--executor <claude|codex|gemini>` (when different from `claude`)
|
|
140
|
+
4. **`taskType: implementation` only — resolve `--base-ref`:** the base ref is NOT stored in the run-manifest; it lives in the worktree registry at `~/.okstra/worktrees/registry.json` against the registered branch. Before assembling the command:
|
|
141
|
+
- If a worktree for this task-key is already registered, the existing branch & base are reused — omit `--base-ref` unless the user explicitly wants a different starting point.
|
|
142
|
+
- If no worktree is registered (e.g. it was cleaned up), `--base-ref` is mandatory. Ask the user for the ref to branch from (e.g. `main`, a commit SHA, a tag) before running.
|
|
143
|
+
5. Display the assembled command:
|
|
113
144
|
|
|
114
145
|
```bash
|
|
115
146
|
okstra.sh \
|
|
@@ -121,23 +152,23 @@ okstra.sh \
|
|
|
121
152
|
--workers <worker-list>
|
|
122
153
|
```
|
|
123
154
|
|
|
124
|
-
|
|
155
|
+
6. Once the user confirms, execute it using the Bash tool.
|
|
125
156
|
|
|
126
|
-
## Step 4: Resume
|
|
157
|
+
## Step 4: Resume (continue an interrupted run)
|
|
127
158
|
|
|
128
|
-
|
|
159
|
+
This continues an existing Claude session for a run that did not finish. It does NOT create a new run-seq — for a fresh dispatch, use Step 3.
|
|
129
160
|
|
|
130
|
-
1.
|
|
131
|
-
2. Verify
|
|
132
|
-
3. If
|
|
161
|
+
1. Read `latestResumeCommandPath` from the task catalog (Step 1) — or `resumeCommandPath` from a specific timeline entry (Step 2).
|
|
162
|
+
2. Verify the file exists on disk.
|
|
163
|
+
3. If present:
|
|
133
164
|
```bash
|
|
134
165
|
bash <resume-command-path>
|
|
135
166
|
```
|
|
136
|
-
4. If
|
|
167
|
+
4. If absent, report: `No resume script available for this run. Use Step 3 to start a fresh run instead.`
|
|
137
168
|
|
|
138
169
|
## Output Rules
|
|
139
170
|
|
|
140
171
|
- Display concisely in a table format
|
|
141
172
|
- Dates in `YYYY-MM-DD HH:MM` format
|
|
142
|
-
- Display status as-is (`completed`, `
|
|
173
|
+
- Display status fields as-is from disk (`completed`, `contract-violated`, `todo`, `error`, empty, ...). Do not normalize or remap.
|
|
143
174
|
- Display `--` if no report is available
|
|
@@ -36,6 +36,9 @@ multi-MB logs; analysis-phase dispatches are typically smaller.
|
|
|
36
36
|
|
|
37
37
|
## Step 0: Verify okstra runtime + project setup
|
|
38
38
|
|
|
39
|
+
Before any other step, ensure both the okstra runtime and the current
|
|
40
|
+
project's okstra metadata are in place:
|
|
41
|
+
|
|
39
42
|
```bash
|
|
40
43
|
if command -v okstra >/dev/null 2>&1; then
|
|
41
44
|
OKSTRA_CMD="okstra"
|
|
@@ -46,6 +49,8 @@ $OKSTRA_CMD ensure-installed >/dev/null 2>&1 || {
|
|
|
46
49
|
echo "FAIL: okstra not installed; tell the user to run: npx okstra@latest install" >&2
|
|
47
50
|
exit 1
|
|
48
51
|
}
|
|
52
|
+
eval "$($OKSTRA_CMD paths --shell)"
|
|
53
|
+
export PYTHONPATH="$OKSTRA_PYTHONPATH"
|
|
49
54
|
OKSTRA_PROJECT_INFO="$($OKSTRA_CMD check-project --json)" || {
|
|
50
55
|
echo "FAIL: this project has no okstra setup. Tell the user to run /okstra-setup first." >&2
|
|
51
56
|
echo "$OKSTRA_PROJECT_INFO" >&2
|
|
@@ -68,7 +73,7 @@ LOGS_ROOT="$PROJECT_ROOT/.project-docs/okstra/tasks"
|
|
|
68
73
|
# columns: size_bytes | mtime_epoch | path
|
|
69
74
|
find "$LOGS_ROOT" -type f -path '*/runs/*/prompts/*.log' \
|
|
70
75
|
-printf '%s\t%T@\t%p\n' 2>/dev/null \
|
|
71
|
-
| sort -
|
|
76
|
+
| sort -k1,1nr
|
|
72
77
|
```
|
|
73
78
|
|
|
74
79
|
On macOS, `find -printf` is unavailable. Fall back to `stat`:
|
|
@@ -78,7 +83,7 @@ find "$LOGS_ROOT" -type f -path '*/runs/*/prompts/*.log' 2>/dev/null \
|
|
|
78
83
|
| while IFS= read -r p; do
|
|
79
84
|
stat -f '%z%t%m%t%N' "$p"
|
|
80
85
|
done \
|
|
81
|
-
| sort -
|
|
86
|
+
| sort -k1,1nr
|
|
82
87
|
```
|
|
83
88
|
|
|
84
89
|
If the result is empty, report `No wrapper log files found under <PROJECT_ROOT>` and exit.
|
|
@@ -117,27 +122,40 @@ Total: N files, X.X MB across M tasks under <PROJECT_ROOT>
|
|
|
117
122
|
## Step 3: Suggested cleanup commands
|
|
118
123
|
|
|
119
124
|
Emit a fenced bash block the user can copy-paste. Do NOT execute these.
|
|
125
|
+
Each block pairs a dry-run preview (`-print`) with the destructive
|
|
126
|
+
(`-delete`) command so the user can confirm the match set before
|
|
127
|
+
committing.
|
|
120
128
|
|
|
121
129
|
```markdown
|
|
122
130
|
## Cleanup options (manual)
|
|
123
131
|
|
|
124
132
|
# 7일 이상 된 로그만 삭제
|
|
133
|
+
find <PROJECT_ROOT>/.project-docs/okstra/tasks \
|
|
134
|
+
-type f -path '*/runs/*/prompts/*.log' -mtime +7 -print # dry-run
|
|
125
135
|
find <PROJECT_ROOT>/.project-docs/okstra/tasks \
|
|
126
136
|
-type f -path '*/runs/*/prompts/*.log' -mtime +7 -delete
|
|
127
137
|
|
|
128
138
|
# 30일 이상 된 로그만 삭제
|
|
139
|
+
find <PROJECT_ROOT>/.project-docs/okstra/tasks \
|
|
140
|
+
-type f -path '*/runs/*/prompts/*.log' -mtime +30 -print # dry-run
|
|
129
141
|
find <PROJECT_ROOT>/.project-docs/okstra/tasks \
|
|
130
142
|
-type f -path '*/runs/*/prompts/*.log' -mtime +30 -delete
|
|
131
143
|
|
|
132
144
|
# 특정 task-group 의 로그 일괄 삭제 (예: dev-9388)
|
|
145
|
+
find <PROJECT_ROOT>/.project-docs/okstra/tasks/dev-9388 \
|
|
146
|
+
-type f -name '*.log' -print # dry-run
|
|
133
147
|
find <PROJECT_ROOT>/.project-docs/okstra/tasks/dev-9388 \
|
|
134
148
|
-type f -name '*.log' -delete
|
|
135
149
|
|
|
136
150
|
# 특정 task-id 의 로그 일괄 삭제 (예: dev-9428)
|
|
151
|
+
find <PROJECT_ROOT>/.project-docs/okstra/tasks/*/dev-9428 \
|
|
152
|
+
-type f -name '*.log' -print # dry-run
|
|
137
153
|
find <PROJECT_ROOT>/.project-docs/okstra/tasks/*/dev-9428 \
|
|
138
154
|
-type f -name '*.log' -delete
|
|
139
155
|
|
|
140
156
|
# 전체 일괄 삭제 (주의)
|
|
157
|
+
find <PROJECT_ROOT>/.project-docs/okstra/tasks \
|
|
158
|
+
-type f -path '*/runs/*/prompts/*.log' -print # dry-run
|
|
141
159
|
find <PROJECT_ROOT>/.project-docs/okstra/tasks \
|
|
142
160
|
-type f -path '*/runs/*/prompts/*.log' -delete
|
|
143
161
|
```
|
|
@@ -152,10 +170,14 @@ End the response with these short reminders:
|
|
|
152
170
|
- Logs are truncated on each re-dispatch of the same `seq`, so deleting an
|
|
153
171
|
in-flight run's log will cause the wrapper to recreate an empty file on
|
|
154
172
|
the next dispatch — no data loss beyond the current trace.
|
|
173
|
+
- **If a dispatch is currently running, check `okstra status` first** and
|
|
174
|
+
avoid deleting logs for tasks in `in-progress` state — you will lose
|
|
175
|
+
the live trace for the active run.
|
|
155
176
|
- Prompt history files (`.md`) are separate and are NOT touched by these
|
|
156
177
|
commands — only `.log` sidecars.
|
|
157
|
-
- This skill does not modify
|
|
158
|
-
`.project-docs/okstra/`, the user
|
|
178
|
+
- This skill **does not modify any external files itself**, including
|
|
179
|
+
`.gitignore`. If the project commits `.project-docs/okstra/`, the user
|
|
180
|
+
may want to add
|
|
159
181
|
`.project-docs/okstra/tasks/**/runs/**/prompts/*.log` to `.gitignore`
|
|
160
182
|
manually to keep large logs out of git.
|
|
161
183
|
|
|
@@ -40,54 +40,81 @@ OKSTRA_PROJECT_INFO="$($OKSTRA_CMD check-project --json)" || {
|
|
|
40
40
|
|
|
41
41
|
task-key 형식: `<project-id>:<task-group>:<task-id>`
|
|
42
42
|
|
|
43
|
+
**Normalization**: task-key 매칭은 lowercase 로 수행한다. 디스크상의 group/id 세그먼트는 slugify (lowercase + non-alphanumeric runs → `-`) 된다 (`scripts/lib/okstra/interactive.sh:147`). 따라서 catalog 조회는 case-insensitive 이지만 파일 경로를 만들 때는 slugified segment 를 사용해야 한다.
|
|
44
|
+
|
|
43
45
|
### 방법 A: task-catalog.json (빠름)
|
|
44
46
|
|
|
45
47
|
1. `.project-docs/okstra/discovery/task-catalog.json`을 읽는다.
|
|
46
|
-
2. `tasks` 배열에서 `taskKey
|
|
47
|
-
3. `latestReportPath`
|
|
48
|
+
2. `tasks` 배열에서 `taskKey` 를 lowercase 비교로 매칭한다.
|
|
49
|
+
3. `latestReportPath` 필드는 task-type 과 무관한 "가장 최근 보고서" 이다.
|
|
48
50
|
|
|
49
51
|
### 방법 B: task-manifest.json (직접)
|
|
50
52
|
|
|
51
|
-
task-catalog가
|
|
53
|
+
task-catalog 가 없는 경우:
|
|
54
|
+
|
|
55
|
+
1. task-key 에서 task-group / task-id 를 추출하고 slugify 한다.
|
|
56
|
+
2. `.project-docs/okstra/tasks/<group-segment>/<id-segment>/task-manifest.json` 을 읽는다.
|
|
57
|
+
3. `latestReportPath` 필드 (task-type 무관, 최신).
|
|
58
|
+
|
|
59
|
+
### 방법 C: timeline.json (특정 run 의 보고서)
|
|
52
60
|
|
|
53
|
-
|
|
54
|
-
2. `.project-docs/okstra/tasks/<task-group>/<task-id>/task-manifest.json`을 읽는다.
|
|
55
|
-
3. `latestReportPath` 필드가 최신 보고서 경로이다.
|
|
61
|
+
특정 날짜나 run 의 보고서가 필요한 경우:
|
|
56
62
|
|
|
57
|
-
|
|
63
|
+
1. `.project-docs/okstra/tasks/<group-segment>/<id-segment>/history/timeline.json` 을 읽는다.
|
|
64
|
+
2. `runs[]` 배열에서 필터한다. 실제 필드:
|
|
65
|
+
- `runs[].runTimestamp` (ISO-8601 시작 시각)
|
|
66
|
+
- `runs[].status` (run 종료 상태)
|
|
67
|
+
- `runs[].taskType` (`requirements-discovery` 등 phase 이름)
|
|
68
|
+
- `runs[].reportPath` (해당 run 의 보고서 상대 경로)
|
|
58
69
|
|
|
59
|
-
특정
|
|
70
|
+
### 방법 D: 특정 task-type 의 최신 보고서 (fallback)
|
|
60
71
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
72
|
+
`latestReportPath` (방법 A/B) 는 task-type 을 가리지 않는다. 사용자가 *특정* task-type (예: `implementation-planning`) 의 최신 보고서를 요청하면:
|
|
73
|
+
|
|
74
|
+
1. 디렉토리: `.project-docs/okstra/tasks/<group-segment>/<id-segment>/runs/<task-type-segment>/reports/`
|
|
75
|
+
2. 파일명 패턴: `final-report-<task-type-segment>-<NNN>.md` (`scripts/okstra_ctl/sequence.py:31`)
|
|
76
|
+
3. seq 가 가장 큰 파일이 해당 task-type 의 최신 보고서.
|
|
77
|
+
4. timeline.json 의 `runs[].taskType == <task-type>` 으로 교차검증 가능.
|
|
64
78
|
|
|
65
79
|
## Step 2: Report 존재 확인
|
|
66
80
|
|
|
67
|
-
1.
|
|
81
|
+
1. `latestReportPath`가 비어있지 않고, 해당 경로의 파일이 실제로 존재하는지 확인한다.
|
|
82
|
+
- 두 신호 중 하나라도 보고서 존재를 가리키면 경로를 표시한다 (tolerant).
|
|
68
83
|
2. 존재하면 경로를 표시하고 읽을지 사용자에게 확인한다.
|
|
69
|
-
3. 존재하지
|
|
70
|
-
- `
|
|
71
|
-
-
|
|
72
|
-
|
|
84
|
+
3. 존재하지 않으면 `task-manifest.json`에서 다음 신호를 확인한다:
|
|
85
|
+
- `latestReportPath`가 비어있거나 누락된 경우, 그리고
|
|
86
|
+
- `currentStatus`가 `completed`가 아니고, `workStatus`가 `done`이 아니며, `workflow.routingStatus`도 완료를 가리키지 않는 경우
|
|
87
|
+
→ "이 task는 아직 완료되지 않았습니다 (currentStatus: `<currentStatus>`, workStatus: `<workStatus>`)."
|
|
88
|
+
- 위 신호 중 하나라도 완료를 가리키는데 파일만 없는 경우
|
|
89
|
+
→ "보고서 파일이 존재하지 않습니다: `<path>`"
|
|
90
|
+
|
|
91
|
+
워크플로 상태 enum은 `todo | in-progress | blocked | done` (workStatus) 와 `currentStatus`의 `completed` / `contract-violated` 등이다. `"completed"` 문자열은 `workStatus`에는 존재하지 않으므로 두 필드를 혼동하지 말 것.
|
|
73
92
|
|
|
74
93
|
## Step 3: Report 읽기 및 후속 작업 안내
|
|
75
94
|
|
|
76
95
|
보고서를 읽은 후 사용자에게 가능한 후속 작업을 안내한다:
|
|
77
96
|
|
|
78
97
|
1. **구현 진행**: 보고서의 "권장 다음 단계" 섹션 기반으로 코드 수정
|
|
79
|
-
2. **추가 검증**: 같은 task-key로 새 okstra run
|
|
80
|
-
|
|
98
|
+
2. **추가 검증**: 같은 task-key 로 새 okstra run 실행. 구체 커맨드:
|
|
99
|
+
```bash
|
|
100
|
+
scripts/okstra.sh --task-key <task-key> --task-type <task-type>
|
|
101
|
+
```
|
|
102
|
+
더 풍부한 옵션 (base-ref, workers, render-only 등) 이 필요하면 `okstra-history` 스킬을 사용한다.
|
|
103
|
+
3. **관련 task 확인**: 보고서의 관련 task 참조가 있으면 해당 task 의 보고서도 조회
|
|
81
104
|
|
|
82
105
|
## Output
|
|
83
106
|
|
|
84
107
|
```markdown
|
|
85
108
|
## Report for <task-key>
|
|
86
109
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
110
|
+
| Field | Value |
|
|
111
|
+
| ------------ | -------------------------------------------------- |
|
|
112
|
+
| Status | `<status>` |
|
|
113
|
+
| Task type | `<task-type>` |
|
|
114
|
+
| Run seq | `<NNN>` |
|
|
115
|
+
| Run date | `<runTimestamp ISO-8601>` |
|
|
116
|
+
| Report (rel) | `<relative-path-from-project-root>` |
|
|
117
|
+
| Report (abs) | `<absolute-path>` |
|
|
91
118
|
```
|
|
92
119
|
|
|
93
120
|
이후 사용자 요청에 따라 보고서를 읽고 후속 작업을 진행한다.
|