okstra 0.98.1 → 0.99.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/docs/for-ai/README.md +55 -0
- package/docs/for-ai/skills/okstra-brief.md +240 -0
- package/docs/for-ai/skills/okstra-container.md +161 -0
- package/docs/for-ai/skills/okstra-inspect.md +260 -0
- package/docs/for-ai/skills/okstra-memory.md +127 -0
- package/docs/for-ai/skills/okstra-run.md +229 -0
- package/docs/for-ai/skills/okstra-schedule.md +321 -0
- package/docs/for-ai/skills/okstra-setup.md +140 -0
- package/docs/kr/architecture.md +5 -5
- package/docs/superpowers/plans/2026-06-23-phase-batch-cleanup-enforcement.md +409 -0
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/claude-worker.md +1 -1
- package/runtime/bin/okstra-team-reconcile.sh +7 -7
- package/runtime/prompts/coding-preflight/overview.md +1 -1
- package/runtime/prompts/lead/context-loader.md +1 -1
- package/runtime/prompts/lead/convergence.md +3 -4
- package/runtime/prompts/lead/okstra-lead-contract.md +26 -24
- package/runtime/prompts/lead/report-writer.md +6 -7
- package/runtime/prompts/lead/team-contract.md +4 -4
- package/runtime/prompts/profiles/_common-contract.md +16 -16
- package/runtime/prompts/profiles/forbidden-actions.json +1 -1
- package/runtime/prompts/profiles/release-handoff.md +2 -2
- package/runtime/python/okstra_ctl/paths.py +1 -5
- package/runtime/python/okstra_ctl/render.py +38 -49
- package/runtime/python/okstra_ctl/run.py +1 -1
- package/runtime/python/okstra_ctl/session.py +1 -1
- package/runtime/python/okstra_ctl/team_reconcile.py +10 -12
- package/runtime/python/okstra_ctl/worktree.py +2 -3
- package/runtime/python/okstra_ctl/worktree_registry.py +3 -3
- package/runtime/python/okstra_project/dirs.py +31 -4
- package/runtime/python/okstra_token_usage/collect.py +7 -5
- package/runtime/skills/okstra-brief/SKILL.md +24 -13
- package/runtime/skills/okstra-run/SKILL.md +3 -3
- package/runtime/validators/lib/fixtures.sh +8 -7
- package/runtime/validators/validate-brief.py +36 -2
- package/runtime/validators/validate-run.py +18 -15
- package/runtime/validators/validate_session_conformance.py +45 -6
- package/src/commands/lifecycle/config.mjs +2 -4
- package/src/commands/memory/memory.mjs +2 -3
- package/src/lib/paths.mjs +28 -2
|
@@ -43,13 +43,13 @@ This document is the operating contract and phase index. Detailed procedures liv
|
|
|
43
43
|
| 1.5 Graph hint | Build shared knowledge graph (optional) | this contract — see "Optional shared knowledge graph" below |
|
|
44
44
|
| 2. Prompts | Prepare shared + role-specific prompts | `team-contract` |
|
|
45
45
|
| 2.5 Team contract | Load operating rules for selected roster | `team-contract` |
|
|
46
|
-
| 3.
|
|
47
|
-
| 4. Execution | Spawn analysis workers (
|
|
48
|
-
| 5.
|
|
46
|
+
| 3. Teammate setup | Record team-state for the session's implicit team; gate split-pane on tmux | this contract — see "Phase 3" below |
|
|
47
|
+
| 4. Execution | Spawn analysis workers (`Agent(name: ..., run_in_background: true)`) | `team-contract` |
|
|
48
|
+
| 5. Completion poll | Self-scheduled poll for worker result files | `team-contract` |
|
|
49
49
|
| 5.5 Convergence | Cross-verify findings across workers | `convergence` |
|
|
50
50
|
| 5.6 Critic pass | (opt-in) reused-worker critic pass: coverage gaps (discovery/error-analysis/impl-planning) or acceptance devil's-advocate (final-verification). The critic dispatch fires concurrently with the first 5.5 reverify round (its input is fixed at Round 0); gap/blocker verification (one round) completes here | `convergence` "Coverage critic pass" / "Acceptance critic pass" |
|
|
51
51
|
| 6. Synthesis | Dispatch Report writer worker, review draft. **For `implementation-planning`: then run the Phase 6 plan-body verification sub-step (see Phase 6 section below).** | `report-writer` + `convergence` (sub-step) |
|
|
52
|
-
| 7. Persist | Run token-usage collector, update manifests, ask about residual tmux panes, then ask whether to
|
|
52
|
+
| 7. Persist | Run token-usage collector, update manifests, ask about residual tmux panes, then ask whether to dismiss the worker teammates (reconcile stale members + `SendMessage` shutdown_request only on approval) | `report-writer` + `_common-contract.md` "Phase wrap-up" / "Run-end teammate teardown" |
|
|
53
53
|
|
|
54
54
|
## Core operating contract
|
|
55
55
|
|
|
@@ -94,22 +94,23 @@ Required checkpoints:
|
|
|
94
94
|
- `PROGRESS: phase-1-intake reading task bundle` — at the start of Phase 1, before issuing parallel Read calls.
|
|
95
95
|
- `PROGRESS: phase-1-intake complete` — after all intake reads return.
|
|
96
96
|
- `PROGRESS: phase-2-prompts preparing <N> worker prompts` — at the start of Phase 2, before any `Write` to the assigned prompt paths.
|
|
97
|
-
- `PROGRESS: phase-3-team-create
|
|
97
|
+
- `PROGRESS: phase-3-team-create using implicit team (split-pane=<on|off>)` — emitted in Phase 3 after recording `teamCreate` in team-state (`on` when `$TMUX` is set, else `off`). When the launch prompt's "Concurrent-run marker" gate applies, emit `PROGRESS: phase-3-team-create skipped (concurrent-run)` instead, immediately after recording `teamCreate: { attempted: false, status: "skipped", reason: "concurrent-run" }` in team-state. The `phase-3-team-create` phase-id is required on every run; only the verb phrase varies.
|
|
98
98
|
- `PROGRESS: phase-4-dispatch worker=<role> model=<model>` — once per worker, immediately before the `Agent` / wrapper call.
|
|
99
99
|
- `PROGRESS: phase-5-poll pending=<n> done=<m>` — emitted on each wakeup while the pending set is non-empty.
|
|
100
100
|
- `PROGRESS: phase-5-collect worker=<role> status=<terminal-status>` — once per worker, immediately after the result file is verified.
|
|
101
101
|
- `PROGRESS: phase-5.5-convergence round=<N> queue=<count>` — at the start of each convergence round (Phase 5.5).
|
|
102
102
|
- `PROGRESS: phase-5.6-critic provider=<provider> gaps=<n>` — after the critic result is collected (Phase 5.6, opt-in; the critic dispatch itself fires concurrently with the first 5.5 reverify round). Omitted when `convergence.critic.enabled == false`.
|
|
103
|
+
- `PROGRESS: phase-batch-cleanup panes=<n> teammates=<m>` — 이전 배치 pane·완료 teammate 를 정리한 직후, 각 배치 경계에서(① 첫 `phase-5.5-convergence` round 직전 ② `phase-6-synthesis` report-writer dispatch 직전). 카운트만 노출하고 `%NNN`/lead-pane.id/raw Agent name 은 절대 노출하지 않는다. 첫 배치(분석 워커 dispatch) 직전엔 치울 게 없어 no-op 이며 마커도 생략.
|
|
103
104
|
- `PROGRESS: phase-6-synthesis dispatching report-writer-worker` — at the start of Phase 6.
|
|
104
105
|
- `PROGRESS: phase-7-persist updating manifests` — at the start of Phase 7.
|
|
105
|
-
- `PROGRESS: phase-7-teardown
|
|
106
|
+
- `PROGRESS: phase-7-teardown dismissing teammates` — after token-usage collection and after the pane-disposition prompt, only when the user approved worker teammate cleanup, immediately before reconciling stale members + dismissing them via `SendMessage` shutdown_request (Teams mode only; see `_common-contract.md` "Run-end teammate teardown"). Skipped when split-pane is off (no roster) or when the user keeps the teammates.
|
|
106
107
|
- `PROGRESS: complete final-report=<relative-path>` — final summary line, after all persistence.
|
|
107
108
|
|
|
108
109
|
These lines are the only structured signal the user has during a long run. Do NOT replace them with prose ("Now I'm starting Phase 2..."), do NOT skip a checkpoint because "the previous message already said that", and do NOT batch multiple checkpoints into one. Each line stands alone so the user (or any operator scraping stdout) can timestamp it externally.
|
|
109
110
|
|
|
110
111
|
`okstra-run` (in-session) surfaces these lines to the user directly; the bash-spawned path leaves them in the session jsonl for post-hoc retrieval. Neither path requires any additional formatting from Lead — emit the literal `PROGRESS:` prefix and the rest of the line as plain text.
|
|
111
112
|
|
|
112
|
-
**Enforcement:** the Phase 7 validator (`validators/validate-run.py` → `validate_session_conformance.py`) scans the lead session jsonl (run-window-scoped, assistant text blocks only) and fails the run as `contract-violated` when a required checkpoint is missing — including the per-worker `phase-4-dispatch` / `phase-5-collect` lines
|
|
113
|
+
**Enforcement:** the Phase 7 validator (`validators/validate-run.py` → `validate_session_conformance.py`) scans the lead session jsonl (run-window-scoped, assistant text blocks only) and fails the run as `contract-violated` when a required checkpoint is missing — including the per-worker `phase-4-dispatch` / `phase-5-collect` lines (which must name each worker's role) and the `phase-batch-cleanup` lines that MUST precede the first `phase-5.5-convergence` round and the `phase-6-synthesis` report-writer dispatch. `phase-7-teardown` and `complete` fire after validation and are not checked.
|
|
113
114
|
|
|
114
115
|
## Model assignments
|
|
115
116
|
|
|
@@ -200,7 +201,7 @@ Extract from the compact intake files: task key, task type, work category, workf
|
|
|
200
201
|
|
|
201
202
|
If previous run reports exist, use as historical context only. If discovery metadata or current artifacts conflict with a newer user instruction, prefer the user instruction. If `reference-expectations.md` explicitly says expectations were not provided (you can confirm this without reading the file if the brief's "Expected state" section is empty), treat that as missing information and say `I don't know` rather than inventing expected states.
|
|
202
203
|
|
|
203
|
-
## Phase 2 — Phase 5: Prompt preparation,
|
|
204
|
+
## Phase 2 — Phase 5: Prompt preparation, teammate setup, execution, completion poll
|
|
204
205
|
|
|
205
206
|
These phases are governed by [team-contract](./team-contract.md). It is the canonical source for:
|
|
206
207
|
|
|
@@ -212,34 +213,35 @@ These phases are governed by [team-contract](./team-contract.md). It is the cano
|
|
|
212
213
|
|
|
213
214
|
`Report writer worker` is NOT an analysis worker. Do not dispatch it in Phase 4/5 alongside analysis workers. It is invoked only in Phase 6 — see [report-writer](./report-writer.md).
|
|
214
215
|
|
|
215
|
-
### Phase 3 —
|
|
216
|
+
### Phase 3 — Teammate setup (BLOCKING)
|
|
216
217
|
|
|
217
|
-
`TeamCreate`
|
|
218
|
+
Claude Code **v2.1.178 removed the `TeamCreate` / `TeamDelete` tools and the `Agent(...)` `team_name` parameter**. With `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` (seeded by `okstra install` into the user's `settings.json`), every session already owns **one implicit team** from the moment it starts — there is no creation step to perform and no `team_name` to pass on dispatch. Workers spawned with `Agent(name: ...)` auto-join that implicit team. Do NOT search for, probe, or call `TeamCreate`: its absence is the expected state on every current Claude Code, NOT an "Agent Teams unavailable" signal, and is never a reason to degrade the run. There is no `select:TeamCreate` probe and no doubly-empty-select branch anymore — those keyed off a tool that no longer exists.
|
|
218
219
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
2
|
|
223
|
-
3.
|
|
224
|
-
|
|
225
|
-
|
|
220
|
+
1. Record team-state before any dispatch:
|
|
221
|
+
- `teamName` — the verbatim name from the launch prompt's gate block (`okstra-<task-key>`; implementation stage runs append `-s<N>`, single-stage final-verification appends `-fv-s<N>`). Audit/display label only — the implicit team's on-disk directory is `session-<leadSessionId-prefix>`, which run-end teardown and reconcile resolve from `lead.sessionId`, NEVER from `teamName`.
|
|
222
|
+
- `teamCreate: { attempted: false, status: "implicit" }` — the audit marker that this run rides the session's implicit team (no explicit creation tool exists). A concurrent-run launch prompt instead pre-records `status: "skipped", reason: "concurrent-run"`; honor whichever the gate specifies.
|
|
223
|
+
2. Verify `team-state.lead.sessionId` is populated. The `okstra.sh` exec path fills it automatically (`generate_claude_session_id` → `claude --session-id ...`). The render-only / in-session takeover path (`okstra-run` skill) auto-detects via `resolve_inproc_lead_session_id`, which is best-effort and may return empty if `~/.claude/projects/<encoded-cwd>/` is unreadable or has no jsonl yet. If `lead.sessionId` is empty at this point, write the running session's id into team-state before proceeding — Phase 7 token-usage collection depends on it and will fail with `lead jsonl not found (sessionId=)` otherwise.
|
|
224
|
+
3. Decide split-pane capability, record it, then emit the phase-3 checkpoint and proceed to Phase 4. Split-pane teammates require a tmux session — this is a host capability, NOT an okstra success/failure gate, and is the only thing the removed "TeamCreate" probe should ever have been deciding:
|
|
225
|
+
- `$TMUX` set → workers render as split-pane teammates under `teammateMode: auto`. Record `teamCreate.splitPane: true`.
|
|
226
|
+
- `$TMUX` unset → teammates run in-process (no split-pane possible — a host limitation, not a degraded run). Record `teamCreate.splitPane: false`.
|
|
227
|
+
Dispatch is identical either way: there is no "Agent Teams unavailable → background" branch. Only when the launch prompt carries the `Concurrent-run marker` or `Tmux Worker Dispatch Gate` block do the dedicated branches below apply.
|
|
226
228
|
|
|
227
229
|
Use agent and subagent names that map cleanly to the selected worker roles. Do not create ambiguous role names that differ from `Claude worker`, `Codex worker`, `Antigravity worker`, or `Report writer worker`.
|
|
228
230
|
|
|
229
|
-
If the launch prompt contains `Tmux Worker Dispatch Gate`, Phase 3 is recorded as skipped with `reason: "tmux-pane"` and Phase 4 dispatch is performed only through `okstra team dispatch` followed by `okstra team await`. In this branch, calling `
|
|
231
|
+
If the launch prompt contains `Tmux Worker Dispatch Gate`, Phase 3 is recorded as skipped with `reason: "tmux-pane"` and Phase 4 dispatch is performed only through `okstra team dispatch` followed by `okstra team await`. In this branch, calling `Agent(...)` is a contract violation; worker completion is read from `workerDispatches[]` plus result files.
|
|
230
232
|
|
|
231
233
|
|
|
232
234
|
### Phase 4 / Phase 5 — Execution and error-log dump
|
|
233
235
|
|
|
234
|
-
Spawn **analysis workers only** in the same turn (
|
|
236
|
+
Spawn **analysis workers only** in the same turn with `Agent(name: ..., run_in_background: true)` and **no `team_name`** — the parameter was removed in CC v2.1.178 and teammates auto-join the session's implicit team. `run_in_background: true` keeps the dispatch async so the lead can run the self-scheduled completion poll. Preserve exact roster, role labels, assigned models from the task bundle.
|
|
235
237
|
|
|
236
238
|
**Agent `name` on dispatch (BLOCKING — token-usage attribution depends on it).** Every analysis-worker `Agent(...)` call MUST set `name: "<workerId>-worker"` — `name: "claude-worker"` / `name: "codex-worker"` / `name: "antigravity-worker"` — exactly as the report-writer dispatch sets `name: "report-writer"` ([report-writer](./report-writer.md)). The Agent harness records this `name` as `agentName` in the subagent session jsonl, and the Phase 7 token collector matches each worker's session by that `agentName` (`okstra_token_usage/collect.py`). A worker dispatched **without** `name` produces a session with no `agentName`; the collector cannot attribute it and records the worker as `source: "unavailable"` even though the session exists and is team-tagged. Convergence reverify dispatches keep the prefix (`<workerId>-worker-reverify-r<N>`). For `implementation` and `final-verification` dispatches the `name` carries the **functional role** so the FleetView teammate pill and trace panes name the actual job instead of a generic `worker`: the Executor dispatch sets `name: "<workerId>-executor"` (e.g. `codex-executor`) and every verifier dispatch sets `name: "<workerId>-verifier"` (e.g. `claude-verifier`). These role suffixes still attribute correctly — the collector matches any `agentName` beginning `<workerId>-` (`okstra_token_usage/collect.py` `match_prefixes`). **For every CLI (Codex / Antigravity) dispatch, Lead MUST inject a `**Pane role:**` line into the dispatched prompt body whose value is the Agent `name` with the leading `<cli>-` prefix removed** — the wrapper subagent reads it and passes it as the wrapper's 5th `<role>` argument so the tmux trace pane title reads `<cli>-<role>` (which equals the FleetView teammate name) instead of a generic `<cli>-worker` (see `agents/workers/_cli-wrapper-template.md`). So `name: "codex-worker"` → `**Pane role:** worker`, `name: "codex-worker-reverify-r1"` → `**Pane role:** worker-reverify-r1`, `name: "codex-executor"` → `**Pane role:** executor`, `name: "codex-verifier"` → `**Pane role:** verifier`. The wrapper defaults to `worker` when the line is absent, but always inject it so the pane names the actual job.
|
|
237
239
|
|
|
238
240
|
**Agent `model:` on dispatch (BLOCKING — assignment is otherwise ignored).** The `Claude worker` `Agent(...)` call MUST set `model: "<family token of that role's modelExecutionValue>"` (`fable` / `opus` / `sonnet` / `haiku`), per [team-contract](./team-contract.md) "Model Assignment Rules" #3–#4. The claude-worker definition is `model: inherit`, so omitting this parameter makes the worker silently run on the lead's model instead of its manifest assignment — the assigned-vs-actual deviation. `Codex worker` / `Antigravity worker` are exempt: their CLI model is applied via the wrapper's own `--model` argument, so leave their Agent `model:` at `inherit` (rule #5).
|
|
239
241
|
|
|
240
|
-
|
|
242
|
+
There is no separate "Teams mode" vs "no-`team_name` fallback" split anymore: every analysis-worker dispatch is `Agent(name: ..., run_in_background: true)` with no `team_name`, and teammates join the session's implicit team automatically. `teamCreate` is still recorded in team-state before any dispatch (`status: "implicit"`, or `"skipped"` / `reason: "concurrent-run"` when the launch prompt's concurrent-run gate applies) — it is now an audit marker, not a branch selector.
|
|
241
243
|
|
|
242
|
-
**Completion detection after dispatch (BLOCKING).** The `Agent(
|
|
244
|
+
**Completion detection after dispatch (BLOCKING).** The `Agent(name: ..., run_in_background: true)` call returns `Spawned successfully` immediately; that ack is NOT completion. After dispatching the analysis workers (async), Lead MUST detect their completion via the self-scheduled polling protocol in [team-contract](./team-contract.md) "Worker-completion detection (self-scheduled polling)" — do NOT restate the algorithm here. Lead MUST NOT end its turn with a prose "waiting for workers" statement; that path stalls the run until the user manually nudges it.
|
|
243
245
|
|
|
244
246
|
### Errors log path wiring (BLOCKING)
|
|
245
247
|
|
|
@@ -375,8 +377,8 @@ After persistence, reply briefly in the resolved Report Language with: completio
|
|
|
375
377
|
|
|
376
378
|
| Mistake | Fix |
|
|
377
379
|
|---------|-----|
|
|
378
|
-
|
|
|
379
|
-
|
|
|
380
|
+
| Probing `select:TeamCreate` and declaring "Agent Teams unavailable" when it comes back empty | `TeamCreate` was removed in CC v2.1.178 — it is empty on every current build. Do not probe it; the session's implicit team is always present with `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1`. Dispatch workers directly with `Agent(name: ...)` |
|
|
381
|
+
| Passing `team_name` on an `Agent(...)` dispatch | The parameter was removed in CC v2.1.178. Dispatch with `name` only — teammates auto-join the implicit team |
|
|
380
382
|
| Substituting Claude lead reasoning for a worker result | Claude lead synthesizes only — spawn the worker |
|
|
381
383
|
| Skipping a worker silently | Always record terminal status with reason |
|
|
382
384
|
| Writing verdict before all workers report | Wait for all results or explicit terminal statuses |
|
|
@@ -393,7 +395,7 @@ After persistence, reply briefly in the resolved Report Language with: completio
|
|
|
393
395
|
| Letting `convergence.maxRounds` default to 2 for `requirements-discovery` | Resolve effective default to `1` for discovery and record in convergence state artifact |
|
|
394
396
|
| Issuing serial Read calls in Phase 1 | The intake files are independent — issue all Read calls in a single message (parallel) |
|
|
395
397
|
| Flagging the claude-worker dispatch prompt as "incomplete" because it lacks `[Required reading]` / `[Error reporting]` blocks | Intentional asymmetry — see [team-contract](./team-contract.md) "Asymmetry between claude-worker and codex/antigravity-worker prompts" |
|
|
396
|
-
| Waiting silently while the dispatched `claude-worker` Agent call returns nothing for many minutes | The claude-worker MUST append a `- PROGRESS: <stage> <ISO-UTC>` line to its audit sidecar (`runs/<task-type>/worker-results/claude-worker-audit-<task-type>-<seq>.md`) at least every 5 minutes (see `agents/workers/claude-worker.md` "Heartbeat" rule). If the sidecar is absent or its mtime is >5 minutes stale, treat the dispatch as `timeout` and redispatch once with a byte-identical prompt; after a second silent hang, record terminal status `timeout` with the missing-sidecar reason in team-state. The authoritative completion signal is the **result file's appearance**, detected via self-scheduled polling (see [team-contract](./team-contract.md) "Worker-completion detection (self-scheduled polling)") — NOT the Agent-call return, which under `
|
|
398
|
+
| Waiting silently while the dispatched `claude-worker` Agent call returns nothing for many minutes | The claude-worker MUST append a `- PROGRESS: <stage> <ISO-UTC>` line to its audit sidecar (`runs/<task-type>/worker-results/claude-worker-audit-<task-type>-<seq>.md`) at least every 5 minutes (see `agents/workers/claude-worker.md` "Heartbeat" rule). If the sidecar is absent or its mtime is >5 minutes stale, treat the dispatch as `timeout` and redispatch once with a byte-identical prompt; after a second silent hang, record terminal status `timeout` with the missing-sidecar reason in team-state. The authoritative completion signal is the **result file's appearance**, detected via self-scheduled polling (see [team-contract](./team-contract.md) "Worker-completion detection (self-scheduled polling)") — NOT the Agent-call return, which under `run_in_background` dispatch is just an immediate `Spawned successfully` ack. The heartbeat sidecar is an auxiliary liveness signal layered on top: a missing sidecar after the result file appears is itself a contract violation per the heartbeat rule |
|
|
397
399
|
| Re-sending confirmed findings (`full-consensus`/`partial-consensus`/`worker-unique`) to a worker in Round 2 | Queue pruning rule — see [convergence](./convergence.md) "Round 1-N: Re-verification Loop (queue-pruned)" |
|
|
398
400
|
| Aggregating a `timeout`/`error` reverify dispatch as `DISAGREE` | Worker failure handling — record as `verification-error` and add to `skippedWorkers[]`. See [convergence](./convergence.md) "Worker failure handling in reverify" |
|
|
399
401
|
| Skipping `--substitute-data` in the Phase 7 collector run | Always pass the flag — see [report-writer](./report-writer.md) "Phase 7 token-usage collector" |
|
|
@@ -28,7 +28,7 @@ Agent(
|
|
|
28
28
|
prompt: "<report-writer prompt: see this contract + Required reading clause + Available MCP Servers section>",
|
|
29
29
|
name: "report-writer",
|
|
30
30
|
subagent_type: "report-writer-worker",
|
|
31
|
-
|
|
31
|
+
run_in_background: true, # no team_name — removed in CC v2.1.178; joins the session's implicit team
|
|
32
32
|
model: "<family token of Report writer worker's modelExecutionValue>", # opus/sonnet/haiku — NOT hardcoded; see below
|
|
33
33
|
mode: "auto"
|
|
34
34
|
)
|
|
@@ -56,15 +56,14 @@ The prompt MUST include, in this order at the top:
|
|
|
56
56
|
11. For implementation-planning runs: a literal block listing the 8 required English section headings the validator scans for (`Option Candidates`, `Trade-off`, `Recommended Option`, `Stepwise Execution Order`, `Dependency`, `Validation Checklist`, `Rollback`, `User Approval Request`). The writer must use these exact substrings as section headings (Korean translation in parentheses is allowed).
|
|
57
57
|
12. An explicit instruction: `You are the author of TWO files: (a) the final-report data.json at <Result Path>, (b) the worker-results audit file at <Worker Result Path>. After writing the data.json, invoke "okstra render-final-report <Result Path>" via Bash so the markdown sibling is rendered before you return. Do not return the report inline. The validator fails the run when (a)'s schema validation fails, when the rendered markdown is absent, or when (b) is missing.`
|
|
58
58
|
|
|
59
|
-
**Completion detection after dispatch (BLOCKING).** The `Agent(
|
|
59
|
+
**Completion detection after dispatch (BLOCKING).** The `Agent(name: ..., run_in_background: true)` call returns `Spawned successfully` immediately; that ack is NOT completion. After dispatching the report-writer (async), Lead MUST detect its completion via the self-scheduled polling protocol in [team-contract](./team-contract.md) "Worker-completion detection (self-scheduled polling)", polling for the appearance of the data.json (Result Path) and the worker-results file (Worker Result Path) — do NOT restate the algorithm here. Report-writer is a single worker, so the pending set has one entry; the SSOT protocol handles that naturally. Lead MUST NOT treat the `Spawned successfully` ack as completion and MUST NOT end its turn with a prose "waiting for the report" statement; that path stalls the run until the user manually nudges it.
|
|
60
60
|
|
|
61
61
|
### Resume-safe dispatch
|
|
62
62
|
|
|
63
|
-
A resumed lead session can ALWAYS dispatch a fresh Report writer worker.
|
|
63
|
+
A resumed lead session can ALWAYS dispatch a fresh Report writer worker. There is no team to revive — CC v2.1.178 removed `TeamCreate`, so every session already has its implicit team:
|
|
64
64
|
|
|
65
|
-
-
|
|
66
|
-
-
|
|
67
|
-
- Do NOT skip dispatch because of any team-related error. Record the team status in team-state and proceed without `team_name`.
|
|
65
|
+
- Dispatch with `Agent(name: "report-writer", run_in_background: true)` and no `team_name` (the parameter was removed). The worker joins the session's implicit team and its session is recoverable by `agentName: "report-writer"` in `okstra-token-usage.py`.
|
|
66
|
+
- Do NOT skip dispatch because of any team-related concern. Record `teamCreate` in team-state and proceed.
|
|
68
67
|
|
|
69
68
|
### Lead-authored fallback (only if dispatch failed)
|
|
70
69
|
|
|
@@ -131,7 +130,7 @@ The four steps below MUST execute in this exact order. Reordering them is the re
|
|
|
131
130
|
|
|
132
131
|
The status file is written after step 3 completes.
|
|
133
132
|
|
|
134
|
-
**Run-end teammate teardown follows this whole sequence.** Token-usage collection (step 1) reads the worker session jsonls, so the lead MUST NOT disband the team until every step above is done. Only then — after the pane-disposition prompt and only when the user approves teammate cleanup — does the lead
|
|
133
|
+
**Run-end teammate teardown follows this whole sequence.** Token-usage collection (step 1) reads the worker session jsonls, so the lead MUST NOT disband the team until every step above is done. Only then — after the pane-disposition prompt and only when the user approves teammate cleanup — does the lead dismiss the worker teammates via `SendMessage` shutdown_request per `_common-contract.md` "Run-end teammate teardown" (split-pane runs only; silent-skip when split-pane is off; if the user keeps the teammates, leave them intact and surface the manual Teams/FleetView cleanup path).
|
|
135
134
|
|
|
136
135
|
## Final Report Structure
|
|
137
136
|
|
|
@@ -48,7 +48,7 @@ Only workers selected from `recommendedWorkers` in `task-manifest.json` and `res
|
|
|
48
48
|
|
|
49
49
|
## Operating Rules
|
|
50
50
|
|
|
51
|
-
0. **
|
|
51
|
+
0. **Teammate dispatch (BLOCKING).** Claude Code v2.1.178 removed the `TeamCreate` / `TeamDelete` tools and the `Agent(...)` `team_name` parameter. With `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` (seeded by `okstra install`) the session owns one implicit team from start; Lead dispatches every worker with `Agent(name: "<role>-worker", run_in_background: true)` and **NO `team_name`**, and teammates auto-join that implicit team. Do NOT search for, probe, or call `TeamCreate` — its absence is the normal state on every current build, never an "Agent Teams unavailable" signal, and never a reason to degrade the run. Before any dispatch, record the `teamName` audit label plus `teamCreate: { attempted: false, status: "implicit", splitPane: <true if $TMUX set, else false> }` (or the `status: "skipped", reason: "concurrent-run"` variant when the launch prompt's concurrent-run gate applies). Split-pane teammates appear when `$TMUX` is set (`teammateMode: auto`); without tmux they run in-process — both are normal. See [okstra-lead-contract.md Phase 3](./okstra-lead-contract.md) for the full sequence.
|
|
52
52
|
1. `Claude lead` is responsible for orchestration, convergence supervision, and final-report review/approval. It never overrides worker analysis results, and it never authors the final-report file when `Report writer worker` is in the roster.
|
|
53
53
|
2. `Report writer worker` is NOT an analysis worker. It is excluded from Phase 4/5 (initial analysis) and Phase 5.5 (convergence re-verification). It is spawned only in Phase 6 and is the **author** of the final-report file at `runs/<task-type>/reports/final-report-<task-type>-<seq>.md`.
|
|
54
54
|
3. When `Report writer worker` is in the roster, Lead MUST dispatch it in Phase 6. The only legal lead-authored fallback is when a dispatch was attempted and recorded a terminal status of `error` / `timeout` / `not-run` with a concrete logged reason. Speculative reasons such as "session resume constraint" or "team is no longer alive" are NOT valid — Lead can always dispatch a fresh subagent (omit `team_name` if the team is gone).
|
|
@@ -118,7 +118,7 @@ Terminal statuses that can be recorded for a worker:
|
|
|
118
118
|
|
|
119
119
|
**SSOT.** This section is the single source of truth for how Lead detects worker completion across all phases and all worker kinds (Claude teammate, Codex / Antigravity wrappers). Other documents (`prompts/lead/okstra-lead-contract.md`, `report-writer`, `convergence`) reference this section by name; they MUST NOT restate the algorithm.
|
|
120
120
|
|
|
121
|
-
Lead dispatches workers asynchronously: an `Agent` call
|
|
121
|
+
Lead dispatches workers asynchronously: an `Agent` call with `run_in_background: true` returns `Spawned successfully` **immediately** — that ack is NOT a completion. Lead MUST NOT treat the spawn ack as completion, and MUST NOT end its turn with a prose "waiting for ..." statement (that path stalls the run — the Agent Teams idle-notification is experimental and can be dropped, leaving Lead parked until the user manually nudges it). Instead:
|
|
122
122
|
|
|
123
123
|
For `tmux-pane` backend runs, Lead MUST use exactly one background wait command instead of raw result-file polling:
|
|
124
124
|
|
|
@@ -175,7 +175,7 @@ After each worker subagent returns (regardless of role), Lead MUST verify the ca
|
|
|
175
175
|
|
|
176
176
|
**One-retry policy:**
|
|
177
177
|
|
|
178
|
-
1. On the FIRST result-missing trigger for a given role within a single run, Lead MUST re-dispatch the same worker with the byte-identical prompt — same `**Result Path:**`, same `**Prompt History Path:**`, same model assignment, same `
|
|
178
|
+
1. On the FIRST result-missing trigger for a given role within a single run, Lead MUST re-dispatch the same worker with the byte-identical prompt — same `**Result Path:**`, same `**Prompt History Path:**`, same model assignment, same Agent `name`. The redispatch counts as a second attempt against the existing role slot; do NOT create a new role-id, do NOT change the result file path, do NOT switch to a different model as a "workaround".
|
|
179
179
|
2. If the SECOND attempt also fails the same check, Lead records the role's terminal status as `error` with `--message "result-missing after 1 retry"` and proceeds to Phase 5.5 / Phase 6 with the remaining workers' results. Lead MUST NOT retry a third time — convergence and the report writer are designed to operate on reduced-confidence single-or-two-analyser mode when one role is absent (`prompts/lead/okstra-lead-contract.md` "If only one worker result is usable: reduced-confidence synthesis").
|
|
180
180
|
3. The retry counter is **per-run, per-role** and is NOT preserved across runs. A subsequent okstra run for the same task-key starts each role's counter fresh.
|
|
181
181
|
4. Convergence reverify rounds (Phase 5.5) inherit the same one-retry budget — a reverify dispatch that triggers result-missing may be re-dispatched once.
|
|
@@ -455,7 +455,7 @@ The script reads:
|
|
|
455
455
|
## Team State Persistence
|
|
456
456
|
|
|
457
457
|
Information to be recorded in the team-state JSON file:
|
|
458
|
-
- `teamName` — record the
|
|
458
|
+
- `teamName` — record the audit label set in Phase 3 (`okstra-<task-key>` + stage suffix). Either `state.teamName` (root) or `state.team.teamName` (nested) is accepted by `scripts/okstra_token_usage/collect.py`. Be consistent within a single run. Without this value the Phase 7 collector falls back to `okstra-<task-id>` (short form), which does NOT match worker jsonls whose team needle carries the full multi-segment task key — every worker will then be recorded as `source: "unavailable"`.
|
|
459
459
|
- Current status of each worker role
|
|
460
460
|
- Start/end times for each worker
|
|
461
461
|
- Prompt history path for each worker
|
|
@@ -36,11 +36,11 @@ profile document.
|
|
|
36
36
|
- This recording is **silent internal setup**: the lead MUST emit NOTHING to the user about it — no pane id, no `%NNN`, no `lead-pane.id` path, no announcement of the phase-start-cleanup / wrap-up-disposition steps. Just record the file and proceed.
|
|
37
37
|
- Phase-start cleanup — prior-batch panes + completed teammates (shared — runs BEFORE dispatching each new worker batch, at the SAME boundaries: just before each `PROGRESS: phase-5.5-convergence round=<N>` marker and just before `PROGRESS: phase-6-synthesis dispatching report-writer-worker`):
|
|
38
38
|
- **(a) tmux panes.** okstra creates two kinds of tmux pane per run: **worker-agent panes** the harness gives to dispatched subagents (titled `claude-worker` / `codex-worker` / `antigravity-worker` / `report-writer-worker`), and **trace panes** the codex/antigravity wrappers spawn (`<cli>-<role>-<pid>-tail`). Both accumulate because each new batch spawns fresh panes and the prior ones are never reclaimed. The lead MUST ALWAYS run `$HOME/.okstra/bin/okstra-trace-cleanup.sh --run-dir "<RUN_DIR>"` immediately before dispatching the next batch — NEVER gating this call on its own reading of `lead-pane.id`. The script self-resolves the lead pane and is a safe no-op outside tmux (it returns an empty pane list), so an always-run call cannot do harm; it closes every prior-batch okstra pane (worker-agent + trace) for this run while NEVER killing the lead's own pane. The lead MUST NOT fabricate a synthetic pane list.
|
|
39
|
-
- **(b) completed teammates (
|
|
39
|
+
- **(b) completed teammates (split-pane runs only).** Each convergence round / critic / gap-verification / report-writer dispatch spawns teammates under fresh Agent `name`s (`<role>-reverify-r<N>`, `<provider>-worker-critic`, `report-writer-worker`, …); completed teammates do NOT leave the FleetView roster on their own — they linger as idle pills until run-end teardown, so a long run's roster grows unreadable. Before dispatching the next batch, the lead MUST send `SendMessage(to: <name>, message: { type: "shutdown_request" })` to every teammate it spawned earlier in THIS run **whose completion is already confirmed** (its Result Path exists / it is in the self-scheduled-polling done set). The `message` MUST be the object literal shown, NEVER a JSON string stuffed into a text field. This is the same graceful-shutdown primitive used at `Run-end teammate teardown`, applied per batch instead of once at the end.
|
|
40
40
|
- **Only confirmed-complete teammates, never the lead.** NEVER send a shutdown_request to a teammate whose result file has not yet appeared. In particular the critic runs CONCURRENTLY with the first reverify round (see `convergence` "Coverage critic pass"), so it MUST NOT be shut down until its own result is collected. NEVER target the lead's own session.
|
|
41
|
-
-
|
|
42
|
-
- Silent-skip
|
|
43
|
-
- Both (a) and (b) run **automatically without any interactive prompt**.
|
|
41
|
+
- Only individual completed teammates are dismissed here; the rest of the roster stays for the remaining batches. There is no `TeamDelete` tool (removed in CC v2.1.178) — the implicit team itself ends with the session; teardown only dismisses teammates.
|
|
42
|
+
- Silent-skip in `tmux-pane` backend runs (external-lead workers are not session teammates); otherwise the on-disk existence check at run-end teardown is the authority on whether a roster exists.
|
|
43
|
+
- Both (a) and (b) run **automatically without any interactive prompt**. After running them, emit the checkpoint line `PROGRESS: phase-batch-cleanup panes=<n> teammates=<m>` (counts only — NEVER expose `%NNN`, `lead-pane.id`, or raw Agent names) and proceed; this is the BLOCKING checkpoint the Phase 7 validator requires before each convergence round and before the report-writer dispatch (`prompts/lead/okstra-lead-contract.md` "Progress reporting (BLOCKING)"). **Effect caveat:** Claude Code exposes no tool to surgically delete a roster entry mid-run, so a dismissed teammate may still show as an idle pill; the shutdown still reclaims its session and prevents the roster from growing with live members. On the FIRST batch of a run nothing has been dispatched yet — both steps no-op.
|
|
44
44
|
- Phase wrap-up — okstra pane disposition (shared, runs AFTER Phase 7 persistence/token collection and BEFORE teammate teardown):
|
|
45
45
|
- At run end the only residual okstra panes are the LAST phase's (e.g. the `report-writer-worker` agent pane and any codex/antigravity trace pane). `okstra-trace-cleanup.sh --list --run-dir "<RUN_DIR>"` returns one tab-separated `<pane_id>\t<pane_title>` line per residual okstra pane (worker-agent + trace) for this run.
|
|
46
46
|
- After the final-report file has been written and the routing recommendation has been issued, the lead MUST ALWAYS run `$HOME/.okstra/bin/okstra-trace-cleanup.sh --list --run-dir "<RUN_DIR>"` exactly once — NEVER gating this call on its own reading of `lead-pane.id`. The output lists every residual okstra pane (worker-agent + trace) for this run, never the lead's own pane; outside tmux it is a safe no-op (empty output).
|
|
@@ -54,19 +54,19 @@ profile document.
|
|
|
54
54
|
- The question MUST be a clean yes/no — do NOT offer "close some / keep some" partial answers, do NOT propose alternatives like "close only codex panes". The whole-set decision keeps the wrap-up predictable.
|
|
55
55
|
- This step is mandatory for every phase (`requirements-discovery`, `error-analysis`, `implementation-planning`, `implementation`, `final-verification`, `release-handoff`). Whether there is anything to ask about is decided by the `--list` output, NOT by the lead's reading of `lead-pane.id`: an empty list → skip the question (above); the lead MUST NOT fabricate a synthetic pane list.
|
|
56
56
|
- Run-end teammate teardown (shared — MUST run AFTER the pane disposition question and BEFORE `PROGRESS: complete`):
|
|
57
|
-
-
|
|
58
|
-
- **Whether to run this step is decided by on-disk fact
|
|
59
|
-
- **Matching on-disk config found, with members beyond the lead** →
|
|
60
|
-
- **No matching on-disk config
|
|
61
|
-
- After the pane disposition step above, the lead MUST ask the user whether to
|
|
62
|
-
> worker teammate
|
|
57
|
+
- CC v2.1.178 removed `TeamCreate` / `TeamDelete`: this run rode the session's **implicit team** (Phase 3 recorded `teamCreate.status: "implicit"` plus the `teamName` audit label). There is no tool to delete a team — the implicit team ends when the session ends. What teardown CAN do is gracefully dismiss the worker teammates the lead spawned, so they stop showing as live members in the FleetView roster.
|
|
58
|
+
- **Whether to run this step is decided by on-disk fact.** The harness names the team directory `session-<leadSessionId-prefix>` (the `teamName` label never becomes a directory name). Resolve existence from disk: find the `~/.claude/teams/session-*/config.json` whose `leadSessionId` equals team-state `lead.sessionId`.
|
|
59
|
+
- **Matching on-disk config found, with members beyond the lead** → teammates exist; run the dismissal sequence below.
|
|
60
|
+
- **No matching on-disk config, or split-pane was off** → nothing to dismiss → silent-skip.
|
|
61
|
+
- After the pane disposition step above, the lead MUST ask the user whether to dismiss the worker teammates. This is a strict binary choice:
|
|
62
|
+
> worker teammate (`<teamName>`) 들을 정리할까요?
|
|
63
63
|
> (예) 팀원 정리 / (아니오) 그대로 두기
|
|
64
|
-
- On `아니오` / `n` / `keep` →
|
|
65
|
-
- On `예` / `y` / `cleanup` → emit `PROGRESS: phase-7-teardown
|
|
66
|
-
1. Run `$HOME/.okstra/bin/okstra-team-reconcile.sh "<resolved-config-path>"` — the on-disk `~/.claude/teams/session-<…>/config.json` resolved in the existence check above
|
|
67
|
-
2.
|
|
68
|
-
-
|
|
69
|
-
- Report
|
|
64
|
+
- On `아니오` / `n` / `keep` → dismiss nothing. Tell the user the teammates remain visible in FleetView/Teams until the session ends, and can be removed earlier from the Teams UI.
|
|
65
|
+
- On `예` / `y` / `cleanup` → emit `PROGRESS: phase-7-teardown dismissing teammates`, then run the sequence below. Token-usage collection MUST already be complete — it reads `~/.claude/projects/` jsonls, which dismissal never touches, but the read MUST precede teardown.
|
|
66
|
+
1. Run `$HOME/.okstra/bin/okstra-team-reconcile.sh "<resolved-config-path>"` — the on-disk `~/.claude/teams/session-<…>/config.json` resolved in the existence check above — exactly once. It flips dead-pane stale-active members to inactive, and no-ops when tmux is unavailable or nothing is stale. Do NOT loop it.
|
|
67
|
+
2. Send each confirmed-complete member a structured `SendMessage(to: <name>, message: { type: "shutdown_request" })` — the `message` MUST be the object literal shown, NEVER a JSON string stuffed into a text field. NEVER target the lead's own session, and never use `TaskStop` (teammates are not background tasks — `TaskStop` 404s on a member address).
|
|
68
|
+
- Claude Code exposes no tool to delete the team or surgically remove a roster entry, so a dismissed teammate may still show as an idle pill until the session ends. Do not pretend the team was deleted. If the user wants the roster gone entirely, surface the manual action: `Teams/FleetView 에서 team <teamName> 삭제`, and if tmux panes were kept earlier also show `$HOME/.okstra/bin/okstra-trace-cleanup.sh --run-dir "<RUN_DIR>"`.
|
|
69
|
+
- Report the outcome in one short line (e.g. `worker teammate 정리 완료`, or `stale 멤버 1명 reconcile 후 정리`) and proceed to the final `PROGRESS: complete ...` line.
|
|
70
70
|
- Brief handoff contract (shared — applies whenever the run consumes a task brief produced by `okstra-brief`):
|
|
71
71
|
- the brief is a **pre-discovery artifact**: it converts a domain-reporter's words (non-expert *or* developer) into expert-consumable form so this and later phases can run with zero fill-in questions to the operator. The brief is **not** authoritative on solution decisions; it is authoritative on the reporter's intent.
|
|
72
72
|
- **Reporter confirmation precondition (BLOCKING)**: the brief's frontmatter carries `reporter-confirmations: <complete | partial | pending | skipped>` set by `okstra-brief` Step 6.5. Every phase that consumes the brief MUST read this field before doing analysis. The handling matrix is:
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"source-code edits, refactors, or any modification to files outside the run's own artifact directories (`reports/`, `prompts/`, `state/`, `manifests/`, `worker-results/`, `status/`, `sessions/`). The diff being shipped MUST be exactly what the prior `implementation` run produced; release-handoff packages it, it does not re-author it.",
|
|
60
60
|
"executing any mutating command the user did NOT select. Examples: opening a PR when the user picked `local checkout`; pushing when the user picked `skip`; switching the PR base branch silently after the user already chose one.",
|
|
61
61
|
"retrying a failed git / gh command with weaker safety flags. If `git push` fails with non-fast-forward, the lead MUST stop, explain the failure to the user, and ask for instructions — it MUST NOT add `--force`.",
|
|
62
|
-
"`
|
|
62
|
+
"`Agent(...)` worker dispatch of any kind, or any other parallel sub-agent fan-out. This phase runs entirely under the Claude lead.",
|
|
63
63
|
"silently treating an unrecognised user reply as one of the menu options. If the user's answer does not match a presented choice, re-ask the question verbatim."
|
|
64
64
|
],
|
|
65
65
|
"unknown": [
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Release Handoff Profile
|
|
2
2
|
|
|
3
3
|
- Purpose: take an `accepted` final-verification verdict for an already-committed implementation branch and turn it into a delivered push and/or pull request, with explicit user selection at every mutating step. Two modes: **whole-task** (default — the verified task branch becomes one PR) and **stage-group** (a user-selected subset of verified stages is merged into a collector branch and becomes one PR).
|
|
4
|
-
- **Execution model: single-lead, no worker dispatch.** This phase is a thin orchestrator over `git` / `gh`; it does NOT
|
|
4
|
+
- **Execution model: single-lead, no worker dispatch.** This phase is a thin orchestrator over `git` / `gh`; it does NOT dispatch teammates, does NOT dispatch analysis or drafter sub-agents, and does NOT run convergence. The Claude lead performs every step inline (drafting PR text, asking the user, running git / gh, writing the final report) — see "Lead-only contract" below.
|
|
5
5
|
- Worker roster: none — this profile intentionally has no `- Required workers:` block; the run is executed entirely by the Claude lead.
|
|
6
6
|
- Lead-only contract (replaces the shared team contract for this phase):
|
|
7
|
-
- The Claude lead is the sole agent for this run. No `Agent(...)` worker dispatch, no
|
|
7
|
+
- The Claude lead is the sole agent for this run. No `Agent(...)` worker dispatch, no teammates, no parallel sub-agents, no convergence loop.
|
|
8
8
|
- The lead drafts the PR title and PR body **inline** by reading the run brief, the cited final-verification report, `git log --oneline <base>..HEAD`, and `git diff <base>..HEAD --stat`. No drafter worker is dispatched.
|
|
9
9
|
- The lead authors the final-report file directly (no `Report writer worker` dispatch). The report still conforms to the standard `templates/reports/final-report.template.md` structure, including the `## 5.6 Release Handoff Deliverables` section.
|
|
10
10
|
- The shared anti-escalation rule from the common contract still applies: do not start any other lifecycle phase from inside this run.
|
|
@@ -22,6 +22,7 @@ from okstra_project.dirs import (
|
|
|
22
22
|
DISCOVERY_RELATIVE,
|
|
23
23
|
OKSTRA_RELATIVE,
|
|
24
24
|
TASKS_RELATIVE,
|
|
25
|
+
okstra_home,
|
|
25
26
|
)
|
|
26
27
|
from okstra_project.state import slugify
|
|
27
28
|
|
|
@@ -39,11 +40,6 @@ __all__ = [
|
|
|
39
40
|
]
|
|
40
41
|
|
|
41
42
|
|
|
42
|
-
def okstra_home() -> Path:
|
|
43
|
-
raw = os.environ.get("OKSTRA_HOME", "").strip()
|
|
44
|
-
return Path(raw).expanduser() if raw else Path.home() / ".okstra"
|
|
45
|
-
|
|
46
|
-
|
|
47
43
|
def task_dir(project_root: Path, task_group: str, task_id: str) -> Path:
|
|
48
44
|
"""task root 경로: ``<project>/.okstra/tasks/<group-seg>/<id-seg>``.
|
|
49
45
|
|
|
@@ -1691,9 +1691,9 @@ def build_available_mcp_servers_block(project_root: Path) -> str:
|
|
|
1691
1691
|
|
|
1692
1692
|
|
|
1693
1693
|
def sanitize_team_name(base: str, suffix: str = "") -> str:
|
|
1694
|
-
"""`base`(+`suffix`)를
|
|
1694
|
+
"""`base`(+`suffix`)를 teamName 라벨 / Agent name 패턴에 맞춘다.
|
|
1695
1695
|
|
|
1696
|
-
|
|
1696
|
+
Agent name·teamName 은 `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$` 를 따른다(콜론
|
|
1697
1697
|
불가·최대 64자). 그런데 okstra 의 base 는 `okstra-<TASK_KEY>` 이고 TASK_KEY 는
|
|
1698
1698
|
`project:group:task-id` 라 콜론을 포함하며 64자를 쉽게 넘는다. 불허문자를 '-' 로
|
|
1699
1699
|
치환하고, 초과 시 stage 격리용 `suffix`(`-s<N>` / `-fv-s<N>`)는 보존하면서 원본
|
|
@@ -1813,13 +1813,15 @@ def inject_lead_prompt_computed_tokens(ctx: dict) -> None:
|
|
|
1813
1813
|
else "- `Antigravity worker` is not selected for this run, so no Antigravity attempt is required."
|
|
1814
1814
|
)
|
|
1815
1815
|
|
|
1816
|
-
#
|
|
1816
|
+
# Teammate dispatch gate block: phase-conditional. CC v2.1.178 removed
|
|
1817
|
+
# TeamCreate/TeamDelete + the Agent team_name param; every session owns one
|
|
1818
|
+
# implicit team, so workers dispatch with Agent(name: ...) and no team_name.
|
|
1817
1819
|
# - release-handoff (and any other phase that renders with no workers
|
|
1818
|
-
# selected) is single-lead and
|
|
1820
|
+
# selected) is single-lead and dispatches no teammate at all. Emit a
|
|
1819
1821
|
# short notice instead of the BLOCKING gate.
|
|
1820
|
-
# - concurrent implementation runs
|
|
1821
|
-
# shared
|
|
1822
|
-
# - All other phases keep the full
|
|
1822
|
+
# - concurrent implementation runs only record an audit marker (each session
|
|
1823
|
+
# has its own implicit team, so there is no shared-team conflict).
|
|
1824
|
+
# - All other phases keep the full teammate-dispatch contract.
|
|
1823
1825
|
task_type = ctx.get("TASK_TYPE", "")
|
|
1824
1826
|
concurrent_stages = str(ctx.get("CONCURRENT_RUN_STAGES", "") or "").strip()
|
|
1825
1827
|
codex_dispatch_command = (
|
|
@@ -1899,28 +1901,26 @@ def inject_lead_prompt_computed_tokens(ctx: dict) -> None:
|
|
|
1899
1901
|
)
|
|
1900
1902
|
elif task_type == "implementation" and concurrent_stages:
|
|
1901
1903
|
team_creation_gate_block = (
|
|
1902
|
-
"## Concurrent-run
|
|
1904
|
+
"## Concurrent-run marker (BLOCKING)\n"
|
|
1903
1905
|
"\n"
|
|
1904
1906
|
"A concurrent implementation run of the same task-key holds stage(s)\n"
|
|
1905
|
-
f"`{concurrent_stages}` right now.
|
|
1906
|
-
"
|
|
1907
|
-
"
|
|
1907
|
+
f"`{concurrent_stages}` right now. Each session owns its own implicit\n"
|
|
1908
|
+
"team (`session-<leadSessionId-prefix>`), so concurrent same-task runs do\n"
|
|
1909
|
+
"not share team state — this run dispatches normally and only records the\n"
|
|
1910
|
+
"concurrent context for the audit trail.\n"
|
|
1908
1911
|
"\n"
|
|
1909
1912
|
"Required actions, in order:\n"
|
|
1910
1913
|
"\n"
|
|
1911
|
-
"1.
|
|
1912
|
-
" `Agent(... team_name: ...)`.\n"
|
|
1913
|
-
"2. Before any dispatch, record in team-state:\n"
|
|
1914
|
+
"1. Before any dispatch, record in team-state:\n"
|
|
1914
1915
|
' `teamCreate: { attempted: false, status: "skipped",'
|
|
1915
1916
|
f' reason: "concurrent-run", concurrentStages: [{concurrent_stages}] }}`.\n'
|
|
1916
|
-
"
|
|
1917
|
+
"2. Immediately after recording it, emit the checkpoint line\n"
|
|
1917
1918
|
" `PROGRESS: phase-3-team-create skipped (concurrent-run)` — the\n"
|
|
1918
|
-
" phase-3 checkpoint is
|
|
1919
|
-
"
|
|
1920
|
-
"
|
|
1921
|
-
" `team_name
|
|
1922
|
-
"
|
|
1923
|
-
" Teams split-pane view is lost."
|
|
1919
|
+
" phase-3 checkpoint is required and the session-conformance validator\n"
|
|
1920
|
+
" fails the run without it.\n"
|
|
1921
|
+
"3. Dispatch every worker with `Agent(name: ..., run_in_background: true)`\n"
|
|
1922
|
+
" and NO `team_name`; teammates join this session's implicit team and\n"
|
|
1923
|
+
" render as split-panes when `$TMUX` is set."
|
|
1924
1924
|
)
|
|
1925
1925
|
else:
|
|
1926
1926
|
base_name = f'okstra-{ctx.get("TASK_KEY", "")}'
|
|
@@ -1939,13 +1939,17 @@ def inject_lead_prompt_computed_tokens(ctx: dict) -> None:
|
|
|
1939
1939
|
stage_suffix = f"-fv-s{fv_stage}"
|
|
1940
1940
|
team_name = sanitize_team_name(base_name, stage_suffix)
|
|
1941
1941
|
team_creation_gate_block = (
|
|
1942
|
-
"##
|
|
1942
|
+
"## Teammate Dispatch Gate (BLOCKING)\n"
|
|
1943
1943
|
"\n"
|
|
1944
1944
|
"Before any `Agent` dispatch for workers, you MUST perform Phase 3 of the\n"
|
|
1945
|
-
f'okstra lead contract (`{lead_contract_path}`
|
|
1946
|
-
"
|
|
1947
|
-
"
|
|
1948
|
-
"
|
|
1945
|
+
f'okstra lead contract (`{lead_contract_path}` → "Phase 3 — Teammate setup").\n'
|
|
1946
|
+
"\n"
|
|
1947
|
+
"Claude Code v2.1.178 removed the `TeamCreate` / `TeamDelete` tools and the\n"
|
|
1948
|
+
"`Agent(...)` `team_name` parameter. With `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1`\n"
|
|
1949
|
+
"(seeded by `okstra install`) this session already owns one implicit team;\n"
|
|
1950
|
+
"workers spawned with `Agent(name: ...)` auto-join it. Do NOT probe or call\n"
|
|
1951
|
+
"`TeamCreate` — its absence is the normal state on every current Claude Code,\n"
|
|
1952
|
+
"not an 'Agent Teams unavailable' signal, and never a reason to degrade the run.\n"
|
|
1949
1953
|
"\n"
|
|
1950
1954
|
"Required actions, in order, regardless of how many workers are selected\n"
|
|
1951
1955
|
"for this run (roster comes from `resultContract.requiredWorkerRoles` in\n"
|
|
@@ -1953,29 +1957,14 @@ def inject_lead_prompt_computed_tokens(ctx: dict) -> None:
|
|
|
1953
1957
|
"\n"
|
|
1954
1958
|
f"1. Read the team-contract resource (`{team_contract_path}`) and verify the selected worker\n"
|
|
1955
1959
|
" roster against `task-manifest.json`'s `resultContract.requiredWorkerRoles`.\n"
|
|
1956
|
-
|
|
1957
|
-
"
|
|
1958
|
-
' `teamCreate: { attempted:
|
|
1959
|
-
"
|
|
1960
|
-
"
|
|
1961
|
-
"
|
|
1962
|
-
"
|
|
1963
|
-
|
|
1964
|
-
"\n"
|
|
1965
|
-
'If `TeamCreate` fails with "team already exists" (stale leftover from an\n'
|
|
1966
|
-
"earlier attempt): call `TeamList`; if the team is listed in this session,\n"
|
|
1967
|
-
"`TeamDelete` it and retry step 2 once. If it is NOT listed, do NOT remove\n"
|
|
1968
|
-
"`~/.claude/teams/...` / `~/.claude/tasks/...` on your own initiative —\n"
|
|
1969
|
-
"shell deletion of harness-internal state is destructive and `rm -rf` is\n"
|
|
1970
|
-
"commonly denied by user permission rules. Instead ask the user via\n"
|
|
1971
|
-
"AskUserQuestion (recommended option: quarantine), and on approval move\n"
|
|
1972
|
-
f"`~/.claude/teams/{team_name}` and `~/.claude/tasks/{team_name}` into\n"
|
|
1973
|
-
"`~/.okstra/trash/<UTC-timestamp>/` with `mv` (reversible, no delete),\n"
|
|
1974
|
-
"then retry step 2 once.\n"
|
|
1975
|
-
"\n"
|
|
1976
|
-
'If the Agent tool rejects a dispatch with `"team must be created first"` /\n'
|
|
1977
|
-
'`"team을 먼저 생성하거나 team_name 없이 호출해야 합니다"`, the correct\n'
|
|
1978
|
-
"response is to go back to step 2 — NOT to strip `team_name` and retry."
|
|
1960
|
+
"2. Record team-state BEFORE any dispatch:\n"
|
|
1961
|
+
f' `teamName: "{team_name}"` (audit/display label only) AND\n'
|
|
1962
|
+
' `teamCreate: { attempted: false, status: "implicit",'
|
|
1963
|
+
" splitPane: <true if $TMUX set, else false> }`.\n"
|
|
1964
|
+
'3. Dispatch every worker with `Agent(name: "<role>-worker",'
|
|
1965
|
+
" run_in_background: true)` and NO `team_name`. Split-pane teammates appear\n"
|
|
1966
|
+
" automatically when `$TMUX` is set (`teammateMode: auto`); when it is\n"
|
|
1967
|
+
" unset they run in-process — both are normal, not a degraded run."
|
|
1979
1968
|
)
|
|
1980
1969
|
|
|
1981
1970
|
if lead_runtime == "external":
|
|
@@ -1299,7 +1299,7 @@ def _register_and_check_project(project_root: Path, inp: PrepareInputs) -> None:
|
|
|
1299
1299
|
def _resolve_roster(inp: PrepareInputs, profile_file: Path) -> tuple[list[str], str]:
|
|
1300
1300
|
"""profile 의 `Required workers:` 블록을 권위로 roster 를 해소한다.
|
|
1301
1301
|
|
|
1302
|
-
release-handoff 은 의도적으로 single-lead (worker dispatch /
|
|
1302
|
+
release-handoff 은 의도적으로 single-lead (worker dispatch / teammate /
|
|
1303
1303
|
convergence 없음). profile 에 `- Required workers:` 블록이 없으므로 어떤
|
|
1304
1304
|
override 가 와도 빈 roster 를 강제해 profile 계약과 일관성을 유지한다."""
|
|
1305
1305
|
if inp.task_type == "release-handoff":
|
|
@@ -95,7 +95,7 @@ EOF
|
|
|
95
95
|
|
|
96
96
|
if compgen -G "$WORKER_PROMPTS_DIR/*-worker-prompt-$TASK_TYPE-$PROMPT_SEQ.md" > /dev/null 2>&1; then
|
|
97
97
|
cat >&2 <<EOF
|
|
98
|
-
Phase 3 부터 진행 —
|
|
98
|
+
Phase 3 부터 진행 — implicit team 으로 Phase 4 worker dispatch 까지.
|
|
99
99
|
Worker prompts (이미 작성·저장됨):
|
|
100
100
|
$WORKER_PROMPTS_DIR/*-worker-prompt-$TASK_TYPE-$PROMPT_SEQ.md
|
|
101
101
|
EOF
|