okstra 0.82.1 → 0.83.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 +7 -6
- package/README.md +6 -6
- package/docs/kr/architecture.md +8 -7
- package/docs/kr/cli.md +2 -2
- package/docs/kr/performance-improvement-plan-v2.md +14 -14
- package/docs/project-structure-overview.md +7 -8
- package/docs/superpowers/plans/2026-06-15-coding-preflight-pack-dispatch-path.md +504 -0
- package/docs/superpowers/plans/2026-06-15-internal-skill-migration-final-fixups.md +342 -0
- package/docs/superpowers/plans/2026-06-15-internal-skill-migration-fixups.md +258 -0
- package/docs/superpowers/plans/2026-06-15-internal-skill-migration-remaining-fixups.md +387 -0
- package/docs/superpowers/plans/2026-06-15-internal-skill-resource-migration.md +749 -0
- package/docs/superpowers/plans/2026-06-15-worker-prompt-anchor-final-fixups.md +828 -0
- package/docs/superpowers/plans/2026-06-15-worker-prompt-header-error-contract.md +490 -0
- package/docs/task-process/README.md +1 -1
- package/docs/task-process/error-analysis.md +1 -1
- package/docs/task-process/final-verification.md +1 -1
- package/docs/task-process/implementation-planning.md +1 -1
- package/docs/task-process/implementation.md +1 -1
- package/docs/task-process/release-handoff.md +1 -1
- package/docs/task-process/requirements-discovery.md +2 -2
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/TODO.md +2 -0
- package/runtime/agents/workers/claude-worker.md +8 -8
- package/runtime/agents/workers/codex-worker.md +8 -8
- package/runtime/agents/workers/gemini-worker.md +8 -8
- package/runtime/agents/workers/report-writer-worker.md +2 -2
- package/runtime/bin/lib/okstra/globals.sh +0 -1
- package/runtime/bin/okstra-wrapper-status.py +1 -1
- package/runtime/{skills/okstra-coding-preflight → prompts/coding-preflight}/languages/python.md +2 -2
- package/runtime/{skills/okstra-coding-preflight → prompts/coding-preflight}/languages/rust.md +1 -1
- package/runtime/{skills/okstra-coding-preflight/SKILL.md → prompts/coding-preflight/overview.md} +27 -38
- package/runtime/prompts/launch.template.md +5 -3
- package/runtime/{skills/okstra-context-loader/SKILL.md → prompts/lead/context-loader.md} +7 -14
- package/runtime/{skills/okstra-convergence/SKILL.md → prompts/lead/convergence.md} +12 -19
- package/runtime/{agents/SKILL.md → prompts/lead/okstra-lead-contract.md} +53 -59
- package/runtime/{skills/okstra-report-writer/SKILL.md → prompts/lead/report-writer.md} +12 -19
- package/runtime/{skills/okstra-team-contract/SKILL.md → prompts/lead/team-contract.md} +13 -19
- package/runtime/prompts/profiles/_coding-conventions-preflight.md +2 -2
- package/runtime/prompts/profiles/_common-contract.md +2 -2
- package/runtime/prompts/profiles/_implementation-executor.md +2 -2
- package/runtime/prompts/profiles/_implementation-verifier.md +2 -2
- package/runtime/prompts/profiles/error-analysis.md +2 -2
- package/runtime/prompts/profiles/final-verification.md +1 -1
- package/runtime/prompts/profiles/implementation-planning.md +4 -4
- package/runtime/prompts/profiles/requirements-discovery.md +2 -2
- package/runtime/python/okstra_ctl/codex_dispatch.py +12 -61
- package/runtime/python/okstra_ctl/context_cost.py +14 -11
- package/runtime/python/okstra_ctl/dispatch_core.py +36 -13
- package/runtime/python/okstra_ctl/paths.py +27 -1
- package/runtime/python/okstra_ctl/render.py +62 -8
- package/runtime/python/okstra_ctl/run.py +5 -5
- package/runtime/python/okstra_ctl/worker_prompt_headers.py +126 -0
- package/runtime/python/okstra_token_usage/claude.py +1 -1
- package/runtime/python/okstra_token_usage/collect.py +1 -1
- package/runtime/templates/reports/task-brief.template.md +2 -2
- package/runtime/templates/worker-prompt-preamble.md +2 -2
- package/runtime/validators/lib/validate-assets.sh +12 -4
- package/runtime/validators/validate-run.py +3 -3
- package/runtime/validators/validate_session_conformance.py +11 -11
- package/src/install.mjs +95 -81
- package/src/skill-catalog.mjs +35 -0
- package/src/uninstall.mjs +5 -0
- /package/runtime/{skills/okstra-coding-preflight/architecture → prompts/coding-preflight/architectures}/hexagonal.md +0 -0
- /package/runtime/{skills/okstra-coding-preflight → prompts/coding-preflight}/clean-code.md +0 -0
- /package/runtime/{skills/okstra-coding-preflight/languages/nodejs.md → prompts/coding-preflight/frameworks/node-server.md} +0 -0
- /package/runtime/{skills/okstra-coding-preflight → prompts/coding-preflight}/languages/java.md +0 -0
- /package/runtime/{skills/okstra-coding-preflight → prompts/coding-preflight}/languages/javascript-typescript.md +0 -0
- /package/runtime/{skills/okstra-coding-preflight → prompts/coding-preflight}/languages/kotlin.md +0 -0
- /package/runtime/{skills/okstra-coding-preflight → prompts/coding-preflight}/languages/sql.md +0 -0
|
@@ -9,7 +9,7 @@ at Phase 5, BEFORE constructing the verifier worker dispatch prompts.
|
|
|
9
9
|
|
|
10
10
|
## Verifier roles (resolved at run-prep time)
|
|
11
11
|
|
|
12
|
-
- **Verifier dispatch labelling.** When Lead dispatches a verifier (here, and identically in `final-verification`) it MUST set the Agent `name` to `<provider>-verifier` (e.g. `claude-verifier`, `codex-verifier`) — NOT the generic `<provider>-worker` — so the FleetView teammate pill names the job; see `
|
|
12
|
+
- **Verifier dispatch labelling.** When Lead dispatches a verifier (here, and identically in `final-verification`) it MUST set the Agent `name` to `<provider>-verifier` (e.g. `claude-verifier`, `codex-verifier`) — NOT the generic `<provider>-worker` — so the FleetView teammate pill names the job; see `prompts/lead/okstra-lead-contract.md` Phase 4 "Agent `name` on dispatch". For a `codex` / `gemini` verifier, Lead MUST additionally inject a `**Pane role:** verifier` line into the dispatched prompt body so the CLI wrapper titles its tmux trace pane `<cli>-verifier-<pid>` (the wrapper reads that line per `agents/workers/_cli-wrapper-template.md`). Token attribution is unaffected — the collector matches any `agentName` beginning `<provider>-`.
|
|
13
13
|
- The verifier slots are `Claude verifier` and `Codex verifier`, plus `Gemini verifier` **only when `gemini` is in the resolved `--workers` roster**. Every verifier in the resolved roster is dispatched regardless of which provider holds the executor role; the executor's own provider is run *separately* as a verifier (a fresh CLI session with no shared context) so that no verdict is produced from the same session that wrote the diff. Verifiers MUST NOT call Edit, Write, or any Bash command that mutates files outside the run's artifact directories. If a verifier wants a fix, it records the recommendation in its worker result; it does not apply the fix itself.
|
|
14
14
|
- Session isolation — not model-variant divergence — is the primary self-review safeguard: each verifier is a separate CLI invocation with its own context window, so reusing the same model variant for executor and same-provider verifier is acceptable. Different model variants (e.g. executor=opus / Claude verifier=sonnet) remain recommended when available.
|
|
15
15
|
- Phase-specific model defaults override the shared defaults: `Claude verifier`=`opus`, `Codex verifier`=`gpt-5.5`, `Gemini verifier`=`auto` (only when present in the roster). The `Executor`'s model is taken from the provider-specific worker model corresponding to `--executor`: claude→`--claude-model` (default `opus`), codex→`--codex-model` (default `gpt-5.5`), gemini→`--gemini-model` (default `auto`).
|
|
@@ -88,7 +88,7 @@ The final report keeps both — executor's `Validation evidence` AND each verifi
|
|
|
88
88
|
Re-running commands proves the diff *builds and passes*; it does NOT prove the diff is *well-designed*. Lint/test green is necessary but not sufficient — self-mocked tests, interaction-only assertions, and untruthful names all survive a green pipeline. This gate is the filter for exactly those defects, so the executor's design errors are caught here instead of in post-merge PR review. It is a real gate, not a checklist: it enumerates the full diff and a blocking hit forces `FAIL`.
|
|
89
89
|
|
|
90
90
|
- **Scope (no silent sampling).** Enumerate every changed source/test file via `git diff --name-only <base>...HEAD` and review each one. Skipping a changed file silently is a `contract-violated` outcome. If a file's language has no reference and is not covered by the agnostic checks below, record `design-review skipped: <file> (language=<x> no reference)` — never pass it silently.
|
|
91
|
-
- **Load the same conventions the executor used
|
|
91
|
+
- **Load the same conventions the executor used via the routed pack.** Use this worker prompt's `**Coding preflight pack:**` anchor header as the absolute path to the installed routed pack. Read `overview.md` first, then `clean-code.md`, then apply the router's three ordered stages: language, framework, architecture. In each stage, iterate every rule, treat a rule as matched when any listed condition is true, and accumulate every matching resource — including `frameworks/node-server.md` for server-side Node work and `architectures/hexagonal.md` for ports-and-adapters / NestJS-hex layouts. Degrade to the agnostic checks below when the resolved pack is unreadable, and record either `coding-conventions: resources=<...>` or `coding-conventions: resource-unavailable → applied <project rules + agnostic principles>`. The verifier does NOT inline language rules — it loads the same situation-specific resources as the executor preflight.
|
|
92
92
|
- **Load project review rule packs when present.** Search the project root, `.claude/skills`, and up to two parent `skills/` directories for `*review*/SKILL.md` rule packs. Read their referenced `references/*.md` files and apply them as an overlay on this static review. If a premium review skill exists, use its coverage philosophy (recall-first enumeration followed by verify-only confirmation) as the verifier's mental model, but do NOT dispatch extra reviewer agents unless the task explicitly configured them. Record `project-review-rules: <paths read>` or `project-review-rules: none found` in the worker result.
|
|
93
93
|
- **Blocking checks (any hit → verdict `FAIL`, cited `path:line` + rule name, recommended fix recorded — the verifier does NOT apply it):**
|
|
94
94
|
- **New duplication / DRY:** two or more newly added or meaningfully modified blocks implement the same helper stack, transform, or domain rule. Literal copy-paste is always blocking; semantically equivalent transforms across services are blocking unless the approved plan explicitly justified keeping them separate. Recommend the shared module location.
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
- **Codebase-first ambiguity resolution (defect rule)**: any ambiguity about repro, file behavior, or symbol semantics that can be answered by `Read` / `Grep` / log inspection MUST be resolved that way and recorded with file:line (or log-line) evidence. Writing a clarification row for something the codebase or shipped logs already answer is a defect of this phase.
|
|
38
38
|
- **Evidence note required inside `Statement`**: every clarification row includes `Evidence checked: <path:line>` or `Evidence checked: none — <reporter-only reason>` in the `Statement` cell. `none` is allowed ONLY when the row's nature is "only the reporter can answer this" (reporter-side data, business priority, environment they observed). A row with `none` that *could* have been answered by code or logs is a defect.
|
|
39
39
|
- Cross-verification mode:
|
|
40
|
-
- Phase 5.5 convergence runs in **adversarial mode** for this phase (`convergence.adversarial=true`). Verifiers actively try to refute each root-cause / reproduction claim by directly re-inspecting the cited code, logs, or config; the burden of proof sits on the claim. See `
|
|
41
|
-
- **Coverage critic (opt-in)**: when `convergence.critic.enabled=true` (chosen via the okstra-run picker or `--critic`), a reused-worker critic pass is dispatched concurrently with the first convergence reverify round to surface missed findings; its gaps are merged only after a 1-round adversarial reverify that follows convergence. See `
|
|
40
|
+
- Phase 5.5 convergence runs in **adversarial mode** for this phase (`convergence.adversarial=true`). Verifiers actively try to refute each root-cause / reproduction claim by directly re-inspecting the cited code, logs, or config; the burden of proof sits on the claim. See `prompts/lead/convergence.md` §"Adversarial Verification Mode". A single evidence-backed refutation prevents a finding from reaching consensus.
|
|
41
|
+
- **Coverage critic (opt-in)**: when `convergence.critic.enabled=true` (chosen via the okstra-run picker or `--critic`), a reused-worker critic pass is dispatched concurrently with the first convergence reverify round to surface missed findings; its gaps are merged only after a 1-round adversarial reverify that follows convergence. See `prompts/lead/convergence.md` "Coverage critic pass".
|
|
42
42
|
- Non-goals:
|
|
43
43
|
- implementation details unless they are necessary to validate the cause
|
|
44
44
|
- **source code edits, builds, migrations, or deployments** — this run produces evidence and cause analysis only; the fix belongs to a later `implementation-planning` run followed by an `implementation` run
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
4. **Verifier dissent preserved** — if workers reach different verdicts, the disagreement is visible in section 1.2; synthesis hides nothing.
|
|
49
49
|
5. **No source-mutation audit** — scan the run's session transcripts for Edit / Write or state-mutating Bash commands that touch paths OUTSIDE `<PROJECT_ROOT>/.okstra/**` and outside the assigned run-artifact paths. Writes to worker prompts, audit sidecars, team-state, the final-report `data.json`, and rendered reports under the run directory are allowed okstra artifacts. Any source/schema/deployment mutation means the run has crossed into implementation and MUST be re-routed; do NOT silently strip the evidence.
|
|
50
50
|
- Cross-verification mode:
|
|
51
|
-
- **Acceptance critic (opt-in)**: when `convergence.critic.enabled=true` (chosen via the okstra-run picker or `--critic`), a reused-worker **acceptance devil's-advocate** pass is dispatched concurrently with the first convergence reverify round to surface candidate acceptance blockers the verifiers may have missed; candidates are verified only after convergence completes. Each candidate is verified **confirm-or-downgrade**: confirmed → an `Acceptance Blockers` row (which, since `accepted` requires zero blockers, moves the verdict to `conditional-accept` / `blocked`); unconfirmed → a `Residual Risk` row (never dropped). See `
|
|
51
|
+
- **Acceptance critic (opt-in)**: when `convergence.critic.enabled=true` (chosen via the okstra-run picker or `--critic`), a reused-worker **acceptance devil's-advocate** pass is dispatched concurrently with the first convergence reverify round to surface candidate acceptance blockers the verifiers may have missed; candidates are verified only after convergence completes. Each candidate is verified **confirm-or-downgrade**: confirmed → an `Acceptance Blockers` row (which, since `accepted` requires zero blockers, moves the verdict to `conditional-accept` / `blocked`); unconfirmed → a `Residual Risk` row (never dropped). See `prompts/lead/convergence.md` "Acceptance critic pass (final-verification)".
|
|
52
52
|
- Non-goals:
|
|
53
53
|
- proposing unrelated refactors beyond the delivered scope
|
|
54
54
|
- **source code edits, follow-up bug fixes, or scope expansion** — this run renders a verdict only; defects detected here become inputs to a new `error-analysis` or `implementation-planning` run
|
|
@@ -40,9 +40,9 @@
|
|
|
40
40
|
- Approval gate (phase-specific addendum to shared authority rule):
|
|
41
41
|
- The YAML frontmatter `approved: true|false` field is the only authorised approval gate. report-writer always emits `approved: false`. The user clears it either by (a) editing the frontmatter line to `approved: true` directly, or (b) invoking the next phase with `--approve` so the CLI flips the frontmatter on the user's behalf. `okstra_ctl.run._validate_approved_plan` reads this field and refuses entry until it is `true`.
|
|
42
42
|
- Cross-verification mode:
|
|
43
|
-
- Phase 5.5 finding convergence runs in **adversarial mode** for this phase (`convergence.adversarial=true`). Verifiers actively try to refute each worker finding (requirement gap / risk / option) by re-inspecting its cited evidence; the burden of proof sits on the claim. See `
|
|
44
|
-
- §5.5.9 plan-body verification runs with an **adversarial posture** (`
|
|
45
|
-
- **Coverage critic (opt-in)**: when `convergence.critic.enabled=true` (chosen via the okstra-run picker or `--critic`), a reused-worker critic pass is dispatched concurrently with the first convergence reverify round to surface missed findings; its gaps are merged only after a 1-round adversarial reverify that follows convergence. See `
|
|
43
|
+
- Phase 5.5 finding convergence runs in **adversarial mode** for this phase (`convergence.adversarial=true`). Verifiers actively try to refute each worker finding (requirement gap / risk / option) by re-inspecting its cited evidence; the burden of proof sits on the claim. See `prompts/lead/convergence.md` §"Adversarial Verification Mode".
|
|
44
|
+
- §5.5.9 plan-body verification runs with an **adversarial posture** (`prompts/lead/convergence.md` §"Adversarial plan-body posture"): verifiers open and confirm every cited path / command and put the burden of proof on the plan. The gate threshold is unchanged — a *majority* `DISAGREE` (`majority-disagree`) is still required to block approval; a single dissent does not.
|
|
45
|
+
- **Coverage critic (opt-in)**: when `convergence.critic.enabled=true` (chosen via the okstra-run picker or `--critic`), a reused-worker critic pass is dispatched concurrently with the first convergence reverify round to surface missed findings; its gaps are merged only after a 1-round adversarial reverify that follows convergence. See `prompts/lead/convergence.md` "Coverage critic pass".
|
|
46
46
|
- Non-goals:
|
|
47
47
|
- code-level micro-optimization unless it changes the implementation approach
|
|
48
48
|
- **source code edits of any kind** — this run produces a plan document only; Edit/Write on project source files is forbidden until the plan is approved and a separate `implementation` run starts
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
- the YAML frontmatter MUST include the line `implementation-option:` directly under `approved:` (report-writer always emits it with an **empty value**). The user selects which Option Candidate the next `implementation` run executes by filling this line with that option's name (manual edit or `--implementation-option <name>` CLI). When left empty, the `implementation` run falls back to the `Recommended Option`.
|
|
91
91
|
- **the frontmatter `approved: false` line is rendered unconditionally; if the plan-body verification gate (§5.5.9) returns `blocked-by-disagreement` or `aborted-non-result`, the writer MUST keep `approved: false` and the validator refuses any report that ships with `approved: true` under such a gate result.**
|
|
92
92
|
- every ambiguity flagged during pre-planning that the user must resolve before approval registered as a `Blocks=approval` row in the `## 1. Clarification Items` table (do NOT create a separate `Open Questions` block under the implementation plan body — the unified table is the single home)
|
|
93
|
-
- **§5.5.9 Plan Body Verification (BLOCKING).** After report-writer finishes the draft, the lead MUST run a worker peer-review round on the consolidated plan body (Option Candidates / Trade-off Matrix / Recommended Option / Stage Map and per-stage sections / Dependency / Validation Checklist / Rollback / Requirement Coverage) and populate `### 5.5.9 Plan Body Verification` in the final report. The round protocol, plan-item ID scheme (`P-Opt-*` / `P-Step-*` / `P-Dep-*` / `P-Val-*` / `P-Rb-*`), verdict semantics, gate-result classification, and dissent log format are defined in `
|
|
93
|
+
- **§5.5.9 Plan Body Verification (BLOCKING).** After report-writer finishes the draft, the lead MUST run a worker peer-review round on the consolidated plan body (Option Candidates / Trade-off Matrix / Recommended Option / Stage Map and per-stage sections / Dependency / Validation Checklist / Rollback / Requirement Coverage) and populate `### 5.5.9 Plan Body Verification` in the final report. The round protocol, plan-item ID scheme (`P-Opt-*` / `P-Step-*` / `P-Dep-*` / `P-Val-*` / `P-Rb-*`), verdict semantics, gate-result classification, and dissent log format are defined in `prompts/lead/convergence.md` "Plan-body verification mode". The four gate-result values are `passed`, `passed-with-dissent`, `blocked-by-disagreement`, `aborted-non-result`. When the gate would have been `blocked-by-disagreement` or `aborted-non-result`, the lead MUST NOT silently flip it to one of the passing values to "unblock" the run — that is a contract violation. When `convergence.adversarial=true` (the default for this phase), this round uses the adversarial posture — verifiers confirm cited paths/commands and the burden of proof is on the plan — but the gate threshold stays `majority-disagree` (see that skill's §"Adversarial plan-body posture").
|
|
94
94
|
- **Decision-record evaluation (sole owner)**: this phase is the **single owner** of decision-record evaluation in the okstra lifecycle. The brief never evaluates or drafts decision records — it only forwards `adr-candidate:*` signals. Every `adr-candidate:*` entry inherited from the brief's `Open Questions` is a mandatory evaluation target. In addition, evaluate every decision the recommended option introduces against the three criteria:
|
|
95
95
|
1. **Hard to reverse** — would changing the decision later cost meaningfully more than deciding now?
|
|
96
96
|
2. **Surprising without context** — would a future reader, seeing only the code, wonder "why was it built this way?"?
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
- **Codebase-first ambiguity resolution (defect rule)**: any ambiguity that can be answered by `Read` / `Grep` / file inspection MUST be resolved that way and recorded with file:line evidence. Writing a clarification row for something the codebase already answers is a defect of this phase.
|
|
53
53
|
- **Evidence note required inside `Statement`**: every clarification row includes `Evidence checked: <path:line>` or `Evidence checked: none — <human-only reason>` in the `Statement` cell. `none` is allowed ONLY when the row's nature is "only a human can answer this" (reporter intent, business priority, external authority). A row with `none` that *could* have been answered by the codebase is a defect.
|
|
54
54
|
- Cross-verification mode:
|
|
55
|
-
- Phase 5.5 convergence runs in **adversarial mode** for this phase (`convergence.adversarial=true`). Verifiers actively try to refute each worker's finding by directly re-inspecting the cited evidence; the burden of proof sits on the claim. See `
|
|
56
|
-
- **Coverage critic (opt-in)**: when `convergence.critic.enabled=true` (chosen via the okstra-run picker or `--critic`), a reused-worker critic pass is dispatched concurrently with the first convergence reverify round to surface missed findings; its gaps are merged only after a 1-round adversarial reverify that follows convergence. See `
|
|
55
|
+
- Phase 5.5 convergence runs in **adversarial mode** for this phase (`convergence.adversarial=true`). Verifiers actively try to refute each worker's finding by directly re-inspecting the cited evidence; the burden of proof sits on the claim. See `prompts/lead/convergence.md` §"Adversarial Verification Mode". A single evidence-backed refutation prevents a finding from reaching consensus.
|
|
56
|
+
- **Coverage critic (opt-in)**: when `convergence.critic.enabled=true` (chosen via the okstra-run picker or `--critic`), a reused-worker critic pass is dispatched concurrently with the first convergence reverify round to surface missed findings; its gaps are merged only after a 1-round adversarial reverify that follows convergence. See `prompts/lead/convergence.md` "Coverage critic pass".
|
|
57
57
|
- Non-goals:
|
|
58
58
|
- full implementation design unless it is required to decide the next phase
|
|
59
59
|
- **source code edits, plan authoring, builds, or deployments** — this run only classifies the work and routes it; deeper analysis and planning belong to subsequent phases
|
|
@@ -21,6 +21,7 @@ from typing import Any, Mapping, Sequence
|
|
|
21
21
|
from .lead_events import LeadEvent, append_lead_event
|
|
22
22
|
from .wrapper_status import status_path_for_prompt
|
|
23
23
|
from .paths import task_dir
|
|
24
|
+
from .worker_prompt_headers import WorkerPromptHeaderError, worker_prompt_headers
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
SUPPORTED_CLI_WORKERS = {
|
|
@@ -32,7 +33,6 @@ BACKEND_MIXED = "mixed"
|
|
|
32
33
|
MAX_WORKER_ATTEMPTS = 2
|
|
33
34
|
REPORT_WRITER_WORKER_ID = "report-writer"
|
|
34
35
|
REPORT_LANGUAGE_VALUES = {"en", "ko", "auto"}
|
|
35
|
-
WORKER_PREAMBLE_PATH = Path.home() / ".okstra" / "templates" / "worker-prompt-preamble.md"
|
|
36
36
|
ANALYSIS_WORKER_LABELS = {
|
|
37
37
|
"codex": "Codex worker",
|
|
38
38
|
"gemini": "Gemini worker",
|
|
@@ -676,19 +676,17 @@ def _base_prompt_headers(
|
|
|
676
676
|
prompt_rel: str,
|
|
677
677
|
result_rel: str,
|
|
678
678
|
) -> list[str]:
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
),
|
|
691
|
-
]
|
|
679
|
+
try:
|
|
680
|
+
return worker_prompt_headers(
|
|
681
|
+
project_root=project_root,
|
|
682
|
+
prompt_rel=prompt_rel,
|
|
683
|
+
result_rel=result_rel,
|
|
684
|
+
worker_id=worker_id,
|
|
685
|
+
manifest=manifest,
|
|
686
|
+
active_context=active_context,
|
|
687
|
+
)
|
|
688
|
+
except WorkerPromptHeaderError as exc:
|
|
689
|
+
raise DispatchError(str(exc)) from exc
|
|
692
690
|
|
|
693
691
|
|
|
694
692
|
def _analysis_input_lines(
|
|
@@ -814,53 +812,6 @@ def _implementation_executor_tail(
|
|
|
814
812
|
return preflight_path.read_text(encoding="utf-8")
|
|
815
813
|
|
|
816
814
|
|
|
817
|
-
def _errors_log_path(
|
|
818
|
-
project_root: Path,
|
|
819
|
-
manifest: Mapping[str, Any],
|
|
820
|
-
active_context: Mapping[str, Any],
|
|
821
|
-
) -> Path:
|
|
822
|
-
error_logs = active_context.get("errorLogs")
|
|
823
|
-
if isinstance(error_logs, Mapping):
|
|
824
|
-
value = _string_value(error_logs.get("runErrorsLogPath"))
|
|
825
|
-
if value:
|
|
826
|
-
return _resolve_project_path(project_root, value)
|
|
827
|
-
run_dir = _run_directory_path(project_root, manifest)
|
|
828
|
-
return run_dir / "logs" / f"errors-{_require_string(manifest, 'taskType')}-{_sequence(manifest, 'state')}.jsonl"
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
def _worker_errors_sidecar_path(
|
|
832
|
-
project_root: Path,
|
|
833
|
-
manifest: Mapping[str, Any],
|
|
834
|
-
active_context: Mapping[str, Any],
|
|
835
|
-
worker_id: str,
|
|
836
|
-
) -> Path:
|
|
837
|
-
error_logs = active_context.get("errorLogs")
|
|
838
|
-
if isinstance(error_logs, Mapping):
|
|
839
|
-
sidecars = error_logs.get("sidecarsByWorkerId")
|
|
840
|
-
if isinstance(sidecars, Mapping):
|
|
841
|
-
value = _string_value(sidecars.get(worker_id))
|
|
842
|
-
if value:
|
|
843
|
-
return _resolve_project_path(project_root, value)
|
|
844
|
-
run_dir = _run_directory_path(project_root, manifest)
|
|
845
|
-
task_type = _require_string(manifest, "taskType")
|
|
846
|
-
seq = _sequence(manifest, "workerResults")
|
|
847
|
-
return run_dir / "worker-results" / f"{worker_id}-worker-errors-{task_type}-{seq}.json"
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
def _run_directory_path(project_root: Path, manifest: Mapping[str, Any]) -> Path:
|
|
851
|
-
value = _string_value(manifest.get("runDirectoryPath"))
|
|
852
|
-
if value:
|
|
853
|
-
return _resolve_project_path(project_root, value)
|
|
854
|
-
return _resolve_required_path(project_root, manifest, "teamStatePath", "team-state").parent.parent
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
def _sequence(manifest: Mapping[str, Any], key: str) -> str:
|
|
858
|
-
seqs = manifest.get("runSequencesByCategory")
|
|
859
|
-
if not isinstance(seqs, Mapping):
|
|
860
|
-
return _run_seq(manifest)
|
|
861
|
-
return _string_value(seqs.get(key)) or _run_seq(manifest)
|
|
862
|
-
|
|
863
|
-
|
|
864
815
|
def _project_relative_path(project_root: Path, path: Path) -> str:
|
|
865
816
|
try:
|
|
866
817
|
return path.relative_to(project_root).as_posix()
|
|
@@ -23,15 +23,17 @@ INPUT_FILES = (
|
|
|
23
23
|
"reference-expectations.md",
|
|
24
24
|
"clarification-response.md",
|
|
25
25
|
)
|
|
26
|
-
# Per-run hot-path instruction assets the lead
|
|
27
|
-
#
|
|
26
|
+
# Per-run hot-path instruction assets the lead loads OUTSIDE the task bundle:
|
|
27
|
+
# the lifecycle lead contracts (Phase 1 / 2-5 / 5.5 / 6-7) plus the worker
|
|
28
28
|
# agent specs. These are the prompt-diet (perf plan v2 P2) targets; the
|
|
29
29
|
# bundle-local metrics below cannot see them, so they get their own metric.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"
|
|
34
|
-
"
|
|
30
|
+
# The contracts ship as runtime resources under ~/.okstra/prompts/lead/, not
|
|
31
|
+
# as agent skills.
|
|
32
|
+
HOT_PATH_LEAD_RESOURCES = (
|
|
33
|
+
"context-loader",
|
|
34
|
+
"team-contract",
|
|
35
|
+
"convergence",
|
|
36
|
+
"report-writer",
|
|
35
37
|
)
|
|
36
38
|
WORKER_AGENT_FILES = (
|
|
37
39
|
"claude-worker.md",
|
|
@@ -178,13 +180,14 @@ def _skill_assets_metric() -> dict:
|
|
|
178
180
|
skill bodies + worker agent specs. These dominate the fixed per-run
|
|
179
181
|
instruction footprint and are the prompt-diet ranking input."""
|
|
180
182
|
entries = []
|
|
183
|
+
okstra_home = Path.home() / ".okstra"
|
|
181
184
|
claude_home = Path.home() / ".claude"
|
|
182
|
-
for name in
|
|
185
|
+
for name in HOT_PATH_LEAD_RESOURCES:
|
|
183
186
|
path = _installed_or_dev(
|
|
184
|
-
|
|
185
|
-
f"
|
|
187
|
+
okstra_home / "prompts" / "lead" / f"{name}.md",
|
|
188
|
+
f"prompts/lead/{name}.md",
|
|
186
189
|
)
|
|
187
|
-
entries.append((f"
|
|
190
|
+
entries.append((f"resource:{name}", path))
|
|
188
191
|
for fname in WORKER_AGENT_FILES:
|
|
189
192
|
path = _installed_or_dev(
|
|
190
193
|
claude_home / "agents" / fname,
|
|
@@ -12,6 +12,7 @@ from typing import Any, Mapping, Sequence
|
|
|
12
12
|
|
|
13
13
|
from . import tmux
|
|
14
14
|
from .lead_events import LeadEvent, append_lead_event
|
|
15
|
+
from .worker_prompt_headers import WorkerPromptHeaderError, worker_prompt_headers
|
|
15
16
|
from .wrapper_status import read_wrapper_status, status_path_for_prompt
|
|
16
17
|
|
|
17
18
|
|
|
@@ -290,9 +291,19 @@ def _job_from_roster_worker(
|
|
|
290
291
|
state = _worker_state(team_state, worker_id)
|
|
291
292
|
provider = _provider_for_worker(worker_id)
|
|
292
293
|
model = _model_for_worker(worker_id, state, report_writer_model)
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
294
|
+
prompt_rel = _worker_prompt_path(manifest, worker_id)
|
|
295
|
+
result_rel = _require_string(state, "resultPath")
|
|
296
|
+
prompt_path = _resolve_project_path(project_root, prompt_rel)
|
|
297
|
+
result_path = _resolve_project_path(project_root, result_rel)
|
|
298
|
+
_materialize_prompt_if_missing(
|
|
299
|
+
prompt_path=prompt_path,
|
|
300
|
+
prompt_rel=prompt_rel,
|
|
301
|
+
worker_id=worker_id,
|
|
302
|
+
result_rel=result_rel,
|
|
303
|
+
project_root=project_root,
|
|
304
|
+
manifest=manifest,
|
|
305
|
+
active_context=active_context,
|
|
306
|
+
)
|
|
296
307
|
return WorkerJob(
|
|
297
308
|
worker_id=worker_id,
|
|
298
309
|
provider=provider,
|
|
@@ -747,19 +758,31 @@ def _resolve_wrapper(provider: str, workspace_root: Path, options: _BuildOptions
|
|
|
747
758
|
raise DispatchError(f"{script} not found (searched: {', '.join(str(c) for c in candidates)})")
|
|
748
759
|
|
|
749
760
|
|
|
750
|
-
def _materialize_prompt_if_missing(
|
|
761
|
+
def _materialize_prompt_if_missing(
|
|
762
|
+
*,
|
|
763
|
+
prompt_path: Path,
|
|
764
|
+
prompt_rel: str,
|
|
765
|
+
worker_id: str,
|
|
766
|
+
result_rel: str,
|
|
767
|
+
project_root: Path,
|
|
768
|
+
manifest: Mapping[str, Any],
|
|
769
|
+
active_context: Mapping[str, Any],
|
|
770
|
+
) -> None:
|
|
751
771
|
if prompt_path.is_file():
|
|
752
772
|
return
|
|
753
773
|
prompt_path.parent.mkdir(parents=True, exist_ok=True)
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
774
|
+
try:
|
|
775
|
+
headers = worker_prompt_headers(
|
|
776
|
+
project_root=project_root,
|
|
777
|
+
prompt_rel=prompt_rel,
|
|
778
|
+
result_rel=result_rel,
|
|
779
|
+
worker_id=worker_id,
|
|
780
|
+
manifest=manifest,
|
|
781
|
+
active_context=active_context,
|
|
782
|
+
)
|
|
783
|
+
except WorkerPromptHeaderError as exc:
|
|
784
|
+
raise DispatchError(str(exc)) from exc
|
|
785
|
+
prompt_path.write_text("\n".join([*headers, ""]), encoding="utf-8")
|
|
763
786
|
|
|
764
787
|
|
|
765
788
|
def _provider_for_worker(worker_id: str) -> str:
|
|
@@ -33,9 +33,15 @@ __all__ = [
|
|
|
33
33
|
"next_run_seq",
|
|
34
34
|
"task_dir",
|
|
35
35
|
"task_runs_dir",
|
|
36
|
+
"okstra_home",
|
|
36
37
|
]
|
|
37
38
|
|
|
38
39
|
|
|
40
|
+
def okstra_home() -> Path:
|
|
41
|
+
raw = os.environ.get("OKSTRA_HOME", "").strip()
|
|
42
|
+
return Path(raw).expanduser() if raw else Path.home() / ".okstra"
|
|
43
|
+
|
|
44
|
+
|
|
39
45
|
def task_dir(project_root: Path, task_group: str, task_id: str) -> Path:
|
|
40
46
|
"""task root 경로: ``<project>/.okstra/tasks/<group-seg>/<id-seg>``.
|
|
41
47
|
|
|
@@ -188,6 +194,8 @@ def compute_run_paths(
|
|
|
188
194
|
report_writer_worker_result = worker_results / f"report-writer-worker{suffixes['worker_results']}.md"
|
|
189
195
|
|
|
190
196
|
run_errors_log = run_logs / f"errors-{task_type_segment}-{seqs['state']}.jsonl"
|
|
197
|
+
runtime_home = okstra_home()
|
|
198
|
+
|
|
191
199
|
claude_worker_errors_sidecar = worker_results / f"claude-worker-errors{suffixes['worker_results']}.json"
|
|
192
200
|
codex_worker_errors_sidecar = worker_results / f"codex-worker-errors{suffixes['worker_results']}.json"
|
|
193
201
|
gemini_worker_errors_sidecar = worker_results / f"gemini-worker-errors{suffixes['worker_results']}.json"
|
|
@@ -209,7 +217,25 @@ def compute_run_paths(
|
|
|
209
217
|
"OKSTRA_ROOT": str(okstra_root),
|
|
210
218
|
"OKSTRA_TASKS_ROOT": str(tasks_root),
|
|
211
219
|
"WORKER_PROMPT_PREAMBLE_PATH": str(
|
|
212
|
-
|
|
220
|
+
runtime_home / "templates" / "worker-prompt-preamble.md"
|
|
221
|
+
),
|
|
222
|
+
"OKSTRA_LEAD_CONTRACT_PATH": str(
|
|
223
|
+
runtime_home / "prompts" / "lead" / "okstra-lead-contract.md"
|
|
224
|
+
),
|
|
225
|
+
"OKSTRA_CONTEXT_LOADER_PATH": str(
|
|
226
|
+
runtime_home / "prompts" / "lead" / "context-loader.md"
|
|
227
|
+
),
|
|
228
|
+
"OKSTRA_TEAM_CONTRACT_PATH": str(
|
|
229
|
+
runtime_home / "prompts" / "lead" / "team-contract.md"
|
|
230
|
+
),
|
|
231
|
+
"OKSTRA_CONVERGENCE_PATH": str(
|
|
232
|
+
runtime_home / "prompts" / "lead" / "convergence.md"
|
|
233
|
+
),
|
|
234
|
+
"OKSTRA_REPORT_WRITER_PATH": str(
|
|
235
|
+
runtime_home / "prompts" / "lead" / "report-writer.md"
|
|
236
|
+
),
|
|
237
|
+
"OKSTRA_CODING_PREFLIGHT_DIR": str(
|
|
238
|
+
runtime_home / "prompts" / "coding-preflight"
|
|
213
239
|
),
|
|
214
240
|
"OKSTRA_DISCOVERY_DIR": str(discovery_dir),
|
|
215
241
|
"TASK_ROOT": str(task_root),
|
|
@@ -28,6 +28,7 @@ from okstra_project.dirs import OKSTRA_DIR_NAME, project_json_path
|
|
|
28
28
|
# render_task_manifest 가 동일한 리스트/딕셔너리를 로컬에 중복 정의했는데,
|
|
29
29
|
# 이는 silent drift 위험이 있어 SSOT import 로 통합한다.
|
|
30
30
|
from . import fix_cycles
|
|
31
|
+
from .paths import okstra_home
|
|
31
32
|
from .lead_runtime import lead_runtime_info
|
|
32
33
|
from .workflow import DEFAULT_NEXT_PHASE, PHASE_SEQUENCE
|
|
33
34
|
|
|
@@ -369,6 +370,14 @@ def _active_error_logs(ctx: dict) -> dict:
|
|
|
369
370
|
}
|
|
370
371
|
|
|
371
372
|
|
|
373
|
+
def _active_runtime_resources(ctx: dict) -> dict:
|
|
374
|
+
runtime_home = okstra_home()
|
|
375
|
+
return {
|
|
376
|
+
"codingPreflightDir": ctx.get("OKSTRA_CODING_PREFLIGHT_DIR")
|
|
377
|
+
or str(runtime_home / "prompts" / "coding-preflight"),
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
|
|
372
381
|
def _active_executor_worktree(ctx: dict) -> dict:
|
|
373
382
|
return {
|
|
374
383
|
"status": ctx.get("EXECUTOR_WORKTREE_STATUS", ""),
|
|
@@ -419,6 +428,7 @@ def render_active_run_context(active_context_path: str, ctx: dict) -> None:
|
|
|
419
428
|
"instructionSet": _active_instruction_set(ctx),
|
|
420
429
|
"workers": _active_workers(ctx),
|
|
421
430
|
"errorLogs": _active_error_logs(ctx),
|
|
431
|
+
"runtimeResources": _active_runtime_resources(ctx),
|
|
422
432
|
"executorWorktree": _active_executor_worktree(ctx),
|
|
423
433
|
"sourceArtifacts": _active_source_artifacts(ctx),
|
|
424
434
|
"lazyReadPlan": _active_lazy_read_plan(),
|
|
@@ -1104,7 +1114,7 @@ def render_task_manifest(manifest_path: str, ctx: dict) -> None:
|
|
|
1104
1114
|
def _build_convergence_block(ctx: dict) -> dict:
|
|
1105
1115
|
"""Resolve the `convergence` sub-tree written into task-manifest.json.
|
|
1106
1116
|
|
|
1107
|
-
Defaults follow `
|
|
1117
|
+
Defaults follow `prompts/lead/convergence.md`:
|
|
1108
1118
|
- `enabled` default True
|
|
1109
1119
|
- `maxRounds` default 1 for `requirements-discovery`, 2 otherwise
|
|
1110
1120
|
- `verificationMode` default "lightweight"
|
|
@@ -1667,6 +1677,35 @@ def inject_lead_prompt_computed_tokens(ctx: dict) -> None:
|
|
|
1667
1677
|
lead_agent_label = _lead_agent_label(ctx)
|
|
1668
1678
|
lead_model = ctx.get("LEAD_MODEL", "")
|
|
1669
1679
|
lead_model_execution = ctx.get("LEAD_MODEL_EXECUTION_VALUE", "")
|
|
1680
|
+
home_prompts = okstra_home() / "prompts"
|
|
1681
|
+
lead_contract_path = ctx.get("OKSTRA_LEAD_CONTRACT_PATH") or str(
|
|
1682
|
+
home_prompts / "lead" / "okstra-lead-contract.md"
|
|
1683
|
+
)
|
|
1684
|
+
context_loader_path = ctx.get("OKSTRA_CONTEXT_LOADER_PATH") or str(
|
|
1685
|
+
home_prompts / "lead" / "context-loader.md"
|
|
1686
|
+
)
|
|
1687
|
+
team_contract_path = ctx.get("OKSTRA_TEAM_CONTRACT_PATH") or str(
|
|
1688
|
+
home_prompts / "lead" / "team-contract.md"
|
|
1689
|
+
)
|
|
1690
|
+
convergence_path = ctx.get("OKSTRA_CONVERGENCE_PATH") or str(
|
|
1691
|
+
home_prompts / "lead" / "convergence.md"
|
|
1692
|
+
)
|
|
1693
|
+
report_writer_path = ctx.get("OKSTRA_REPORT_WRITER_PATH") or str(
|
|
1694
|
+
home_prompts / "lead" / "report-writer.md"
|
|
1695
|
+
)
|
|
1696
|
+
coding_preflight_dir = ctx.get("OKSTRA_CODING_PREFLIGHT_DIR") or str(
|
|
1697
|
+
home_prompts / "coding-preflight"
|
|
1698
|
+
)
|
|
1699
|
+
okstra_runtime_resources_block = (
|
|
1700
|
+
"## Okstra Runtime Resources\n"
|
|
1701
|
+
"\n"
|
|
1702
|
+
f"- Lead contract: `{lead_contract_path}`\n"
|
|
1703
|
+
f"- Context loader: `{context_loader_path}`\n"
|
|
1704
|
+
f"- Team contract: `{team_contract_path}`\n"
|
|
1705
|
+
f"- Convergence contract: `{convergence_path}`\n"
|
|
1706
|
+
f"- Report writer contract: `{report_writer_path}`\n"
|
|
1707
|
+
f"- Coding preflight pack: `{coding_preflight_dir}`"
|
|
1708
|
+
)
|
|
1670
1709
|
|
|
1671
1710
|
def fmt_assignment(role: str, model: str, execution: str) -> str:
|
|
1672
1711
|
if execution and execution != model:
|
|
@@ -1852,7 +1891,7 @@ def inject_lead_prompt_computed_tokens(ctx: dict) -> None:
|
|
|
1852
1891
|
"## Team Creation Gate (BLOCKING)\n"
|
|
1853
1892
|
"\n"
|
|
1854
1893
|
"Before any `Agent` dispatch for workers, you MUST perform Phase 3 of the\n"
|
|
1855
|
-
'
|
|
1894
|
+
f'okstra lead contract (`{lead_contract_path}` \u2192 "Phase 3 \u2014 Team creation"). Skipping\n'
|
|
1856
1895
|
"this gate silently degrades the run to in-process background dispatch and\n"
|
|
1857
1896
|
"loses the Teams split-pane observability surface, even though worker\n"
|
|
1858
1897
|
"outputs may still appear correct on disk.\n"
|
|
@@ -1861,7 +1900,7 @@ def inject_lead_prompt_computed_tokens(ctx: dict) -> None:
|
|
|
1861
1900
|
"for this run (roster comes from `resultContract.requiredWorkerRoles` in\n"
|
|
1862
1901
|
"`task-manifest.json` — it may be 1, 2, 3, or more workers):\n"
|
|
1863
1902
|
"\n"
|
|
1864
|
-
"1.
|
|
1903
|
+
f"1. Read the team-contract resource (`{team_contract_path}`) and verify the selected worker\n"
|
|
1865
1904
|
" roster against `task-manifest.json`'s `resultContract.requiredWorkerRoles`.\n"
|
|
1866
1905
|
f'2. Call `TeamCreate(team_name: "{team_name}", description: ...)`.\n'
|
|
1867
1906
|
"3. Record the outcome in team-state under\n"
|
|
@@ -1943,8 +1982,10 @@ def inject_lead_prompt_computed_tokens(ctx: dict) -> None:
|
|
|
1943
1982
|
else:
|
|
1944
1983
|
lead_intro_line = f"You are `{lead_role}` for project `{ctx.get('PROJECT_ID', '')}`."
|
|
1945
1984
|
lead_bootstrap_instruction = (
|
|
1946
|
-
"
|
|
1947
|
-
"metadata, paths, model assignments, and
|
|
1985
|
+
f"Read the Okstra lead contract at `{lead_contract_path}` now, then read "
|
|
1986
|
+
"the manifests below for all task metadata, paths, model assignments, and "
|
|
1987
|
+
"worker roster. Follow the contract's lazy-read rules for support resources; "
|
|
1988
|
+
"do not invoke hidden/internal skills."
|
|
1948
1989
|
)
|
|
1949
1990
|
lead_session_block = (
|
|
1950
1991
|
"## Session\n"
|
|
@@ -1957,9 +1998,9 @@ def inject_lead_prompt_computed_tokens(ctx: dict) -> None:
|
|
|
1957
1998
|
"\n"
|
|
1958
1999
|
"- The `runs/<phase>/prompts/<worker>-worker-prompt-*.md` files referenced in `task-manifest.json → artifacts.workerPromptPathByWorkerId` are **NOT** rendered by the okstra runtime. Those paths are the *assigned* locations where the prompt history MUST be written at dispatch time.\n"
|
|
1959
2000
|
"- Therefore: at the start of every phase the prompts/ directory is normally empty (or contains only previously-dispatched workers' files). This is expected. Do NOT narrate it as \"missing\", \"누락\", or \"not yet rendered\" — it just means dispatch has not happened yet.\n"
|
|
1960
|
-
"- Before dispatching any required worker, **you (the lead) construct the worker prompt and persist it to the assigned absolute path using `Write`** (per `
|
|
2001
|
+
"- Before dispatching any required worker, **you (the lead) construct the worker prompt and persist it to the assigned absolute path using `Write`** (per `team-contract` rule 6). Only after persisting do you call the worker subagent (Agent tool / Codex / Gemini wrapper). The wrapper subagents will also re-write the same file on their end; the double-write is intentional and idempotent.\n"
|
|
1961
2002
|
"- Do not \"check if the file exists and skip dispatch\" — file presence is not a signal to skip. Worker selection and skipping rules come from team-state, never from prompts/ directory contents.\n"
|
|
1962
|
-
f"- **Worker Preamble Path (single anchor — replaces inlined `[Required reading]` and `[Error reporting]` blocks).** Every worker prompt you construct MUST include the anchor header `**Worker Preamble Path:** {ctx.get('WORKER_PROMPT_PREAMBLE_PATH', str(
|
|
2003
|
+
f"- **Worker Preamble Path (single anchor — replaces inlined `[Required reading]` and `[Error reporting]` blocks).** Every worker prompt you construct MUST include the anchor header `**Worker Preamble Path:** {ctx.get('WORKER_PROMPT_PREAMBLE_PATH', str(okstra_home() / 'templates' / 'worker-prompt-preamble.md'))}`. The file at that path is the canonical SSOT for Required Reading + Error Reporting + Output sections; workers Read it end-to-end before producing output. Do NOT re-inline those blocks into worker prompt bodies — that is the legacy ~80-line dispatch boilerplate that this anchor is designed to replace."
|
|
1963
2004
|
)
|
|
1964
2005
|
|
|
1965
2006
|
# Compute results (deterministic from ctx, 덮어쓰기)
|
|
@@ -1968,6 +2009,7 @@ def inject_lead_prompt_computed_tokens(ctx: dict) -> None:
|
|
|
1968
2009
|
ctx["LEAD_SESSION_BLOCK"] = lead_session_block
|
|
1969
2010
|
ctx["TEAM_CREATION_GATE"] = team_creation_gate_block
|
|
1970
2011
|
ctx["WORKER_DISPATCH_GUIDANCE"] = worker_dispatch_guidance
|
|
2012
|
+
ctx["OKSTRA_RUNTIME_RESOURCES"] = okstra_runtime_resources_block
|
|
1971
2013
|
ctx["WORKER_RESULT_PATH_LINES"] = "\n".join(worker_result_lines)
|
|
1972
2014
|
ctx["MODEL_ASSIGNMENT_LINES"] = "\n".join(model_assignment_lines)
|
|
1973
2015
|
ctx["TEAM_ROLE_LINES"] = "\n".join(team_role_lines)
|
|
@@ -2001,9 +2043,21 @@ def apply_lead_prompt_defaults(ctx: dict) -> None:
|
|
|
2001
2043
|
# Empty for non-final-verification runs; the final-verification prepare
|
|
2002
2044
|
# path overwrites it with the resolved verification target block.
|
|
2003
2045
|
ctx.setdefault("VERIFICATION_TARGET", "")
|
|
2046
|
+
runtime_home = okstra_home()
|
|
2004
2047
|
ctx.setdefault(
|
|
2005
2048
|
"WORKER_PROMPT_PREAMBLE_PATH",
|
|
2006
|
-
str(
|
|
2049
|
+
str(runtime_home / "templates" / "worker-prompt-preamble.md"),
|
|
2050
|
+
)
|
|
2051
|
+
# Lead resource paths the launch template references directly. paths.py
|
|
2052
|
+
# seeds the production values; setdefault backfills callers that render the
|
|
2053
|
+
# template without the full path ctx (mirrors WORKER_PROMPT_PREAMBLE_PATH).
|
|
2054
|
+
ctx.setdefault(
|
|
2055
|
+
"OKSTRA_LEAD_CONTRACT_PATH",
|
|
2056
|
+
str(runtime_home / "prompts" / "lead" / "okstra-lead-contract.md"),
|
|
2057
|
+
)
|
|
2058
|
+
ctx.setdefault(
|
|
2059
|
+
"OKSTRA_TEAM_CONTRACT_PATH",
|
|
2060
|
+
str(runtime_home / "prompts" / "lead" / "team-contract.md"),
|
|
2007
2061
|
)
|
|
2008
2062
|
if "AVAILABLE_MCP_SERVERS" not in ctx:
|
|
2009
2063
|
ctx["AVAILABLE_MCP_SERVERS"] = build_available_mcp_servers_block(
|
|
@@ -1067,7 +1067,7 @@ class _ResolvedAssets:
|
|
|
1067
1067
|
prompt_template: Path
|
|
1068
1068
|
task_index_template: Path
|
|
1069
1069
|
final_report_template: Path
|
|
1070
|
-
|
|
1070
|
+
lead_contract: Path
|
|
1071
1071
|
run_validator: Path
|
|
1072
1072
|
|
|
1073
1073
|
|
|
@@ -1090,19 +1090,19 @@ def _resolve_runtime_assets(workspace_root: Path, inp: PrepareInputs) -> _Resolv
|
|
|
1090
1090
|
final_report_template = (
|
|
1091
1091
|
workspace_root / "templates" / "reports" / "final-report.template.md"
|
|
1092
1092
|
)
|
|
1093
|
-
|
|
1093
|
+
lead_contract = workspace_root / "prompts" / "lead" / "okstra-lead-contract.md"
|
|
1094
1094
|
run_validator = workspace_root / "validators" / "validate-run.py"
|
|
1095
|
-
for required in (task_index_template, final_report_template, run_validator,
|
|
1095
|
+
for required in (task_index_template, final_report_template, run_validator, lead_contract):
|
|
1096
1096
|
if not required.is_file():
|
|
1097
1097
|
raise PrepareError(
|
|
1098
|
-
f"required okstra template or
|
|
1098
|
+
f"required okstra template or lead contract missing: {required}.{_INSTALL_HINT}"
|
|
1099
1099
|
)
|
|
1100
1100
|
return _ResolvedAssets(
|
|
1101
1101
|
profile_file=profile_file,
|
|
1102
1102
|
prompt_template=prompt_template,
|
|
1103
1103
|
task_index_template=task_index_template,
|
|
1104
1104
|
final_report_template=final_report_template,
|
|
1105
|
-
|
|
1105
|
+
lead_contract=lead_contract,
|
|
1106
1106
|
run_validator=run_validator,
|
|
1107
1107
|
)
|
|
1108
1108
|
|