okstra 0.172.0 → 0.174.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 +8 -6
- package/docs/architecture/storage-model.md +24 -3
- package/docs/architecture.md +21 -35
- package/docs/cli.md +39 -7
- package/docs/container.md +1 -1
- package/docs/contributor-change-matrix.md +1 -1
- package/docs/performance-improvement-plan-v2.md +6 -5
- package/docs/project-structure-overview.md +33 -25
- package/docs/task-process/README.md +6 -4
- package/docs/task-process/error-analysis.md +2 -2
- package/docs/task-process/final-verification.md +2 -2
- package/docs/task-process/implementation-option-selection.md +70 -0
- package/docs/task-process/implementation-planning.md +24 -16
- package/docs/task-process/requirements-discovery.md +2 -2
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/claude-worker.md +1 -1
- package/runtime/agents/workers/report-writer-worker.md +30 -6
- package/runtime/bin/lib/okstra/cli.sh +5 -1
- package/runtime/bin/lib/okstra/globals.sh +2 -1
- package/runtime/bin/lib/okstra/usage.sh +3 -0
- package/runtime/bin/okstra-provider-exec.py +29 -12
- package/runtime/bin/okstra-trace-cleanup.sh +58 -129
- package/runtime/bin/okstra.sh +2 -0
- package/runtime/prompts/duties/direction-selection-worker.md +44 -0
- package/runtime/prompts/duties/planning-worker.md +12 -4
- package/runtime/prompts/lead/adapters/cmux.md +2 -0
- package/runtime/prompts/lead/context-loader.md +1 -1
- package/runtime/prompts/lead/convergence.md +5 -5
- package/runtime/prompts/lead/okstra-lead-contract.md +7 -6
- package/runtime/prompts/lead/plan-body-verification.md +23 -6
- package/runtime/prompts/lead/report-writer.md +33 -11
- package/runtime/prompts/profiles/_common-contract.md +3 -3
- package/runtime/prompts/profiles/_implementation-deliverable.md +2 -2
- package/runtime/prompts/profiles/_implementation-executor.md +2 -0
- package/runtime/prompts/profiles/_implementation-verifier.md +2 -2
- package/runtime/prompts/profiles/error-analysis.md +4 -4
- package/runtime/prompts/profiles/final-verification.md +3 -3
- package/runtime/prompts/profiles/forbidden-actions.json +7 -0
- package/runtime/prompts/profiles/implementation-option-selection.md +35 -0
- package/runtime/prompts/profiles/implementation-planning.md +61 -46
- package/runtime/prompts/profiles/implementation.md +4 -2
- package/runtime/prompts/profiles/improvement-discovery.md +1 -1
- package/runtime/prompts/profiles/release-handoff.md +1 -1
- package/runtime/prompts/profiles/requirements-discovery.md +3 -3
- package/runtime/prompts/wizard/prompts.ko.json +9 -1
- package/runtime/python/okstra_ctl/adapters/dispatch/__init__.py +1 -6
- package/runtime/python/okstra_ctl/adapters/hosts/external/relay.md +4 -4
- package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +5 -0
- package/runtime/python/okstra_ctl/agent_invocation.py +1 -0
- package/runtime/python/okstra_ctl/analysis_packet.py +6 -0
- package/runtime/python/okstra_ctl/conformance.py +68 -0
- package/runtime/python/okstra_ctl/dispatch_core.py +89 -39
- package/runtime/python/okstra_ctl/dispatch_state.py +142 -14
- package/runtime/python/okstra_ctl/doctor.py +2 -2
- package/runtime/python/okstra_ctl/domain/worker_exec.py +5 -0
- package/runtime/python/okstra_ctl/exact_coverage.py +128 -0
- package/runtime/python/okstra_ctl/final_report_schema.py +5 -4
- package/runtime/python/okstra_ctl/fix_cycles.py +3 -1
- package/runtime/python/okstra_ctl/implementation_direction.py +836 -0
- package/runtime/python/okstra_ctl/implementation_options.py +479 -0
- package/runtime/python/okstra_ctl/pane_reclaim.py +13 -22
- package/runtime/python/okstra_ctl/plan_items.py +51 -3
- package/runtime/python/okstra_ctl/render.py +1 -0
- package/runtime/python/okstra_ctl/render_final_report.py +16 -19
- package/runtime/python/okstra_ctl/report_contract.py +45 -14
- package/runtime/python/okstra_ctl/report_finalize.py +68 -9
- package/runtime/python/okstra_ctl/report_html/render.py +4 -2
- package/runtime/python/okstra_ctl/report_html/router.py +4 -0
- package/runtime/python/okstra_ctl/report_html/view_models/implementation_option_selection.py +32 -0
- package/runtime/python/okstra_ctl/report_html/view_models/implementation_planning.py +25 -10
- package/runtime/python/okstra_ctl/report_views.py +148 -12
- package/runtime/python/okstra_ctl/run.py +393 -4
- package/runtime/python/okstra_ctl/schema_excerpt.py +1 -1
- package/runtime/python/okstra_ctl/scope_provenance.py +16 -10
- package/runtime/python/okstra_ctl/session.py +69 -12
- package/runtime/python/okstra_ctl/team.py +51 -25
- package/runtime/python/okstra_ctl/tmux.py +19 -149
- package/runtime/python/okstra_ctl/user_response.py +75 -0
- package/runtime/python/okstra_ctl/wizard.py +144 -0
- package/runtime/python/okstra_ctl/worker_prompt_policy.py +2 -0
- package/runtime/python/okstra_ctl/worker_request.py +2 -0
- package/runtime/python/okstra_ctl/workflow.py +29 -7
- package/runtime/python/okstra_ctl/worktree.py +69 -3
- package/runtime/python/okstra_token_usage/cli.py +1 -1
- package/runtime/python/okstra_token_usage/collect.py +66 -6
- package/runtime/schemas/final-report-v2.0.schema.json +1428 -137
- package/runtime/skills/okstra-setup/references/project-config.md +11 -0
- package/runtime/templates/reports/final-report-v2.template.md +4 -0
- package/runtime/templates/reports/final-verification-input.template.md +1 -1
- package/runtime/templates/reports/html/base.template.html +3 -2
- package/runtime/templates/reports/html/i18n/en.json +21 -1
- package/runtime/templates/reports/html/i18n/ko.json +21 -1
- package/runtime/templates/reports/html/macros/forms.html +21 -2
- package/runtime/templates/reports/html/tasks/implementation-option-selection.template.html +49 -0
- package/runtime/templates/reports/html/tasks/implementation-planning.template.html +36 -2
- package/runtime/templates/reports/i18n/en.json +13 -0
- package/runtime/templates/reports/implementation-input.template.md +4 -2
- package/runtime/templates/reports/implementation-planning-input.template.md +18 -4
- package/runtime/templates/reports/improvement-discovery-input.template.md +1 -1
- package/runtime/templates/reports/md/tasks/implementation-option-selection.template.md +13 -0
- package/runtime/templates/reports/md/tasks/implementation-planning.template.md +17 -0
- package/runtime/templates/reports/report.js +111 -4
- package/runtime/templates/reports/settings.template.json +0 -24
- package/runtime/templates/reports/task-brief.template.md +9 -3
- package/runtime/templates/reports/user-response.template.md +25 -4
- package/runtime/templates/worker-prompt-preamble.md +8 -0
- package/runtime/validators/lib/fixtures.sh +49 -17
- package/runtime/validators/validate-implementation-plan-stages.py +169 -4
- package/runtime/validators/validate-report-views.py +2 -2
- package/runtime/validators/validate-run.py +149 -498
- package/runtime/validators/validate_improvement_report.py +5 -1
- package/runtime/validators/validate_session_conformance.py +1 -1
- package/src/cli-registry.mjs +8 -1
- package/src/commands/execute/codex-run.mjs +1 -0
- package/src/commands/execute/render-bundle.mjs +1 -0
- package/src/commands/execute/team.mjs +3 -3
- package/src/commands/execute/worktree-status.mjs +109 -0
- package/src/commands/lifecycle/install.mjs +0 -2
- package/src/commands/report/finalize.mjs +13 -6
- package/runtime/bin/okstra-subagent-reclaim.sh +0 -26
- package/runtime/schemas/final-report-v1.0.schema.json +0 -6366
- package/runtime/templates/reports/final-report.template.md +0 -1258
|
@@ -102,12 +102,36 @@ You author the final-report data.json (the JSON SSOT). You author it against the
|
|
|
102
102
|
|
|
103
103
|
The AI handoff Markdown is an agent-facing ledger: verdict, routing, clarification decisions, evidence, one structured task deliverable, and execution audits. The human HTML is the reader-facing explanation: `humanSummary` plus the selected task block's `userNarrative` and structured facts. Populate both human fields in data.json even though the Markdown intentionally omits their full prose. Worker discussion, convergence mechanics, and token usage belong to audit data and must not be copied into the HTML human main body.
|
|
104
104
|
|
|
105
|
+
### Implementation-planning frontmatter contract
|
|
106
|
+
|
|
107
|
+
#### Selected-direction
|
|
108
|
+
|
|
109
|
+
Emit `frontmatter.approved` as `false` and copy `implementationPlanning.selectedDirectionRef.snapshotPath` into `frontmatter.selectedDirectionRef`. You MUST omit `frontmatter.implementationOption`; the direction was selected upstream and cannot be selected again in planning. `schemas/final-report-v2.0.schema.json` enforces the required selected-direction reference and rejects an `implementationOption` property for this branch.
|
|
110
|
+
|
|
111
|
+
#### Legacy candidate-comparison
|
|
112
|
+
|
|
113
|
+
Emit `frontmatter.approved` as `false` and `frontmatter.implementationOption` as the empty string `""`. The user later flips `approved` to `true` and fills `implementationOption` with the chosen Option Candidate name to authorise and scope the next `implementation` run. Every other report type follows the same empty `implementationOption` default; the schema's non-selected-direction branch requires that field and rejects a selected-direction reference.
|
|
114
|
+
|
|
115
|
+
### General authoring rules
|
|
116
|
+
|
|
105
117
|
Rules (the schema enforces most of these — they are listed here so you know *what* to populate, not *how* to validate):
|
|
106
118
|
|
|
107
119
|
- Read the exact permitted header values from the task bundle schema excerpt. In the current v2 contract, `header.reportOwner` is `"Okstra lead"` and `header.reportAuthor` is `"Report writer worker"`. Set author to `"Okstra lead"` only for `release-handoff` runs (single-lead by design) or a recorded report-writer dispatch failure fallback. A legacy v1 excerpt may retain its historical compatibility values; follow that excerpt rather than inferring ownership from the provider.
|
|
108
120
|
- **Source items (worker:item) preservation.** Every `consensus[].sourceItems`, `differences[].workersPosition[].itemId`, and `evidence.primary[].sourceItems` entry MUST carry the worker:item-id pair (e.g. `claude:F-001`, `codex:1.1`, `antigravity:F-3`, or `lead:mcp-1` for lead-only evidence). The schema enforces this via the `SourceItem` regex; bare worker-name lists no longer parse.
|
|
109
121
|
- **Verdict Card consistency.** `verdictCard.verdictToken` and `verdictCard.direction` MUST byte-match `finalVerdict.verdictToken` / `.direction`; `validators/validate-run.py` diffs both and fails the run on divergence. `verdictCard.nextStep` names the same action as `finalVerdict.nextStep` and `recommendedNextSteps[0].text` but is written as the actionable command the reader runs (e.g. `/okstra-run task-key=… task-type=release-handoff`) where the other two are prose — it is deliberately not a byte copy. Duplicating the compared values across `verdictCard` and `finalVerdict` is intentional so the validator can diff them.
|
|
110
|
-
- **Error-analysis diagnosis and routing.** When `header.taskType` is `error-analysis`, populate the required `errorAnalysis` object. Copy `errorAnalysis.symptomVerbatim` byte-for-byte from the symptom stated in the brief's `Source Material`; do not paraphrase it. Every `causeCandidates[]` row includes the full `supportingEvidence`, `falsifyingEvidenceChecked`, `confidence`, and `disproveWith` fields. When a candidate is a step in a propagation chain rather than a competing explanation — the analysis calls it a downstream step, a second stage, or a consequence of another candidate — set its `downstreamOf` to the ids of the candidates immediately upstream of it; leave the field absent for a candidate that stands on its own. Every id listed MUST be another candidate in the same report, no row may name itself, and the links MUST NOT form a cycle; `validators/validate-run.py::_validate_cause_chain` rejects all three. This is the only place the chain is machine-readable — prose calling a candidate "the second step of the chain" while `downstreamOf` is absent leaves the report's figure claiming the candidates are alternatives. Route `errorAnalysis.routing.nextTaskType=implementation-
|
|
122
|
+
- **Error-analysis diagnosis and routing.** When `header.taskType` is `error-analysis`, populate the required `errorAnalysis` object. Copy `errorAnalysis.symptomVerbatim` byte-for-byte from the symptom stated in the brief's `Source Material`; do not paraphrase it. Every `causeCandidates[]` row includes the full `supportingEvidence`, `falsifyingEvidenceChecked`, `confidence`, and `disproveWith` fields. When a candidate is a step in a propagation chain rather than a competing explanation — the analysis calls it a downstream step, a second stage, or a consequence of another candidate — set its `downstreamOf` to the ids of the candidates immediately upstream of it; leave the field absent for a candidate that stands on its own. Every id listed MUST be another candidate in the same report, no row may name itself, and the links MUST NOT form a cycle; `validators/validate-run.py::_validate_cause_chain` rejects all three. This is the only place the chain is machine-readable — prose calling a candidate "the second step of the chain" while `downstreamOf` is absent leaves the report's figure claiming the candidates are alternatives. Route `errorAnalysis.routing.nextTaskType=implementation-option-selection` with `direction=begin-option-selection`, or route `errorAnalysis.routing.nextTaskType=error-analysis` with `direction=continue-investigation`; no other pairing is valid. `verdictCard.nextStep`, `finalVerdict.nextStep`, the first `recommendedNextSteps` action and command, and the unique `followUpTasks` row whose `origin` is `phase-continuation` MUST all point to the same `errorAnalysis.routing.nextTaskType` target. The schema enforces only the presence of a `phase-continuation` row; `validators/validate-run.py::_validate_error_analysis_consistency` enforces exact target agreement and uniqueness.
|
|
123
|
+
- **Implementation-option-selection comparison.** When `header.taskType` is `implementation-option-selection`, populate `implementationOptionSelection` from the converged direction-selection findings. Preserve every merged or rejected raw candidate in `candidateAudit`, and put at most three selectable candidates in `rankedOptions`. Each displayed candidate carries its requirement coverage, scope commitments, criterion scores, feasibility votes, safety blockers, unresolved feasibility facts, planning invariants, and exact coverage summary. In each displayed candidate, `expectedChangeAreas` names direction-level change surfaces, never exact file paths or an exact file list. `expectedVerification` names direction-level verification signals, never a stage list or executable test commands. `schemas/final-report-v2.0.schema.json` enforces the displayed-summary constants and the three-option cap; semantic recalculation belongs to `validators/validate-run.py`.
|
|
124
|
+
- **Implementation-planning direction branch.** For `planningContract: selected-direction`, read `selectedDirectionRef` and its snapshot, then preserve their core mechanism, architecture boundaries, and planning invariants in `directionRealization`. Materialize files, interfaces, stages, validation, rollback, and bidirectional original-requirement links without candidate generation, scoring, recommendation, or user candidate selection. Author exactly one `P-Dir-1` whose payload is the complete `directionRealization`; its verifier checks those preserved properties and any hidden direction change. If the direction must change, author `direction-invalidated` and no execution queue. Legacy candidate-comparison reruns retain Option Candidates, trade-offs, Recommended Option, and `P-Opt-*` semantics.
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"candidateDetailBoundary": {
|
|
129
|
+
"expectedChangeAreas": "direction-level-only",
|
|
130
|
+
"expectedVerification": "direction-level-signals-only",
|
|
131
|
+
"forbidden": ["exact-file-lists", "stage-lists", "test-commands"]
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
111
135
|
- **Human narrative.** Populate required `humanSummary` and the selected task block's `userNarrative`. Human-visible analysis facts must not exist only in Markdown; HTML is derived independently and can use only data.json. Keep worker discussion and audit details in `crossVerification`, `executionStatus`, and `tokenUsage`, outside the human narrative fields.
|
|
112
136
|
- **External QA advisory.** A Tier 3 entry requiring `db`, `http`, or
|
|
113
137
|
`external` may be non-PASS without changing approval or final verdict. Render
|
|
@@ -116,7 +140,7 @@ Rules (the schema enforces most of these — they are listed here so you know *w
|
|
|
116
140
|
and add the exact rerun command to `recommendedNextSteps`. Never turn this
|
|
117
141
|
advisory alone into a clarification, Acceptance Blocker, conditional
|
|
118
142
|
acceptance condition, or blocked routing.
|
|
119
|
-
- **§7 phase-continuation row (mandatory for non-terminal task-types).** When `header.taskType` is one of `requirements-discovery` / `implementation-planning` / `error-analysis` / `implementation` / `final-verification`, `followUpTasks` MUST contain at least one row whose `origin` is `phase-continuation`, `newTaskId` reuses the current task-id, `autoSpawn` is `"no"`, and `priority` is `"P0"`. For `release-handoff` runs, omit the phase-continuation row. The schema `allOf` / `contains` clause enforces row presence, not exact route-target agreement or uniqueness; phase validation must enforce those error-analysis semantics as specified above.
|
|
143
|
+
- **§7 phase-continuation row (mandatory for non-terminal task-types).** When `header.taskType` is one of `requirements-discovery` / `implementation-option-selection` / `implementation-planning` / `error-analysis` / `implementation` / `final-verification`, `followUpTasks` MUST contain at least one row whose `origin` is `phase-continuation`, `newTaskId` reuses the current task-id, `autoSpawn` is `"no"`, and `priority` is `"P0"`. For `release-handoff` runs, omit the phase-continuation row. The schema `allOf` / `contains` clause enforces row presence, not exact route-target agreement or uniqueness; phase validation must enforce those error-analysis semantics as specified above.
|
|
120
144
|
- **No deprecated sections.** The schema has no `4.5.8 User Approval Request` body field, no `4.5.9 Open Questions`, no `5.1 Additional Material Request`, no `5.2 User Confirmation Questions` — clarifications go under the unified `clarificationItems[]` array.
|
|
121
145
|
- **Optional Section 0.** Include `clarificationCarryIn` ONLY when the lead's prompt provides a non-empty carry-in path. Omit the key entirely otherwise (do NOT set it to `null` or an empty object).
|
|
122
146
|
- **Reading Confirmation** goes at `**Audit sidecar path:**` per the selected report-writer preamble's `Required reading` section — never in the data.json or the main worker-results file.
|
|
@@ -129,10 +153,10 @@ Rules (the schema enforces most of these — they are listed here so you know *w
|
|
|
129
153
|
- Cite file paths and line numbers in every `evidence.primary[].source` / `consensus[].evidence` cell.
|
|
130
154
|
- Preserve every analysis worker's ticket tagging — every row's `ticketId` field carries the ticket key or the task-fallback. For single-ticket runs, set `ticketCoverage` to `{"singleTicket": "<ticket>"}`. For runs that do not require ticket tagging (`release-handoff`, `final-verification`), set `ticketCoverage` to `{"omit": true}`.
|
|
131
155
|
- For `requirements-discovery`, `error-analysis`, and `implementation-planning`, populate the top-level `endStateCoverage` with exactly one row per end-state id the brief declares — no more, no fewer. `disposition` is one of `addressed` / `deferred` / `not-applicable` / `blocked`. `addressed` requires a `coveredBy` anchor in THIS phase's own deliverable (requirements-discovery: the routing decision, the fan-out unit id, or the `C-NNN` clarification; error-analysis: the root-cause candidate or the next diagnostic; implementation-planning: the `R-NNN` row); every other disposition requires a `rationale`. Do not author a goal of your own here and do not restate the brief — this table records only how this phase accounted for what the reporter already pinned. When the brief declares no end-state ids (a brief authored before those sections existed), omit the field entirely. **Enforced:** `validators/validate-run.py` `_validate_end_state_coverage`.
|
|
132
|
-
- For `implementation-planning`, populate `implementationPlanning.requirementCoverage` with one row per concrete requirement from the brief / packet, using IDs `R-001`, `R-002`, ... in source order. A `covered` row's `coveredBy` MUST name the specific Option Candidate plus Stage/Step that satisfies the requirement. Use `status: "covered"` only when the report's plan actually covers it; use `documented-deviation` only when `coveredBy` states the concrete alternative and the row records non-empty unique `decisionRefs` plus `approvalDisposition`. Each `C-NNN` ref must name a clarification in this report; each `D-NNNN` ref must name a `decisionDrafts[].number`. `approvalDisposition: "accepted"` requires a referenced clarification with `status: answered|resolved` and non-empty `userInput`; `approvalDisposition: "blocked C-NNN"` requires that same-report clarification to be `status: open, blocks: approval`. Otherwise use `gap` or `blocked C-NNN` and ensure the corresponding `Clarification Items` row blocks approval. Do not collapse this into `ticketCoverage`; ticket coverage is not requirement coverage. **Enforced:** `schemas/final-report-v2.0.schema.json` `$defs.ImplementationRequirementCoverageRow` and `validators/validate-run.py` `_validate_requirement_deviations`.
|
|
133
|
-
- For `implementation-planning`, each `requirementCoverage` row's `source` is a graded cell, not prose — free text like `"carry-in from requirements-discovery C-001"` is rejected. Write exactly one of: `brief:EB-001` / `brief:PB-001` / `brief:EO-001`, an end-state id the brief declares — when the brief pins ids, citing a heading instead is rejected, because every brief carries the same generic headings and a heading cannot say WHICH reporter line the requirement came from (only a brief authored before the end-state sections existed still takes the older `brief:<heading>` form, and there the heading must literally exist in it); `derived:R-NNN — <one-line reason>`, whose chain must terminate at a `brief:` or `contract:` row of the same table without cycling; or `contract:<rule>`, for artifacts okstra's own phase contract mandates, whose allowlist is exactly the two tokens `decision-record-step` (the §5.4 Decision Drafts materialization step) and `glossary-step` (the glossary proposal step) — any other rule name is rejected, so never invent one. (Maintainer SSOT for that allowlist: `scripts/okstra_ctl/scope_provenance.py` in the okstra repo.) A requirement you cannot source this way does not belong in the table: put it in `clarificationItems[]` with `Blocks=approval`. **Enforced:** `validators/validate-run.py` `_validate_requirement_provenance`. In the same table, anchor every stage number in `coveredBy` to a `Stage` / `Stages` word (`Stage 2`, `Stages 1-3`) — `_validate_stage_has_requirement` reads that cell as prose and fails the plan when a Stage Map stage is cited by no row.
|
|
134
|
-
- For `implementation-planning`, also populate `implementationPlanning.decisionDrafts` (one row per decision meeting all three decision-record criteria; `[]` otherwise) and `implementationPlanning.skippedAdrCandidates` (evaluated-but-dropped adr-candidates; `[]` otherwise). The schema excerpt enumerates the row shape; the renderer emits §5.4 `### Decision Drafts`. When `decisionDrafts` is non-empty, the plan's stages MUST carry a stepwise step that creates `.okstra/decisions/<NNNN>-<slug>.md` (validate-run gates this).
|
|
135
|
-
- For `implementation-planning`, populate `implementationPlanning.variationPointAnalysis` — a `hasMultipleImplementations` judgement synthesized from the analysis workers' output, not a field filled in last. When it is `true`, write one `points[]` row per varying behavior carrying `behavior`, the two or more `implementations` that serve it, `evidence` (a `path:line`, or the sibling task / stage that already implements that behavior), and an `extractionDecision` of `extract` / `interfaceKind` / `coveredBy` (the Stage Map stage that builds the interface) / `rationale`; when it is `false`, write a non-empty `noVariationRationale` and leave `points` empty (the two branches are mutually exclusive). Do NOT pass a boilerplate rationale
|
|
156
|
+
- For selected-direction `implementation-planning`, preserve each original requirement ID and populate its `stageRefs`, `stepRefs`, `validationRefs`, and `fileRefs`; `validate_selected_direction_plan` enforces forward and reverse exact coverage. For legacy candidate-comparison `implementation-planning`, populate `implementationPlanning.requirementCoverage` with one row per concrete requirement from the brief / packet, using IDs `R-001`, `R-002`, ... in source order. A `covered` row's `coveredBy` MUST name the specific Option Candidate plus Stage/Step that satisfies the requirement. Use `status: "covered"` only when the report's plan actually covers it; use `documented-deviation` only when `coveredBy` states the concrete alternative and the row records non-empty unique `decisionRefs` plus `approvalDisposition`. Each `C-NNN` ref must name a clarification in this report; each `D-NNNN` ref must name a `decisionDrafts[].number`. `approvalDisposition: "accepted"` requires a referenced clarification with `status: answered|resolved` and non-empty `userInput`; `approvalDisposition: "blocked C-NNN"` requires that same-report clarification to be `status: open, blocks: approval`. Otherwise use `gap` or `blocked C-NNN` and ensure the corresponding `Clarification Items` row blocks approval. Do not collapse this into `ticketCoverage`; ticket coverage is not requirement coverage. **Enforced:** `schemas/final-report-v2.0.schema.json` `$defs.ImplementationRequirementCoverageRow` and `validators/validate-run.py` `_validate_requirement_deviations`.
|
|
157
|
+
- For legacy candidate-comparison `implementation-planning`, each `requirementCoverage` row's `source` is a graded cell, not prose — free text like `"carry-in from requirements-discovery C-001"` is rejected. Write exactly one of: `brief:EB-001` / `brief:PB-001` / `brief:EO-001`, an end-state id the brief declares — when the brief pins ids, citing a heading instead is rejected, because every brief carries the same generic headings and a heading cannot say WHICH reporter line the requirement came from (only a brief authored before the end-state sections existed still takes the older `brief:<heading>` form, and there the heading must literally exist in it); `derived:R-NNN — <one-line reason>`, whose chain must terminate at a `brief:` or `contract:` row of the same table without cycling; or `contract:<rule>`, for artifacts okstra's own phase contract mandates, whose allowlist is exactly the two tokens `decision-record-step` (the §5.4 Decision Drafts materialization step) and `glossary-step` (the glossary proposal step) — any other rule name is rejected, so never invent one. (Maintainer SSOT for that allowlist: `scripts/okstra_ctl/scope_provenance.py` in the okstra repo.) A requirement you cannot source this way does not belong in the table: put it in `clarificationItems[]` with `Blocks=approval`. **Enforced:** `validators/validate-run.py` `_validate_requirement_provenance`. In the same table, anchor every stage number in `coveredBy` to a `Stage` / `Stages` word (`Stage 2`, `Stages 1-3`) — `_validate_stage_has_requirement` reads that cell as prose and fails the plan when a Stage Map stage is cited by no row.
|
|
158
|
+
- For legacy candidate-comparison `implementation-planning`, also populate `implementationPlanning.decisionDrafts` (one row per decision meeting all three decision-record criteria; `[]` otherwise) and `implementationPlanning.skippedAdrCandidates` (evaluated-but-dropped adr-candidates; `[]` otherwise). The schema excerpt enumerates the row shape; the renderer emits §5.4 `### Decision Drafts`. When `decisionDrafts` is non-empty, the plan's stages MUST carry a stepwise step that creates `.okstra/decisions/<NNNN>-<slug>.md` (validate-run gates this).
|
|
159
|
+
- For `implementation-planning`, populate `implementationPlanning.variationPointAnalysis` — a `hasMultipleImplementations` judgement synthesized from the analysis workers' output, not a field filled in last. When it is `true`, write one `points[]` row per varying behavior carrying `behavior`, the two or more `implementations` that serve it, `evidence` (a `path:line`, or the sibling task / stage that already implements that behavior), and an `extractionDecision` of `extract` / `interfaceKind` / `coveredBy` (the Stage Map stage that builds the interface) / `rationale`; when it is `false`, write a non-empty `noVariationRationale` and leave `points` empty (the two branches are mutually exclusive). Do NOT pass a boilerplate rationale. Populate test seams under `implementationPlanning.directionRealization.testSeams` for selected-direction plans and under `implementationPlanning.recommendedOption.testSeams` for legacy candidate-comparison plans. An empty list is a conscious "no seam needed" claim, never a default for a field nobody filled. Every point becomes a `P-Var-*` plan item judged in §5.5.9.
|
|
136
160
|
- When the `Task Type` is `improvement-discovery`, populate `improvementDiscovery.candidates[]`, `improvementDiscovery.lensCoverage[]`, `improvementDiscovery.selectionLimit`, and `improvementDiscovery.userNarrative`. Each candidate carries the 11 logical fields enforced by `validators/validate_improvement_report.py`; each lens-coverage row records candidate IDs or an evidence-backed no-candidate rationale. Source IDs, lens names, and worker prefixes from `scripts/okstra_ctl/improvement_lenses.py`. The standard renderer derives the AI handoff Markdown; never author a free-form improvement report.
|
|
137
161
|
|
|
138
162
|
Write the three completion artifacts and the separate audit sidecar with your `Write` tool — that is the canonical authoring path, and okstra ships no hook that blocks `.md` writes (its seeded settings carry no `PreToolUse` entry at all — only the session/subagent lifecycle hooks `SessionStart` compact-reminder, `SessionEnd` trace-cleanup, and `SubagentStop` / `TaskCompleted` pane reclaim, none of which can intercept a tool call). A Bash heredoc is acceptable ONLY when a specific `Write` call is genuinely rejected by the host environment, and it MUST produce byte-identical content — do not reach for it pre-emptively. After writing data.json, invoke the renderer (`Bash`): `okstra render-final-report <data.json path>`, then write the Worker Result Path pointer. Confirm data.json, rendered Markdown, the pointer, and the audit sidecar exist before responding with a short status line prefixed by your model identity, per the preamble §"Return message to the lead". **Enforced:** dispatch `completionPaths` requires the first three files and `validators/validate_session_conformance.py` validates the audit sidecar.
|
|
@@ -175,6 +175,10 @@ while [[ $# -gt 0 ]]; do
|
|
|
175
175
|
CLARIFICATION_RESPONSE_PATH="$(require_option_value --clarification-response "${2-}")"
|
|
176
176
|
shift 2
|
|
177
177
|
;;
|
|
178
|
+
--selected-direction)
|
|
179
|
+
SELECTED_DIRECTION_PATH="$(require_option_value --selected-direction "${2-}")"
|
|
180
|
+
shift 2
|
|
181
|
+
;;
|
|
178
182
|
--task-key)
|
|
179
183
|
TASK_KEY_INPUT="$(require_option_value --task-key "${2-}")"
|
|
180
184
|
shift 2
|
|
@@ -233,7 +237,7 @@ while [[ $# -gt 0 ]]; do
|
|
|
233
237
|
printf ' hint: did you mean --task-id?\n' >&2
|
|
234
238
|
;;
|
|
235
239
|
esac
|
|
236
|
-
printf ' valid options: --render-only --resume-clarification --yes --workers --lead-provider --lead-model --claude-model --codex-model --antigravity-model --worker-model --report-writer-provider --report-writer-model --lead-runtime --executor --critic --related-tasks --work-category --task-type --project-id --project-root --task-group --task-id --task-brief --directive --base-ref --fix-cycle --clarification-response --task-key --approved-plan --approve --implementation-option --stage --stages --qa-waiver --no-plan-verification -h|--help\n' >&2
|
|
240
|
+
printf ' valid options: --render-only --resume-clarification --yes --workers --lead-provider --lead-model --claude-model --codex-model --antigravity-model --worker-model --report-writer-provider --report-writer-model --lead-runtime --executor --critic --related-tasks --work-category --task-type --project-id --project-root --task-group --task-id --task-brief --directive --base-ref --fix-cycle --clarification-response --selected-direction --task-key --approved-plan --approve --implementation-option --stage --stages --qa-waiver --no-plan-verification -h|--help\n' >&2
|
|
237
241
|
usage
|
|
238
242
|
exit 1
|
|
239
243
|
;;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
PROFILE_DIR="$WORKSPACE_ROOT/prompts/profiles"
|
|
4
4
|
PROMPT_TEMPLATE="$WORKSPACE_ROOT/prompts/launch.template.md"
|
|
5
5
|
TASK_INDEX_TEMPLATE="$WORKSPACE_ROOT/templates/project-docs/task-index.template.md"
|
|
6
|
-
FINAL_REPORT_TEMPLATE_SOURCE="$WORKSPACE_ROOT/templates/reports/final-report.template.md"
|
|
6
|
+
FINAL_REPORT_TEMPLATE_SOURCE="$WORKSPACE_ROOT/templates/reports/final-report-v2.template.md"
|
|
7
7
|
RUN_VALIDATOR_PATH="$WORKSPACE_ROOT/validators/validate-run.py"
|
|
8
8
|
OKSTRA_ROOT=""
|
|
9
9
|
OKSTRA_TASKS_ROOT=""
|
|
@@ -44,6 +44,7 @@ ANALYSIS_PROFILE=""
|
|
|
44
44
|
DIRECTIVE=""
|
|
45
45
|
FIX_CYCLE=""
|
|
46
46
|
CLARIFICATION_RESPONSE_PATH=""
|
|
47
|
+
SELECTED_DIRECTION_PATH=""
|
|
47
48
|
APPROVED_PLAN_PATH=""
|
|
48
49
|
APPROVE_PLAN_ACK="false"
|
|
49
50
|
# implementation 전용: 유저가 고른 Option Candidate 이름. 빈 값이면 implementation
|
|
@@ -43,6 +43,9 @@ optional arguments:
|
|
|
43
43
|
input so the lead can reconcile each prior Q*. Use this for scripted or
|
|
44
44
|
CI runs where the answer file is already prepared. Interactive users
|
|
45
45
|
should prefer --resume-clarification, which wraps this flag.
|
|
46
|
+
--selected-direction Path to a validated implementation-option-selection final report.
|
|
47
|
+
Required for a new implementation-planning run. Existing planning
|
|
48
|
+
reruns continue to use --clarification-response with their prior report.
|
|
46
49
|
--approved-plan Path to the approved final-report.md from a prior implementation-planning run.
|
|
47
50
|
Required when --task-type=implementation; the file MUST contain a recorded user approval marker.
|
|
48
51
|
--approve Treat the user's CLI invocation itself as the plan-approval signal. Only meaningful
|
|
@@ -40,10 +40,11 @@ from okstra_ctl.worker_runner import LIVE, QUIET, run_worker # noqa: E402
|
|
|
40
40
|
_USAGE = (
|
|
41
41
|
"usage: okstra-provider-exec.py <provider> <project-root> "
|
|
42
42
|
"<model-execution-value> <prompt-path> [worktree-path] [role] "
|
|
43
|
-
"[idle-timeout-seconds] [--presentation live|quiet]"
|
|
43
|
+
"[idle-timeout-seconds] [--presentation live|quiet] [--session-id <uuid>]"
|
|
44
44
|
)
|
|
45
45
|
|
|
46
46
|
_PRESENTATION_FLAG = "--presentation"
|
|
47
|
+
_SESSION_ID_FLAG = "--session-id"
|
|
47
48
|
_PRESENTATIONS = (LIVE, QUIET)
|
|
48
49
|
|
|
49
50
|
|
|
@@ -66,6 +67,9 @@ class Invocation:
|
|
|
66
67
|
def parse_invocation(argv: list[str]) -> Invocation:
|
|
67
68
|
"""Resolve the wrapper's positional contract into one runnable dispatch."""
|
|
68
69
|
positional, presentation = _take_presentation(argv)
|
|
70
|
+
# Empty unless the dispatcher issued one. Without it the CLI picks its own
|
|
71
|
+
# id and nothing downstream can map that session back to this worker.
|
|
72
|
+
positional, session_id = _take_flag(positional, _SESSION_ID_FLAG, "")
|
|
69
73
|
if not 4 <= len(positional) <= 7:
|
|
70
74
|
raise PreflightError(64, _USAGE)
|
|
71
75
|
provider_id, project_root_raw, model, prompt_raw = positional[:4]
|
|
@@ -90,6 +94,7 @@ def parse_invocation(argv: list[str]) -> Invocation:
|
|
|
90
94
|
worktree_path=worktree,
|
|
91
95
|
role=role,
|
|
92
96
|
idle_timeout_seconds=idle_timeout_seconds,
|
|
97
|
+
session_id=session_id,
|
|
93
98
|
)
|
|
94
99
|
strategy = spec.exec_strategy
|
|
95
100
|
_check_command(strategy, request)
|
|
@@ -103,27 +108,39 @@ def parse_invocation(argv: list[str]) -> Invocation:
|
|
|
103
108
|
)
|
|
104
109
|
|
|
105
110
|
|
|
106
|
-
def
|
|
107
|
-
"""Split
|
|
111
|
+
def _take_flag(argv: list[str], flag: str, default: str) -> tuple[list[str], str]:
|
|
112
|
+
"""Split one value-carrying flag out of an otherwise positional argv.
|
|
108
113
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
into its caller's context window instead.
|
|
114
|
+
The wrapper's contract is positional, so every flag it grows has to be
|
|
115
|
+
lifted out before the positions are counted. Written once because the flags
|
|
116
|
+
differ only in the value they carry: a second copy of this loop is where
|
|
117
|
+
their refusals of a flag with no value would drift apart.
|
|
114
118
|
"""
|
|
115
119
|
positional: list[str] = []
|
|
116
|
-
|
|
120
|
+
value = default
|
|
117
121
|
index = 0
|
|
118
122
|
while index < len(argv):
|
|
119
|
-
if argv[index] !=
|
|
123
|
+
if argv[index] != flag:
|
|
120
124
|
positional.append(argv[index])
|
|
121
125
|
index += 1
|
|
122
126
|
continue
|
|
123
127
|
if index + 1 >= len(argv):
|
|
124
|
-
raise PreflightError(64, f"{
|
|
125
|
-
|
|
128
|
+
raise PreflightError(64, f"{flag} needs a value: {_USAGE}")
|
|
129
|
+
value = argv[index + 1]
|
|
126
130
|
index += 2
|
|
131
|
+
return positional, value
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _take_presentation(argv: list[str]) -> tuple[list[str], str]:
|
|
135
|
+
"""``_take_flag`` plus the allowlist only this flag has.
|
|
136
|
+
|
|
137
|
+
Defaults to ``quiet``. ``live`` is only ever right where a screen was
|
|
138
|
+
declared, and the only callers that can declare one are the pane backends —
|
|
139
|
+
which pass the flag explicitly. Defaulting the other way assumed a screen
|
|
140
|
+
that a subagent dispatch does not have, and sent every worker's progress
|
|
141
|
+
into its caller's context window instead.
|
|
142
|
+
"""
|
|
143
|
+
positional, presentation = _take_flag(argv, _PRESENTATION_FLAG, QUIET)
|
|
127
144
|
if presentation not in _PRESENTATIONS:
|
|
128
145
|
allowed = " | ".join(_PRESENTATIONS)
|
|
129
146
|
raise PreflightError(
|
|
@@ -1,45 +1,45 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
#
|
|
3
|
-
# okstra-trace-cleanup.sh — close
|
|
3
|
+
# okstra-trace-cleanup.sh — close the harness-owned worker panes of an okstra run.
|
|
4
4
|
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
5
|
+
# The panes this closes are the teammate panes the HARNESS splits for dispatched
|
|
6
|
+
# worker agents. okstra does not create them and cannot tag them, so they are
|
|
7
|
+
# found by a title allowlist scoped to the lead's window. They accumulate until
|
|
8
|
+
# the pane budget runs out, and `shutdown_request` only idles the agent without
|
|
9
|
+
# freeing its pane — this script is the only thing that closes them, which is why
|
|
10
|
+
# the lead calls it at every round boundary and before every user gate.
|
|
10
11
|
#
|
|
11
|
-
#
|
|
12
|
-
# `@okstra_trace_run` / `@okstra_status`. Those wrappers are now four-line
|
|
13
|
-
# entrypoints and worker progress renders into the worker's own pane, so
|
|
14
|
-
# NOTHING SPAWNS A TRACE PANE and neither tag has a writer left. The trace
|
|
15
|
-
# paths below still run and simply match nothing; `--reclaim-completed`, which
|
|
16
|
-
# keys on `@okstra_status`, is inert for the same reason. Kept rather than
|
|
17
|
-
# deleted because the hooks that call this script are already seeded on user
|
|
18
|
-
# machines — retiring the trace machinery is a deliberate follow-up.
|
|
12
|
+
# Two tag-driven paths used to live here and both are gone:
|
|
19
13
|
#
|
|
20
|
-
#
|
|
14
|
+
# - Worker-compute panes of the `tmux-pane` dispatch backend, tagged
|
|
15
|
+
# `@okstra_worker_run`. That backend was removed — a worker now gets a cmux
|
|
16
|
+
# surface or a cli-wrapper subprocess, and neither is a tmux pane okstra owns.
|
|
17
|
+
# - Trace panes, tagged `@okstra_trace_run` / `@okstra_status`, which the
|
|
18
|
+
# provider wrappers used to split as `tail -F` siblings. The wrappers became
|
|
19
|
+
# four-line entrypoints and worker progress renders into the worker's own
|
|
20
|
+
# pane, so nothing had spawned a trace pane for some time.
|
|
21
21
|
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
# by a title allowlist scoped to the LEAD's WINDOW. The
|
|
28
|
-
# lead pane is read from `<RUN_DIR>/state/lead-pane.id`
|
|
29
|
-
# (recorded once by the lead in its own foreground pane —
|
|
30
|
-
# reliable even though Claude Code's Bash tool strips
|
|
31
|
-
# `$TMUX`/`$TMUX_PANE`); it scopes the title scan and is
|
|
32
|
-
# NEVER killed. Window scope keeps a second lead running
|
|
33
|
-
# in another window of the same session out of range.
|
|
22
|
+
# With both writers gone the tag scan matched nothing, so it was removed along
|
|
23
|
+
# with the two modes that existed only to drive it (`--reap`, which the SessionEnd
|
|
24
|
+
# hook called, and `--reclaim-completed`, which `okstra-subagent-reclaim.sh` drove
|
|
25
|
+
# from SubagentStop / TaskCompleted). Those hooks are gone from the seeded
|
|
26
|
+
# settings too.
|
|
34
27
|
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
# worker
|
|
28
|
+
# Invocation:
|
|
29
|
+
#
|
|
30
|
+
# --run-dir <RUN_DIR> Closes the run's worker-agent panes (`claude-worker` /
|
|
31
|
+
# `codex-worker` / `antigravity-worker` /
|
|
32
|
+
# `report-writer-worker` and the FleetView teammate
|
|
33
|
+
# prefixes), scoped to the LEAD's WINDOW. The lead pane is
|
|
34
|
+
# read from `<RUN_DIR>/state/lead-pane.id` (recorded once
|
|
35
|
+
# by the lead in its own foreground pane — reliable even
|
|
36
|
+
# though Claude Code's Bash tool strips `$TMUX` /
|
|
37
|
+
# `$TMUX_PANE`); it scopes the scan and is NEVER killed.
|
|
38
|
+
# Window scope keeps a second lead running in another
|
|
39
|
+
# window of the same session out of range.
|
|
40
40
|
#
|
|
41
41
|
# `--list` (alias `--dry-run`) prints `<pane_id>\t<pane_title>` per pane instead
|
|
42
|
-
# of killing
|
|
42
|
+
# of killing.
|
|
43
43
|
#
|
|
44
44
|
# `--keep <substr>` (repeatable) spares any pane whose current title contains
|
|
45
45
|
# <substr>, in both the kill and the list set. Used to preserve an in-flight
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
|
|
51
51
|
set -u
|
|
52
52
|
|
|
53
|
-
#
|
|
53
|
+
# Caller-pane resolution helper (okstra_resolve_caller_pane) — see
|
|
54
54
|
# lib/okstra/tmux-pane.sh. Used as the lead-pane fallback below so a missing /
|
|
55
55
|
# stale lead-pane.id resolves to the pane THIS process actually runs in (via
|
|
56
56
|
# ancestor-PID ↔ tmux pane_pid matching), never a foreign active-client pane.
|
|
@@ -58,43 +58,26 @@ set -u
|
|
|
58
58
|
_clean_script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
|
|
59
59
|
[ -r "$_clean_script_dir/lib/okstra/tmux-pane.sh" ] && . "$_clean_script_dir/lib/okstra/tmux-pane.sh"
|
|
60
60
|
|
|
61
|
-
# --reclaim-completed shells out to `python3 -m okstra_ctl.pane_reclaim`. The
|
|
62
|
-
# package is a bin-sibling in the repo layout (scripts/okstra_ctl) and under
|
|
63
|
-
# $OKSTRA_HOME/lib/python in the installed layout. Put both on PYTHONPATH so the
|
|
64
|
-
# import resolves regardless of where this script runs from.
|
|
65
|
-
_okstra_home="${OKSTRA_HOME:-$HOME/.okstra}"
|
|
66
|
-
export PYTHONPATH="${_clean_script_dir}:${_okstra_home}/lib/python${PYTHONPATH:+:$PYTHONPATH}"
|
|
67
|
-
|
|
68
61
|
MODE="kill" # kill | list
|
|
69
|
-
RECLAIM=0 # 1: trace pane 은 @okstra_status 가 완료(exited)일 때만 회수 (--reclaim-completed)
|
|
70
|
-
REAP=0
|
|
71
62
|
run_dir=""
|
|
72
63
|
KEEP_PATTERNS=() # --keep <substr>: panes whose title contains substr are spared from kill/list
|
|
73
64
|
while [[ $# -gt 0 ]]; do
|
|
74
65
|
case "$1" in
|
|
75
|
-
--list|--dry-run)
|
|
76
|
-
--reclaim-completed) RECLAIM=1 ;;
|
|
77
|
-
--reap) REAP=1 ;;
|
|
66
|
+
--list|--dry-run) MODE="list" ;;
|
|
78
67
|
--run-dir) shift; run_dir="${1-}" ;;
|
|
79
68
|
--run-dir=*) run_dir="${1#--run-dir=}" ;;
|
|
80
69
|
--keep) shift; KEEP_PATTERNS+=("${1-}") ;;
|
|
81
70
|
--keep=*) KEEP_PATTERNS+=("${1#--keep=}") ;;
|
|
82
71
|
-h|--help)
|
|
83
72
|
cat <<'USAGE'
|
|
84
|
-
usage: okstra-trace-cleanup.sh
|
|
73
|
+
usage: okstra-trace-cleanup.sh --run-dir <RUN_DIR> [--list] [--keep <substr>]...
|
|
85
74
|
|
|
86
|
-
--run-dir okstra run directory; closes that run's
|
|
87
|
-
--list
|
|
75
|
+
--run-dir okstra run directory; closes that run's worker-agent panes.
|
|
76
|
+
--list print "<pane_id>\t<pane_title>" per pane; no kill.
|
|
88
77
|
--dry-run alias for --list.
|
|
89
78
|
--keep <substr> exclude any pane whose title contains <substr> from the
|
|
90
79
|
kill/list set (repeatable). Used to spare an in-flight
|
|
91
80
|
report-writer at a round boundary.
|
|
92
|
-
--reclaim-completed with --run-dir: restrict trace panes to those whose
|
|
93
|
-
@okstra_status sidecar is terminal (stage=exited); in-flight
|
|
94
|
-
and teammate panes are preserved. Skips the title-allowlist
|
|
95
|
-
teammate scan. Combinable with --list.
|
|
96
|
-
--reap close every okstra trace pane under $CLAUDE_PROJECT_DIR/.okstra
|
|
97
|
-
(SessionEnd hook; no single run-dir applies).
|
|
98
81
|
USAGE
|
|
99
82
|
exit 0 ;;
|
|
100
83
|
*)
|
|
@@ -104,29 +87,16 @@ USAGE
|
|
|
104
87
|
shift
|
|
105
88
|
done
|
|
106
89
|
|
|
107
|
-
if [[
|
|
108
|
-
printf 'okstra-trace-cleanup.sh: --run-dir <RUN_DIR>
|
|
90
|
+
if [[ -z "$run_dir" ]]; then
|
|
91
|
+
printf 'okstra-trace-cleanup.sh: --run-dir <RUN_DIR> is required\n' >&2
|
|
109
92
|
exit 2
|
|
110
93
|
fi
|
|
111
94
|
|
|
112
|
-
#
|
|
113
|
-
#
|
|
114
|
-
# — else a symlinked component (e.g. macOS /tmp -> /private/tmp) makes the
|
|
115
|
-
# compare miss. Fall back to the literal value if the dir does not resolve.
|
|
116
|
-
_resolve() { (cd "$1" 2>/dev/null && pwd -P) || printf '%s' "$1"; }
|
|
117
|
-
[[ -n "$run_dir" ]] && run_dir="$(_resolve "$run_dir")"
|
|
118
|
-
project_dir=""
|
|
119
|
-
[[ -n "${CLAUDE_PROJECT_DIR:-}" ]] && project_dir="$(_resolve "$CLAUDE_PROJECT_DIR")"
|
|
120
|
-
|
|
121
|
-
# Lead pane. For a run, prefer the value the lead recorded in its own foreground
|
|
122
|
-
# pane; fall back to the active-pane probe. Rejected if the recorded pane is
|
|
123
|
-
# gone. For --reap there is no run state — probe the active pane, used only to
|
|
124
|
-
# avoid killing whatever pane the reap runs from.
|
|
95
|
+
# Lead pane. Prefer the value the lead recorded in its own foreground pane; fall
|
|
96
|
+
# back to the active-pane probe. Rejected if the recorded pane is gone.
|
|
125
97
|
lead_pane=""
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
[[ -r "$lead_pane_file" ]] && lead_pane="$(head -n1 "$lead_pane_file" 2>/dev/null || true)"
|
|
129
|
-
fi
|
|
98
|
+
lead_pane_file="$run_dir/state/lead-pane.id"
|
|
99
|
+
[[ -r "$lead_pane_file" ]] && lead_pane="$(head -n1 "$lead_pane_file" 2>/dev/null || true)"
|
|
130
100
|
if [[ -z "$lead_pane" ]] || ! tmux display-message -p -t "$lead_pane" '#{pane_id}' >/dev/null 2>&1; then
|
|
131
101
|
if type okstra_resolve_caller_pane >/dev/null 2>&1; then
|
|
132
102
|
lead_pane="$(okstra_resolve_caller_pane 2>/dev/null || true)"
|
|
@@ -135,17 +105,6 @@ if [[ -z "$lead_pane" ]] || ! tmux display-message -p -t "$lead_pane" '#{pane_id
|
|
|
135
105
|
fi
|
|
136
106
|
fi
|
|
137
107
|
|
|
138
|
-
# Does a trace pane's tag belong to the set we are closing?
|
|
139
|
-
_tag_in_scope() {
|
|
140
|
-
local tag="$1"
|
|
141
|
-
if [[ "$REAP" -eq 1 ]]; then
|
|
142
|
-
[[ -z "$tag" ]] && return 1
|
|
143
|
-
[[ -n "$project_dir" ]] && { [[ "$tag" == "$project_dir/"* ]]; return; }
|
|
144
|
-
return 0 # no project scope available → reap every tagged trace pane
|
|
145
|
-
fi
|
|
146
|
-
[[ "$tag" == "$run_dir" ]]
|
|
147
|
-
}
|
|
148
|
-
|
|
149
108
|
_title_in_okstra_scope() {
|
|
150
109
|
local title="$1"
|
|
151
110
|
case "$title" in
|
|
@@ -168,7 +127,6 @@ _title_in_okstra_scope() {
|
|
|
168
127
|
}
|
|
169
128
|
|
|
170
129
|
# A collected pane whose current title contains any --keep substring is spared.
|
|
171
|
-
# Applied at the final emit so both the tag scan and the title scan honour it.
|
|
172
130
|
_keep_excluded() {
|
|
173
131
|
local pid="$1" title pat
|
|
174
132
|
(( ${#KEEP_PATTERNS[@]} )) || return 1
|
|
@@ -181,54 +139,25 @@ _keep_excluded() {
|
|
|
181
139
|
|
|
182
140
|
collect_okstra_panes() {
|
|
183
141
|
local -a panes=()
|
|
184
|
-
local pid
|
|
185
|
-
|
|
186
|
-
#
|
|
187
|
-
#
|
|
188
|
-
#
|
|
189
|
-
#
|
|
190
|
-
#
|
|
191
|
-
#
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
while IFS=$'\t' read -r pid trace_tag worker_tag status_tag; do
|
|
195
|
-
trace_tag="${trace_tag#x}"; worker_tag="${worker_tag#x}"; status_tag="${status_tag#x}"
|
|
142
|
+
local pid title
|
|
143
|
+
|
|
144
|
+
# Title-allowlisted worker-agent panes in the lead's WINDOW. `list-panes -t
|
|
145
|
+
# <pane>` (no `-s`, no `-a`) resolves the window containing that pane and lists
|
|
146
|
+
# only that window's panes. Split-pane teammates always land in the lead's
|
|
147
|
+
# window, so this catches all of THIS run's worker panes while a second okstra
|
|
148
|
+
# lead in another window of the same tmux session is left untouched. Skipped
|
|
149
|
+
# when the lead pane is unknown.
|
|
150
|
+
[[ -n "$lead_pane" ]] || return 0
|
|
151
|
+
while IFS=$'\t' read -r pid title; do
|
|
196
152
|
[[ -n "$pid" ]] || continue
|
|
197
153
|
[[ "$pid" == "$lead_pane" ]] && continue
|
|
198
|
-
if
|
|
199
|
-
if [[ "$RECLAIM" -eq 1 ]]; then
|
|
200
|
-
# reclaim 모드: 완료(stage=exited)된 worker 의 pane 만. status 태그가
|
|
201
|
-
# 없거나(teammate/비-wrapper pane) 미완료면 보존.
|
|
202
|
-
[[ -n "$status_tag" ]] || continue
|
|
203
|
-
python3 -m okstra_ctl.pane_reclaim "$status_tag" || continue
|
|
204
|
-
fi
|
|
154
|
+
if _title_in_okstra_scope "$title"; then
|
|
205
155
|
panes+=("$pid")
|
|
206
156
|
fi
|
|
207
|
-
done < <(tmux list-panes -
|
|
208
|
-
-F '#{pane_id}'$'\t''
|
|
209
|
-
2>/dev/null || true)
|
|
210
|
-
# (2) Title-allowlisted worker-agent panes in the lead's WINDOW. Only for a
|
|
211
|
-
# run (reap leaves these harness-owned panes to the harness). `list-panes -t
|
|
212
|
-
# <pane>` (no `-s`, no `-a`) resolves the window containing that pane and
|
|
213
|
-
# lists only that window's panes. Split-pane teammates always land in the
|
|
214
|
-
# lead's window, so this catches all of THIS run's worker panes while a second
|
|
215
|
-
# okstra lead in another window of the same tmux session — whose in-flight
|
|
216
|
-
# worker panes are untagged and title-only — is left untouched. Skipped when
|
|
217
|
-
# the lead pane is unknown. reclaim 모드는 teammate pane 을 회수하지 않으므로
|
|
218
|
-
# (완료 판정 불가, trace-only) 이 스캔을 건너뛴다.
|
|
219
|
-
if [[ "$REAP" -eq 0 && "$RECLAIM" -eq 0 && -n "$lead_pane" ]]; then
|
|
220
|
-
while IFS=$'\t' read -r pid title; do
|
|
221
|
-
[[ -n "$pid" ]] || continue
|
|
222
|
-
[[ "$pid" == "$lead_pane" ]] && continue
|
|
223
|
-
if _title_in_okstra_scope "$title"; then
|
|
224
|
-
panes+=("$pid")
|
|
225
|
-
fi
|
|
226
|
-
done < <(tmux list-panes -t "$lead_pane" \
|
|
227
|
-
-F '#{pane_id}'$'\t''#{pane_title}' 2>/dev/null || true)
|
|
228
|
-
fi
|
|
157
|
+
done < <(tmux list-panes -t "$lead_pane" \
|
|
158
|
+
-F '#{pane_id}'$'\t''#{pane_title}' 2>/dev/null || true)
|
|
229
159
|
|
|
230
|
-
#
|
|
231
|
-
# Then drop any pane a --keep pattern spares (in-flight report-writer).
|
|
160
|
+
# Drop any pane a --keep pattern spares (in-flight report-writer).
|
|
232
161
|
if (( ${#panes[@]} )); then
|
|
233
162
|
printf '%s\n' "${panes[@]}" | awk 'NF && !seen[$0]++' | while IFS= read -r _pid; do
|
|
234
163
|
_keep_excluded "$_pid" && continue
|
package/runtime/bin/okstra.sh
CHANGED
|
@@ -187,6 +187,7 @@ okstra execution summary:
|
|
|
187
187
|
task brief: ${BRIEF_PATH}
|
|
188
188
|
directive: ${DIRECTIVE:-None}
|
|
189
189
|
clarification response: ${CLARIFICATION_RESPONSE_PATH:-None}
|
|
190
|
+
selected direction: ${SELECTED_DIRECTION_PATH:-None}
|
|
190
191
|
workers override: ${WORKERS_OVERRIDE:-None}
|
|
191
192
|
executor (implementation only): ${EXECUTOR_OVERRIDE:-default(claude)}
|
|
192
193
|
approved plan: ${APPROVED_PLAN_PATH:-None}
|
|
@@ -237,6 +238,7 @@ PY_ARGS=(
|
|
|
237
238
|
[[ "$APPROVE_PLAN_ACK" == "true" ]] && PY_ARGS+=(--approve)
|
|
238
239
|
[[ -n "${IMPLEMENTATION_OPTION-}" ]] && PY_ARGS+=(--implementation-option "$IMPLEMENTATION_OPTION")
|
|
239
240
|
[[ -n "${CLARIFICATION_RESPONSE_PATH-}" ]] && PY_ARGS+=(--clarification-response "$CLARIFICATION_RESPONSE_PATH")
|
|
241
|
+
[[ -n "${SELECTED_DIRECTION_PATH-}" ]] && PY_ARGS+=(--selected-direction "$SELECTED_DIRECTION_PATH")
|
|
240
242
|
[[ -n "${WORK_CATEGORY-}" ]] && PY_ARGS+=(--work-category "$WORK_CATEGORY")
|
|
241
243
|
[[ -n "${BASE_REF-}" ]] && PY_ARGS+=(--base-ref "$BASE_REF")
|
|
242
244
|
[[ -n "${STAGE-}" ]] && PY_ARGS+=(--stage "$STAGE")
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: direction-selection-worker
|
|
3
|
+
version: 1
|
|
4
|
+
kind: role
|
|
5
|
+
appliesTo: direction-selection-worker
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Direction Selection Worker Duty Contract
|
|
9
|
+
|
|
10
|
+
## Responsibility
|
|
11
|
+
|
|
12
|
+
Compare feasible directions before planning in `candidate-comparison` mode, or validate one preselected direction in `preselected-validation` mode.
|
|
13
|
+
|
|
14
|
+
## Required conduct
|
|
15
|
+
|
|
16
|
+
In `candidate-comparison` mode, inspect the evidence needed to distinguish candidates, submit no more than three candidates, state the strongest counterevidence for each one, and map every candidate to the stable brief end-state IDs it satisfies, preserves, or leaves unresolved. In `preselected-validation` mode, validate the one preselected direction against that evidence and mapping; the worker must not generate new candidates.
|
|
17
|
+
|
|
18
|
+
## Decision principles
|
|
19
|
+
|
|
20
|
+
Score candidates against the same stated criteria. Prefer evidence-backed feasibility over familiarity, and preserve a rejected candidate when its evidence or trade-off could affect the later planning decision.
|
|
21
|
+
|
|
22
|
+
## Authority and boundaries
|
|
23
|
+
|
|
24
|
+
Select directions only. Do not edit project state, author detailed file lists, create stage maps, prescribe execution commands, or approve an implementation plan.
|
|
25
|
+
|
|
26
|
+
## Evidence standard
|
|
27
|
+
|
|
28
|
+
Each candidate, score, counterexample, and requirement mapping cites inspected evidence. State uncertainty when the code or brief cannot establish a criterion.
|
|
29
|
+
|
|
30
|
+
## Collaboration contract
|
|
31
|
+
|
|
32
|
+
Reason independently from other workers. Do not collapse overlapping candidates or seek agreement before convergence; provide the evidence that lets the lead merge and re-evaluate them.
|
|
33
|
+
|
|
34
|
+
## Completion criteria
|
|
35
|
+
|
|
36
|
+
In `candidate-comparison` mode, every submitted candidate has a criterion score, counterevidence, and stable requirement mapping. In `preselected-validation` mode, the one preselected direction has a validation result, counterevidence, and stable requirement mapping. Rejected candidates retain their audit reason and evidence.
|
|
37
|
+
|
|
38
|
+
## Forbidden conduct
|
|
39
|
+
|
|
40
|
+
Do not turn a candidate into a detailed implementation plan, invent a requirement ID, omit contrary evidence, present a selection as user approval, or generate a new candidate in `preselected-validation` mode.
|
|
41
|
+
|
|
42
|
+
## Blocked-state reporting
|
|
43
|
+
|
|
44
|
+
Name the missing evidence or unresolved requirement that prevents a candidate comparison, the inspection attempted, and the criterion it leaves unscored.
|
|
@@ -9,11 +9,19 @@ appliesTo: planning-worker
|
|
|
9
9
|
|
|
10
10
|
## Responsibility
|
|
11
11
|
|
|
12
|
-
Produce an implementation
|
|
12
|
+
Produce an executable implementation plan without writing the implementation.
|
|
13
|
+
|
|
14
|
+
### Selected-direction responsibility
|
|
15
|
+
|
|
16
|
+
When `selected-direction.json` is present, read it and the requirements ledger first. Preserve the selected direction's core mechanism, architecture boundaries, user constraints, and planning invariants. Concretize only its files, interfaces, stages, validation, and rollback. Link every file and stage back to original requirements, and link every original requirement forward to its files, stages, and checks. If current code evidence requires changing the direction, return `direction-invalidated` with evidence and stop. Direction selection remains upstream of this branch.
|
|
17
|
+
|
|
18
|
+
### Legacy candidate-comparison compatibility
|
|
19
|
+
|
|
20
|
+
For a legacy rerun without `selected-direction.json`, retain Option Candidates, trade-offs, the Recommended Option, and `P-Opt-*` verification semantics. Only this compatibility branch compares alternatives or chooses a recommendation.
|
|
13
21
|
|
|
14
22
|
## Required conduct
|
|
15
23
|
|
|
16
|
-
Read the current state of the code the work touches before
|
|
24
|
+
Read the current state of the code the work touches before planning. Split work into stages along real dependencies with each stage's validation signal and rollback. Connect every requirement to the stage that satisfies it. In the selected-direction branch, verify direction preservation before adding plan detail. In the legacy branch, compare options on current-code evidence.
|
|
17
25
|
|
|
18
26
|
## Decision principles
|
|
19
27
|
|
|
@@ -29,11 +37,11 @@ Every cited path, symbol, and command must exist as written and be executable in
|
|
|
29
37
|
|
|
30
38
|
## Collaboration contract
|
|
31
39
|
|
|
32
|
-
Draft independently of the other planners
|
|
40
|
+
Draft independently of the other planners. Leave contested plan details to convergence and the lead, and hand the executor a plan complete enough to follow without re-deriving the selected direction.
|
|
33
41
|
|
|
34
42
|
## Completion criteria
|
|
35
43
|
|
|
36
|
-
|
|
44
|
+
For selected-direction planning, the snapshot and requirements ledger are preserved; files, interfaces, stages, validation, rollback, and requirements links are mutually consistent; planning invariants have evidence; and no hidden direction change is present. For legacy candidate-comparison compatibility, Option Candidates, trade-offs, the Recommended Option, and `P-Opt-*` semantics remain present. Every unresolved decision is recorded rather than assumed, and no stage depends on work the plan never places.
|
|
37
45
|
|
|
38
46
|
## Forbidden conduct
|
|
39
47
|
|