okstra 0.20.1 → 0.21.1
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 +2 -2
- package/README.md +2 -2
- package/docs/kr/architecture.md +1 -0
- package/docs/kr/cli.md +1 -1
- package/docs/kr/performance-improvement-plan-v2.md +330 -0
- package/docs/kr/performance-improvement-plan.md +125 -0
- package/docs/project-structure-overview.md +388 -0
- package/docs/superpowers/plans/2026-05-14-convergence-queue-pruning.md +1568 -0
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/SKILL.md +7 -1
- package/runtime/agents/workers/claude-worker.md +3 -1
- package/runtime/agents/workers/report-writer-worker.md +4 -0
- package/runtime/bin/okstra-codex-exec.sh +42 -0
- package/runtime/bin/okstra-gemini-exec.sh +7 -0
- package/runtime/bin/okstra-trace-cleanup.sh +42 -0
- package/runtime/prompts/profiles/final-verification.md +8 -2
- package/runtime/prompts/profiles/implementation-planning.md +1 -1
- package/runtime/prompts/profiles/release-handoff.md +26 -28
- package/runtime/prompts/profiles/requirements-discovery.md +1 -1
- package/runtime/python/okstra_ctl/render.py +78 -4
- package/runtime/python/okstra_ctl/run_context.py +5 -0
- package/runtime/python/okstra_ctl/workflow.py +8 -7
- package/runtime/python/okstra_ctl/worktree.py +155 -12
- package/runtime/skills/okstra-brief/SKILL.md +523 -0
- package/runtime/skills/okstra-convergence/SKILL.md +149 -37
- package/runtime/skills/okstra-report-writer/SKILL.md +8 -6
- package/runtime/templates/prd/brief.template.md +12 -0
- package/runtime/templates/project-docs/task-index.template.md +12 -0
- package/runtime/templates/reports/error-analysis-input.template.md +12 -0
- package/runtime/templates/reports/final-report.template.md +39 -12
- package/runtime/templates/reports/final-verification-input.template.md +22 -0
- package/runtime/templates/reports/implementation-input.template.md +12 -0
- package/runtime/templates/reports/implementation-planning-input.template.md +12 -0
- package/runtime/templates/reports/quick-input.template.md +12 -0
- package/runtime/templates/reports/release-handoff-input.template.md +23 -10
- package/runtime/templates/reports/schedule.template.md +12 -0
- package/runtime/templates/reports/settings.template.json +92 -30
- package/runtime/templates/reports/task-brief.template.md +12 -0
- package/src/install.mjs +1 -0
- package/src/uninstall.mjs +1 -0
package/package.json
CHANGED
package/runtime/BUILD.json
CHANGED
package/runtime/agents/SKILL.md
CHANGED
|
@@ -205,7 +205,11 @@ Convergence is enabled by default. Configure via task-manifest.json:
|
|
|
205
205
|
- `convergence.maxRounds`: 1–3 — **phase-aware default**: `1` for `requirements-discovery`, `2` for all other task types
|
|
206
206
|
- `convergence.verificationMode`: `"lightweight"` | `"full-reanalysis"` (default: `"lightweight"`)
|
|
207
207
|
|
|
208
|
-
When `task-manifest.json` does not set `convergence.maxRounds`, lead MUST resolve the effective value via the phase-aware default above before entering Phase 5.5, and record the resolved value in the convergence state artifact.
|
|
208
|
+
When `task-manifest.json` does not set `convergence.maxRounds`, lead MUST resolve the effective value via the phase-aware default above before entering Phase 5.5, and record the resolved value in the convergence state artifact at `config.effectiveMaxRounds`.
|
|
209
|
+
|
|
210
|
+
**Round 2 is gated, not unconditional.** Even when `effectiveMaxRounds == 2`, Round 2 runs only when (a) the verification queue is non-empty after Round 1, AND (b) at least one Round 1 reverify dispatch terminated as `completed`. Otherwise lead writes `round2SkippedReason` to the convergence state and proceeds to final classification. See [okstra-convergence](./skills/okstra-convergence/SKILL.md) "Round 2 gate" for the predicate.
|
|
211
|
+
|
|
212
|
+
**Confirmed findings are pruned from the queue.** Findings classified as `full-consensus`, `partial-consensus`, or `worker-unique` MUST NOT appear in any subsequent round's reverify prompt for any worker. `contested` is a final classification assigned only when the last executed round completes and the queue is still non-empty — it is NEVER an intermediate queue label.
|
|
209
213
|
|
|
210
214
|
If any re-verification batch yields a `verification-error` terminal status, or a worker result fails the contract, Lead MUST record one event per violation via `python3 scripts/okstra-error-log.py append-observed --error-type contract-violation --agent <offending-agent> ...`. Use `agent: "claude-lead"` only when the violation is detected internally without a specific worker.
|
|
211
215
|
|
|
@@ -282,4 +286,6 @@ After persistence, reply briefly in Korean with: completion status, final report
|
|
|
282
286
|
| Letting `convergence.maxRounds` default to 2 for `requirements-discovery` | Resolve effective default to `1` for discovery and record in convergence state artifact |
|
|
283
287
|
| Issuing serial Read calls in Phase 1 | The intake files are independent — issue all Read calls in a single message (parallel) |
|
|
284
288
|
| Flagging the claude-worker dispatch prompt as "incomplete" because it lacks `[Required reading]` / `[Error reporting]` blocks | Intentional asymmetry — see [okstra-team-contract](./skills/okstra-team-contract/SKILL.md) "Asymmetry between claude-worker and codex/gemini-worker prompts" |
|
|
289
|
+
| Re-sending confirmed findings (`full-consensus`/`partial-consensus`/`worker-unique`) to a worker in Round 2 | Queue pruning rule — see [okstra-convergence](./skills/okstra-convergence/SKILL.md) "Round 1-N: Re-verification Loop (queue-pruned)" |
|
|
290
|
+
| Aggregating a `timeout`/`error` reverify dispatch as `DISAGREE` | Worker failure handling — record as `verification-error` and add to `skippedWorkers[]`. See [okstra-convergence](./skills/okstra-convergence/SKILL.md) "Worker failure handling in reverify" |
|
|
285
291
|
| Skipping `--substitute-final-report` in the Phase 7 collector run | Always pass the flag — see [okstra-report-writer](./skills/okstra-report-writer/SKILL.md) "Phase 7 token-usage collector" |
|
|
@@ -44,7 +44,9 @@ Unlike the Codex / Gemini workers, you are an in-process Claude subagent — you
|
|
|
44
44
|
- If the parent directory does not exist yet, create it before writing.
|
|
45
45
|
|
|
46
46
|
4. Anchor all file operations to the absolute `Project Root` from the lead prompt. Use absolute paths — do NOT rely on inherited cwd. Never use `cd` to change directory.
|
|
47
|
-
- **Executor exception (implementation phase only):** when this worker is dispatched as the `Executor` and the lead prompt provides an `EXECUTOR_WORKTREE_PATH` that differs from the session's inherited cwd, cwd-sensitive Bash commands (`cargo *`, `npm *`, `pnpm *`, `bun *`, `pytest`, `make *`, `go *`, language-toolchain test/build commands) MUST be prefixed with `cd <EXECUTOR_WORKTREE_PATH> && ` in the same Bash invocation — e.g. `cd /Users/.../worktrees/foo && cargo test -p bar`. Do NOT wrap the whole thing in `bash -lc "..."` or `bash -c "..."`; pass the chained command directly to the Bash tool so the leading `cd` token remains visible to the permission layer. The `cd` is scoped to the single Bash subshell and does not mutate the session's shell state, so this does not conflict with the "never use cd" rule above (which prevents the worker from drifting the session cwd across calls).
|
|
47
|
+
- **Executor exception (implementation phase only):** when this worker is dispatched as the `Executor` and the lead prompt provides an `EXECUTOR_WORKTREE_PATH` that differs from the session's inherited cwd, cwd-sensitive Bash commands (`cargo *`, `npm *`, `pnpm *`, `bun *`, `pytest`, `make *`, `go *`, language-toolchain test/build commands) MUST be prefixed with `cd <EXECUTOR_WORKTREE_PATH> && ` in the same Bash invocation — e.g. `cd /Users/.../worktrees/foo && cargo test -p bar`. Do NOT wrap the whole thing in `bash -lc "..."` or `bash -c "..."`; pass the chained command directly to the Bash tool so the leading `cd` token remains visible to the permission layer. The `cd` is scoped to the single Bash subshell and does not mutate the session's shell state, so this does not conflict with the "never use cd" rule above (which prevents the worker from drifting the session cwd across calls).
|
|
48
|
+
- **Verifier QA-gate exception:** verifier roles MAY use the same `cd <WORKTREE> && <cmd>` shape when executing project-declared `qaCommands` (lint / format / typecheck / test) from `project.json`, since those commands are cwd-sensitive by nature. Outside the QA gate, verifiers still read with absolute paths only — do NOT use `cd` for file inspection.
|
|
49
|
+
- **No extra chaining beyond `cd && cmd`:** the permission matcher only allows the exact two-segment shape `cd <PATH> && <single-command>`. Do NOT append additional pipes, semicolons, redirects, or `&&` chains — e.g. `cd ... && cargo test ... 2>&1 | tail -20; echo "exit:$?"` will trigger a permission prompt every dispatch because the trailing `| tail`, `; echo`, and `2>&1` tokens disqualify the prefix match against `Bash(cargo:*)`. Let Claude Code capture the full stdout/stderr and exit code natively — do not post-process with `tail`, `head`, or `echo "exit:$?"`. If output truncation is genuinely needed, run the command first and read the result in a separate tool call.
|
|
48
50
|
|
|
49
51
|
5. **MCP usage**: The canonical list of MCP servers and tools available for this run lives in the lead prompt's `## Available MCP Servers` section (sourced from `.project-docs/okstra/project.json`'s `mcpServers` array). When the task requires inspection of an external system covered by one of those servers, call the listed tool directly by name (e.g. `mcp__<server>__<tool>`). Do NOT shell out via `claude --mcp-cli call ...` or run the tool name as a Bash command — those are not valid invocation paths. If a server you need is not listed, record `MCP not available for this run` in your worker output rather than guessing a tool name.
|
|
50
52
|
|
|
@@ -49,6 +49,7 @@ Before writing the final report, you MUST read every input file enumerated in th
|
|
|
49
49
|
- For the carry-in `clarification-response.md` (if present), walk every row of sub-section 5.1 (`A1`, `A2`, ...) and 5.2 (`Q1`, `Q2`, ...) including blank-answer rows. The fact that the file you write has a structurally similar Section 5/6 is NOT an excuse to skim.
|
|
50
50
|
- Open every analysis-worker result file under `worker-results/` end-to-end. Do not summarize them from convergence output alone — convergence captures classifications, not full evidence.
|
|
51
51
|
- Before writing, state one sentence per input file confirming end-to-end reading. If you cannot truthfully say this for a file, record a `tool-failure` in the errors sidecar instead of fabricating the report.
|
|
52
|
+
- When the convergence-state file is present, read it fully and reproduce the `roundHistory[]` array, `round2SkippedReason`, and `finalClassificationCounts` in the final report's Section 1 Round History sub-table. Do not derive these values from worker results alone — they live in `state/convergence-<task-type>-<seq>.json`.
|
|
52
53
|
|
|
53
54
|
## Authoring Contract
|
|
54
55
|
|
|
@@ -58,6 +59,8 @@ Hard rules:
|
|
|
58
59
|
|
|
59
60
|
- The file's `Author:` header line is `Report writer worker` (your role) — NOT `Claude lead`.
|
|
60
61
|
- Include all four convergence categories (Full Consensus, Partial Consensus, Contested, Worker-Unique). Do not omit Contested or Worker-Unique findings.
|
|
62
|
+
- Include a Round History sub-table in Section 1 (one row per executed round) and a `round2SkippedReason` line below it. When convergence is disabled, omit both. The values are quoted verbatim from `state/convergence-<task-type>-<seq>.json` — do not recompute.
|
|
63
|
+
- Treat `verification-error` votes as their own verdict. They are listed in vote summaries as `verification-error`, not folded into AGREE/DISAGREE counts.
|
|
61
64
|
- Include the per-agent execution status table and the token-usage summary section. All numbers come from `team-state-<task-type>-<seq>.json` (populated by `okstra-token-usage.py` at the start of Phase 7). Do not estimate or invent.
|
|
62
65
|
- If only one analysis worker produced a usable result, perform a reduced-confidence write-up and say so explicitly.
|
|
63
66
|
- If evidence is missing, write `I don't know` rather than fabricating confidence.
|
|
@@ -115,3 +118,4 @@ There is NO `cli-failure` category for this worker — it has no external CLI to
|
|
|
115
118
|
- You do NOT produce independent analysis findings — your input is the analysis workers' results plus convergence output.
|
|
116
119
|
- Do NOT modify analysis worker result files. They are read-only inputs to you.
|
|
117
120
|
- If the analysis workers disagree and convergence ended with `Contested` items, surface them in the final report verbatim — do not silently pick a side.
|
|
121
|
+
- `Contested` is a final-only classification. If you see findings labeled `Contested` in the convergence state, the lead has already exhausted re-verification — do not invent a synthesizing answer; surface each worker's position verbatim.
|
|
@@ -29,6 +29,18 @@
|
|
|
29
29
|
# if worktree-path is non-empty (the implementation-phase invariant) and
|
|
30
30
|
# `worker` otherwise.
|
|
31
31
|
#
|
|
32
|
+
# When role == `verifier`, the wrapper additionally grants the codex
|
|
33
|
+
# `workspace-write` sandbox write access to `~/.cargo` and `~/.rustup` (when
|
|
34
|
+
# they exist). Cargo's package-cache flock (`~/.cargo/.package-cache`) and
|
|
35
|
+
# the registry/cache trees live OUTSIDE the workspace, so without these
|
|
36
|
+
# extra `--add-dir` entries any `cargo build/test/clippy` invoked by the
|
|
37
|
+
# verifier fails with `Resource temporarily unavailable` on the global
|
|
38
|
+
# flock — which is the documented FU-V1 verifier-harness failure. Only the
|
|
39
|
+
# `verifier` role gets this extension; other roles (`worker`, `executor`,
|
|
40
|
+
# any custom label) keep the prior policy. Override per-role extras via the
|
|
41
|
+
# `OKSTRA_CODEX_VERIFIER_EXTRA_DIRS` env var (colon-separated absolute
|
|
42
|
+
# paths; empty disables the verifier extension entirely).
|
|
43
|
+
#
|
|
32
44
|
# For linked worktrees (the okstra implementation default), the per-worktree
|
|
33
45
|
# git metadata (index, HEAD, refs) and the shared object database live in the
|
|
34
46
|
# main repository's `.git` directory — OUTSIDE the worktree-path. Without
|
|
@@ -107,6 +119,25 @@ if [[ -n "$worktree_path" ]]; then
|
|
|
107
119
|
fi
|
|
108
120
|
fi
|
|
109
121
|
|
|
122
|
+
# Verifier-role sandbox extension (see header comment for rationale).
|
|
123
|
+
if [[ "$role" == "verifier" ]]; then
|
|
124
|
+
if [[ -n "${OKSTRA_CODEX_VERIFIER_EXTRA_DIRS+x}" ]]; then
|
|
125
|
+
verifier_extra_raw="$OKSTRA_CODEX_VERIFIER_EXTRA_DIRS"
|
|
126
|
+
else
|
|
127
|
+
verifier_extra_raw="${HOME}/.cargo:${HOME}/.rustup"
|
|
128
|
+
fi
|
|
129
|
+
if [[ -n "$verifier_extra_raw" ]]; then
|
|
130
|
+
IFS=':' read -r -a verifier_extra_dirs <<< "$verifier_extra_raw"
|
|
131
|
+
for verifier_dir in "${verifier_extra_dirs[@]}"; do
|
|
132
|
+
[[ -z "$verifier_dir" ]] && continue
|
|
133
|
+
if [[ -d "$verifier_dir" ]]; then
|
|
134
|
+
verifier_dir_abs=$(cd "$verifier_dir" && pwd -P)
|
|
135
|
+
extra_args+=(--add-dir "$verifier_dir_abs")
|
|
136
|
+
fi
|
|
137
|
+
done
|
|
138
|
+
fi
|
|
139
|
+
fi
|
|
140
|
+
|
|
110
141
|
# Derive a live-progress log path next to the prompt. The codex CLI streams
|
|
111
142
|
# its progress over stdout/stderr, but the caller (codex-worker subagent)
|
|
112
143
|
# only polls `BashOutput` on a 60s cadence — so without a sideband, a 10–30
|
|
@@ -141,6 +172,17 @@ if [[ -n "${TMUX:-}" ]]; then
|
|
|
141
172
|
if [[ -n "$trace_pane" ]]; then
|
|
142
173
|
tmux select-pane -t "$trace_pane" -T "codex-${role}-trace" 2>/dev/null || true
|
|
143
174
|
tmux last-pane 2>/dev/null || true
|
|
175
|
+
# Register the spawned pane so the `SessionEnd` hook (see
|
|
176
|
+
# `okstra-trace-cleanup.sh`) can kill it when the caller's Claude
|
|
177
|
+
# session exits. Scope by caller `$TMUX_PANE` — the pane Claude itself
|
|
178
|
+
# is attached to — so concurrent Claude instances in the same tmux
|
|
179
|
+
# session do not stomp each other's trace panes.
|
|
180
|
+
if [[ -n "${TMUX_PANE:-}" ]]; then
|
|
181
|
+
registry_dir="${TMPDIR:-/tmp}/okstra-trace-panes"
|
|
182
|
+
mkdir -p "$registry_dir" 2>/dev/null || true
|
|
183
|
+
safe_pane="${TMUX_PANE//[^A-Za-z0-9]/_}"
|
|
184
|
+
printf '%s\n' "$trace_pane" >> "$registry_dir/${safe_pane}.list" 2>/dev/null || true
|
|
185
|
+
fi
|
|
144
186
|
fi
|
|
145
187
|
fi
|
|
146
188
|
|
|
@@ -121,6 +121,13 @@ if [[ -n "${TMUX:-}" ]]; then
|
|
|
121
121
|
if [[ -n "$trace_pane" ]]; then
|
|
122
122
|
tmux select-pane -t "$trace_pane" -T "gemini-${role}-trace" 2>/dev/null || true
|
|
123
123
|
tmux last-pane 2>/dev/null || true
|
|
124
|
+
# See `okstra-codex-exec.sh` for the registry rationale — kept in lock-step.
|
|
125
|
+
if [[ -n "${TMUX_PANE:-}" ]]; then
|
|
126
|
+
registry_dir="${TMPDIR:-/tmp}/okstra-trace-panes"
|
|
127
|
+
mkdir -p "$registry_dir" 2>/dev/null || true
|
|
128
|
+
safe_pane="${TMUX_PANE//[^A-Za-z0-9]/_}"
|
|
129
|
+
printf '%s\n' "$trace_pane" >> "$registry_dir/${safe_pane}.list" 2>/dev/null || true
|
|
130
|
+
fi
|
|
124
131
|
fi
|
|
125
132
|
fi
|
|
126
133
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# okstra-trace-cleanup.sh — kill tmux trace panes spawned by okstra worker
|
|
4
|
+
# wrappers (`okstra-codex-exec.sh`, `okstra-gemini-exec.sh`) for the current
|
|
5
|
+
# Claude Code session.
|
|
6
|
+
#
|
|
7
|
+
# Invoked from the `SessionEnd` hook in
|
|
8
|
+
# `templates/reports/settings.template.json`. The wrappers register every
|
|
9
|
+
# pane they split into a registry file keyed by the caller's `$TMUX_PANE`
|
|
10
|
+
# (i.e. the pane Claude itself is attached to). On Claude `/exit`, the hook
|
|
11
|
+
# runs in that same pane's env, reads its own registry file, and kills each
|
|
12
|
+
# registered pane.
|
|
13
|
+
#
|
|
14
|
+
# Scoping by caller `$TMUX_PANE` (not by tmux session) lets multiple Claude
|
|
15
|
+
# instances coexist in the same tmux session without stomping each other's
|
|
16
|
+
# trace panes.
|
|
17
|
+
#
|
|
18
|
+
# Failures are tolerated silently — a stale pane id, missing $TMUX, or a
|
|
19
|
+
# locked tmux client must never prevent Claude from exiting cleanly.
|
|
20
|
+
|
|
21
|
+
set -u
|
|
22
|
+
|
|
23
|
+
# No tmux pane context → nothing to clean.
|
|
24
|
+
if [[ -z "${TMUX_PANE:-}" ]]; then
|
|
25
|
+
exit 0
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
registry_dir="${TMPDIR:-/tmp}/okstra-trace-panes"
|
|
29
|
+
safe_pane="${TMUX_PANE//[^A-Za-z0-9]/_}"
|
|
30
|
+
registry_file="$registry_dir/${safe_pane}.list"
|
|
31
|
+
|
|
32
|
+
if [[ ! -f "$registry_file" ]]; then
|
|
33
|
+
exit 0
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
while IFS= read -r pane_id; do
|
|
37
|
+
[[ -n "$pane_id" ]] || continue
|
|
38
|
+
tmux kill-pane -t "$pane_id" 2>/dev/null || true
|
|
39
|
+
done < "$registry_file"
|
|
40
|
+
|
|
41
|
+
rm -f "$registry_file" 2>/dev/null || true
|
|
42
|
+
exit 0
|
|
@@ -19,8 +19,14 @@
|
|
|
19
19
|
- acceptance blockers
|
|
20
20
|
- residual risk
|
|
21
21
|
- final release recommendations
|
|
22
|
+
- Pre-verification entry gate (mandatory — refuse to start if any item fails):
|
|
23
|
+
- the task brief MUST cite the originating `implementation` final-report path under `## Source Implementation Report`. The lead opens that file and confirms it includes `Plan link & approval evidence`, `Commit list`, `Diff summary`, `Validation evidence`, and `Routing recommendation for final-verification`.
|
|
24
|
+
- the task brief MUST identify the worktree / checkout under verification and the implementation base ref. If the implementation report names a task worktree, final-verification MUST inspect that same worktree rather than the caller's original checkout.
|
|
25
|
+
- the lead MUST capture `git rev-parse HEAD`, `git status --short`, and `git diff --stat <implementation-base>..HEAD` from the verification worktree before dispatching workers. These values are the verification target and must be cited in the final report.
|
|
26
|
+
- if the cited implementation report is missing, lacks commits for delivered code changes, or the current checkout does not match the implementation report's commit list / diff summary, the run MUST end with status `blocked` and route back to `implementation` or `implementation-planning` rather than verifying an ambiguous target.
|
|
22
27
|
- Required deliverable shape (final report, in addition to the standard sections):
|
|
23
|
-
- **
|
|
28
|
+
- **Source Implementation Report**: relative path of the originating `implementation` final-report file, the quoted commit list / diff summary used as the verification target, the worktree path inspected, and the base/head SHAs captured at run start.
|
|
29
|
+
- **Verdict vocabulary**: Section 2 (`Final Verdict`) MUST include a `Verdict Token` field whose value is exactly one of `accepted`, `conditional-accept`, or `blocked`. `conditional-accept` requires an explicit, exhaustive list of conditions; ambiguous verdicts ("looks good", "mostly ready") are not allowed.
|
|
24
30
|
- **Acceptance Blockers block** (under section 4): one row per blocker with `id`, `severity` (`critical` / `major` / `minor`), evidence (file path, log excerpt, or test output), and the recommended follow-up phase (`error-analysis` or `implementation-planning`). Empty block is acceptable and preferred — render the single line `- No acceptance blockers found.`
|
|
25
31
|
- **Residual Risk block** (under section 4): risks that are not blockers but should be tracked, each with mitigation owner and a trigger that would escalate them to a blocker.
|
|
26
32
|
- **Validation Evidence**: for every requirement in the originating plan or task brief, cite the artifact (commit SHA, test output, log line, MCP SELECT result) that demonstrates coverage. Paraphrased "verified" claims without an artifact are rejected.
|
|
@@ -30,7 +36,7 @@
|
|
|
30
36
|
- Clarification request policy (phase-specific addendum — shared policy is in `_common-contract.md`):
|
|
31
37
|
- populate section 5 only when a blocker hinges on information only the user can supply (deployment intent, intended target environment, business-rule interpretation)
|
|
32
38
|
- Self-review pass before finalising the report (`Claude lead` runs this; do not delegate to a generic subagent):
|
|
33
|
-
1. **Verdict precision** — section 2
|
|
39
|
+
1. **Verdict precision** — section 2 includes `Verdict Token` with one of the three allowed verdict tokens; `conditional-accept` lists every condition as an actionable item.
|
|
34
40
|
2. **Blocker traceability** — every blocker cites a concrete artifact (file:line, log excerpt, test exit code, MCP SELECT). Blockers without evidence are demoted to residual risk or removed.
|
|
35
41
|
3. **Coverage check** — every requirement in the originating plan/task brief is either marked covered (with artifact) or listed as a blocker. No silent omissions.
|
|
36
42
|
4. **Verifier dissent preserved** — if workers reach different verdicts, the disagreement is visible in section 1.2; synthesis hides nothing.
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
- each step is one action completable in roughly 2–5 minutes (e.g. "write the failing test for X", "run it to confirm it fails", "implement minimal code", "run test to confirm pass", "commit")
|
|
54
54
|
- every step names exact file paths and exact commands; for code steps, include the actual code or the diff sketch — not a description
|
|
55
55
|
- prefer TDD ordering (failing test → implementation → green → commit) when the touched area has or can have tests
|
|
56
|
-
- dependency / migration risk assessment (ordering constraints, data backfills, feature-flag prerequisites,
|
|
56
|
+
- dependency / migration risk assessment (ordering constraints, data backfills, feature-flag prerequisites, repo-internal sequencing)
|
|
57
57
|
- validation checklist (pre / mid / post) — each item is an exact command or observable outcome
|
|
58
58
|
- rollback strategy — exact revert path (commits, flags, migrations) and the signal that triggers rollback
|
|
59
59
|
- explicit `User Approval Request (사용자 승인 게이트)` block placed at the **top of the report** with a single canonical checkbox marker `- [ ] Approved` (user toggles to `- [x] Approved` to authorise the next `implementation` run). Section `4.5.8` is retained only as a back-pointer to this top block for validator/key-substring compatibility — it must NOT carry an independent marker.
|
|
@@ -1,52 +1,49 @@
|
|
|
1
1
|
# Release Handoff Profile
|
|
2
2
|
|
|
3
|
-
- Purpose: take an `accepted` final-verification verdict and turn it into a delivered
|
|
4
|
-
- **Execution model: single-lead, no worker dispatch.** This phase is a thin orchestrator over `git` / `gh`; it does NOT run team-mode, does NOT call `TeamCreate`, does NOT dispatch analysis or drafter sub-agents, and does NOT run convergence. The Claude lead performs every step inline (drafting
|
|
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
|
|
4
|
+
- **Execution model: single-lead, no worker dispatch.** This phase is a thin orchestrator over `git` / `gh`; it does NOT run team-mode, does NOT call `TeamCreate`, 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
|
- Required workers: *(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
7
|
- The Claude lead is the sole agent for this run. No `Agent(...)` worker dispatch, no `TeamCreate`, no parallel sub-agents, no convergence loop.
|
|
8
|
-
- The lead drafts the
|
|
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 `okstra-final-report.template.md` structure, including the `## 4.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.
|
|
11
11
|
- The shared "authority & permissions assumption" rule from the common contract still applies: assume the user holds every permission needed; do not block on hypothetical approvals.
|
|
12
12
|
- The shared "MCP read-only" rule still applies if the brief lists MCP servers, though most release-handoff runs do not use MCP.
|
|
13
13
|
- Pre-handoff entry gate (mandatory — refuse to start if any item fails):
|
|
14
|
-
- the task brief MUST cite the originating `final-verification` final-report path under `## Source Verification Report`. The lead opens that file and confirms section `## 2. Final Verdict` contains
|
|
15
|
-
- if the verdict is `conditional-accept`, `blocked`, or any other token (including ambiguous phrasing like "looks good"), the run MUST end immediately with status `blocked` and a routing recommendation back to `error-analysis` or `implementation-planning`. Do NOT prompt the user;
|
|
16
|
-
- the lead MUST capture `git status --short` and confirm the working tree is clean
|
|
14
|
+
- the task brief MUST cite the originating `final-verification` final-report path under `## Source Verification Report`. The lead opens that file and confirms section `## 2. Final Verdict` contains a `Verdict Token` field whose value is exactly `accepted`.
|
|
15
|
+
- if the verdict is `conditional-accept`, `blocked`, or any other token (including ambiguous phrasing like "looks good"), the run MUST end immediately with status `blocked` and a routing recommendation back to `error-analysis` or `implementation-planning`. Do NOT prompt the user; Do NOT run any git command.
|
|
16
|
+
- the lead MUST capture `git status --short` and confirm the working tree is clean. Dirty state aborts the run; release-handoff packages the commits produced by `implementation`, it does not stage or commit changes.
|
|
17
17
|
- the lead MUST capture `git rev-parse --abbrev-ref HEAD` and record it as the **feature branch**. If the current branch is itself `main`, `master`, `prod`, `preprod`, `staging`, or `dev`, the run MUST end immediately — release-handoff never operates on a base branch.
|
|
18
|
+
- the lead MUST confirm `git log --oneline <base>..HEAD` contains at least one implementation commit. If it is empty, the run MUST end with status `blocked` and route back to `implementation`.
|
|
18
19
|
- User interaction protocol (Claude lead — performed in order, using `AskUserQuestion` or the equivalent interactive prompt):
|
|
19
20
|
1. **Action selection** — present three choices and capture exactly one:
|
|
20
|
-
- `
|
|
21
|
-
- `
|
|
21
|
+
- `local only` — record the accepted, already-committed branch and end without push or PR.
|
|
22
|
+
- `push + PR` — push the feature branch, then open or reuse a pull request.
|
|
22
23
|
- `skip` — record the verified state and end the run without any git command.
|
|
23
24
|
If the user picks `skip`, route directly to the final-report self-review pass.
|
|
24
|
-
2. **PR base branch** (only when the user picked `
|
|
25
|
+
2. **PR base branch** (only when the user picked `push + PR`) — present six options and capture exactly one:
|
|
25
26
|
- `staging`
|
|
26
27
|
- `preprod`
|
|
27
|
-
- `prod`
|
|
28
28
|
- `main`
|
|
29
|
-
- `dev`
|
|
30
29
|
- `직접 입력` (free-form branch name; lead validates the name exists on origin via `git ls-remote --heads origin <name>` and re-asks on failure)
|
|
31
30
|
The chosen base MUST NOT equal the feature branch. If it does, re-ask.
|
|
32
|
-
3. **
|
|
31
|
+
3. **PR title + PR body confirmation** — show the lead's inline draft verbatim and capture one of:
|
|
33
32
|
- `use as-is` — proceed with the drafted text.
|
|
34
33
|
- `edit then proceed` — accept inline edits from the user, then proceed with the edited text.
|
|
35
|
-
- `cancel` — end the run without executing
|
|
34
|
+
- `cancel` — end the run without executing push or PR commands; record the cancellation in the final report.
|
|
36
35
|
- Inline drafting rules (Claude lead):
|
|
37
|
-
- read the run brief, the cited final-verification report, and `git diff <base>..HEAD --stat` to ground the drafted text in actual changes.
|
|
36
|
+
- read the run brief, the cited final-verification report, `git log --oneline <base>..HEAD`, and `git diff <base>..HEAD --stat` to ground the drafted text in actual committed changes.
|
|
38
37
|
- produce **two artifacts** before showing them to the user:
|
|
39
|
-
1. **
|
|
38
|
+
1. **PR title** — by default the subject of the most recent implementation commit, or a concise Conventional Commits-style summary of the committed range.
|
|
40
39
|
2. **PR body** — markdown with sections `## Summary`, `## Changes`, `## Test plan`, `## Linked issues` (omit a section only if it is genuinely empty).
|
|
41
|
-
- if the diff is empty or no commit can be produced (working tree already matches the base), record "no staged changes; commit skipped" in the final report and skip `git commit` while still proceeding to the PR step if requested.
|
|
42
40
|
- Allowed actions during the run (Claude lead only):
|
|
43
41
|
- read-only inspection: `git status`, `git status --short`, `git diff`, `git log`, `git rev-parse`, `git ls-remote --heads origin <name>`, `gh pr list --head <branch>`, `gh pr view <url>`.
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
- PR creation (only when the user picked `commit + PR` AND no PR with the same head already exists on origin): `gh pr create --base <chosen-base> --head <current-branch> --title "<title>" --body "<body>"`. The title is the commit message subject by default; the body is the user-confirmed PR body.
|
|
42
|
+
- feature-branch push (only when the user picked `push + PR`): `git push -u origin <current-branch>`. The pushed ref MUST be the feature branch — never the chosen base branch.
|
|
43
|
+
- PR creation (only when the user picked `push + PR` AND no PR with the same head already exists on origin): `gh pr create --base <chosen-base> --head <current-branch> --title "<title>" --body "<body>"`. The title and body are the user-confirmed PR draft.
|
|
47
44
|
- PR reuse: if `gh pr list --head <branch> --state open --json url --jq '.[0].url'` returns a URL, treat that PR as already existing — record the URL in the final report and SKIP `gh pr create`.
|
|
48
|
-
- Idempotency: if `git diff --cached` and `git diff` are both empty (nothing to commit), record "no staged changes; commit skipped" in the final report and skip `git commit` while still proceeding to the PR step if requested.
|
|
49
45
|
- Forbidden actions (any occurrence → terminal status `contract-violated`):
|
|
46
|
+
- local commit commands of any kind (`git add`, `git commit`, `git restore --staged`, `git stash`). Commits belong to the prior `implementation` phase.
|
|
50
47
|
- any of the following git push variants, regardless of intent or whether the user said "force it":
|
|
51
48
|
- `git push --force`
|
|
52
49
|
- `git push --force-with-lease`
|
|
@@ -54,36 +51,37 @@
|
|
|
54
51
|
- `git push +<refspec>`
|
|
55
52
|
- any other invocation that rewrites remote history
|
|
56
53
|
- pushing directly to a base branch — i.e. `git push origin <branch>` where `<branch>` is `main`, `master`, `prod`, `preprod`, `staging`, `dev`, or the branch the user chose as the PR base in this run. The only permitted push target is the current feature branch.
|
|
57
|
-
- bypassing repo safeguards: `--no-verify` / `-n` on `git
|
|
54
|
+
- bypassing repo safeguards: `--no-verify` / `-n` on `git push`, disabling safeguards via equivalent flags, or any hook bypass.
|
|
58
55
|
- release-publishing commands: `gh release create`, `gh release edit`, `npm publish`, `cargo publish`, `pip publish`, `twine upload`, `docker push`, `terraform apply`, `kubectl apply` against any non-local cluster.
|
|
59
56
|
- 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
|
-
- executing any mutating command the user did NOT select. Examples: opening a PR when the user picked `
|
|
57
|
+
- executing any mutating command the user did NOT select. Examples: opening a PR when the user picked `local only`; pushing when the user picked `skip`; switching the PR base branch silently after the user already chose one.
|
|
61
58
|
- 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
59
|
- `TeamCreate`, `Agent(...)` dispatch of any kind, or any other parallel sub-agent fan-out. This phase runs entirely under the Claude lead.
|
|
63
60
|
- 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
61
|
- Required deliverable shape (final report, in addition to the standard sections):
|
|
65
|
-
- **Source Verification Report**: relative path of the originating `final-verification` final-report file plus the literal quoted
|
|
62
|
+
- **Source Verification Report**: relative path of the originating `final-verification` final-report file plus the literal quoted `Verdict Token` row whose value is `accepted`.
|
|
66
63
|
- **Feature Branch & Working-Tree State**: branch name from `git rev-parse --abbrev-ref HEAD`, output of `git status --short` at run start.
|
|
67
64
|
- **User Selections**: a block recording each prompt and the user's verbatim answer.
|
|
68
|
-
- Q1 action: `
|
|
65
|
+
- Q1 action: `local only` | `push + PR` | `skip`.
|
|
69
66
|
- Q2 PR base (if applicable): the chosen branch and how it was selected (menu pick vs free-form input).
|
|
70
|
-
- Q3
|
|
67
|
+
- Q3 title/body: `use as-is` | `edit then proceed` (with a diff between the lead's draft and the final text) | `cancel`.
|
|
71
68
|
- **Executed Commands**: every git / gh command the lead actually ran, with its exit code and a one-line stdout/stderr summary. Read-only inspection commands MAY be summarised; mutating commands MUST be listed verbatim.
|
|
72
|
-
- **Commit List**: each commit
|
|
69
|
+
- **Commit List**: each existing implementation commit in `git log <base>..HEAD`, with short/full SHA, subject line, and touched files. Release-handoff MUST NOT create new commits.
|
|
73
70
|
- **Pull Request Outcome**: one of
|
|
74
|
-
- `- No PR action requested.` (user picked `
|
|
71
|
+
- `- No PR action requested.` (user picked `local only` or `skip`)
|
|
75
72
|
- `- PR created: <url>` with title and base branch
|
|
76
73
|
- `- PR reused: <url>` when an existing PR was found via `gh pr list`
|
|
77
74
|
- `- PR creation skipped: <reason>` for any user-driven cancellation
|
|
78
75
|
- **Routing recommendation**: explicit `done` token, since release-handoff is the terminal lifecycle phase. If the run ended in `skip` or `cancel`, the recommendation MUST also state whether re-entry into release-handoff is appropriate.
|
|
79
76
|
- Self-review pass before finalising the report (`Claude lead` runs this):
|
|
80
|
-
1. **Entry-gate audit** — section 2 cites the originating final-verification report path and the literal `
|
|
77
|
+
1. **Entry-gate audit** — section 2 cites the originating final-verification report path and the literal `Verdict Token` row with value `accepted`. If either is missing, the run is invalid and MUST be re-routed to `final-verification`.
|
|
81
78
|
2. **User-selection traceability** — every executed mutating command maps to a user selection captured in the report. Any mutating command without a corresponding user answer is a contract violation.
|
|
82
79
|
3. **Forbidden-action audit** — scan the run's session transcripts (`git`, `gh` invocations) for every entry in the Forbidden actions list above. Any occurrence means the run has crossed into unsafe territory and MUST be flagged as `contract-violated`.
|
|
83
80
|
4. **Push-target audit** — for every `git push` recorded, confirm the refspec resolves to the feature branch, not the base branch.
|
|
84
81
|
5. **Idempotency check** — if a PR with the same head already existed at run start, confirm the report records `PR reused` rather than a fresh `gh pr create` invocation.
|
|
85
82
|
- Non-goals:
|
|
86
83
|
- re-litigating the final-verification verdict — release-handoff trusts the cited `accepted` verdict and does not reopen acceptance checks.
|
|
84
|
+
- creating, amending, squashing, or rewriting commits. Commit production belongs to `implementation`.
|
|
87
85
|
- opening additional PRs, releases, or deployments beyond the single PR the user chose to create.
|
|
88
86
|
- merging the PR. Merging is a separate, manual step performed by the user (or by repo automation) after release-handoff ends; the lead MUST NOT call `gh pr merge`.
|
|
89
87
|
- escalating beyond the menu choices on user phrasing — every mutating action requires an explicit menu selection.
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
{{INCLUDE:_common-contract.md}}
|
|
11
11
|
- Primary focus areas:
|
|
12
12
|
- classify the work as bugfix, feature, improvement, refactor, or ops-change
|
|
13
|
-
- determine whether `error-analysis
|
|
13
|
+
- determine whether `error-analysis` or `implementation-planning` is the next safe step; direct `implementation` handoff is not a valid routing target because implementation requires an approved `implementation-planning` report
|
|
14
14
|
- identify missing materials that block reliable routing
|
|
15
15
|
- define task continuity expectations for long-running work under the same task key
|
|
16
16
|
- capture approval or confirmation points before the next phase starts
|
|
@@ -47,6 +47,74 @@ def _write_json(path: Path, payload: dict) -> None:
|
|
|
47
47
|
_write_text(path, json.dumps(payload, indent=2, ensure_ascii=False) + "\n")
|
|
48
48
|
|
|
49
49
|
|
|
50
|
+
_FM_DEFAULT = "no-classification"
|
|
51
|
+
|
|
52
|
+
_FM_TAGS_BASE = ["obsidian", "okstra"]
|
|
53
|
+
|
|
54
|
+
_FM_TAGS_CATALOG: dict[str, list[str]] = {
|
|
55
|
+
"task-brief": ["task-brief"],
|
|
56
|
+
"task-index": ["task-index"],
|
|
57
|
+
"error-analysis-input": ["error-analysis", "input"],
|
|
58
|
+
"implementation-input": ["implementation", "input"],
|
|
59
|
+
"implementation-planning-input": ["implementation-planning", "input"],
|
|
60
|
+
"final-verification-input": ["final-verification", "input"],
|
|
61
|
+
"release-handoff-input": ["release-handoff", "input"],
|
|
62
|
+
"quick-input": ["quick", "input"],
|
|
63
|
+
"final-report": ["final-report"],
|
|
64
|
+
"schedule": ["schedule"],
|
|
65
|
+
"prd-brief": ["prd", "brief"],
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _fm_scalar(value: str, default: str = _FM_DEFAULT) -> str:
|
|
70
|
+
v = (value or "").strip()
|
|
71
|
+
return v if v else default
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _fm_array(values: list[str], extras: list[str] | None = None) -> str:
|
|
75
|
+
items = [str(v).strip() for v in values if v and str(v).strip()]
|
|
76
|
+
if extras:
|
|
77
|
+
items.extend(str(e).strip() for e in extras if str(e).strip())
|
|
78
|
+
if not items:
|
|
79
|
+
return "[]"
|
|
80
|
+
return "[" + ", ".join(f'"{v}"' for v in items) + "]"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _fm_tags(doc_type: str) -> str:
|
|
84
|
+
extra = _FM_TAGS_CATALOG.get((doc_type or "").strip(), [])
|
|
85
|
+
return _fm_array(_FM_TAGS_BASE + list(extra))
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _doc_type_from_template_path(template_path: str) -> str:
|
|
89
|
+
name = Path(template_path).name
|
|
90
|
+
if name.endswith(".template.md"):
|
|
91
|
+
stem = name[: -len(".template.md")]
|
|
92
|
+
else:
|
|
93
|
+
stem = Path(name).stem
|
|
94
|
+
if stem == "brief" and "prd" in Path(template_path).parts:
|
|
95
|
+
return "prd-brief"
|
|
96
|
+
return stem
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _frontmatter_mapping(ctx: dict) -> dict:
|
|
100
|
+
task_id = (ctx.get("TASK_ID") or "").strip()
|
|
101
|
+
project_id = (ctx.get("PROJECT_ID") or "").strip()
|
|
102
|
+
task_group = (ctx.get("TASK_GROUP") or "").strip()
|
|
103
|
+
task_key = (ctx.get("TASK_KEY") or "").strip()
|
|
104
|
+
task_date = (ctx.get("TASK_DATE") or "").strip()
|
|
105
|
+
doc_type = (ctx.get("DOC_TYPE") or "").strip()
|
|
106
|
+
return {
|
|
107
|
+
"{{TASK_KEY}}": _fm_scalar(task_key),
|
|
108
|
+
"{{TASK_ID}}": _fm_scalar(task_id),
|
|
109
|
+
"{{PROJECT_ID}}": _fm_scalar(project_id),
|
|
110
|
+
"{{TASK_GROUP}}": _fm_scalar(task_group),
|
|
111
|
+
"{{TASK_DATE}}": _fm_scalar(task_date),
|
|
112
|
+
"{{FM_ID}}": _fm_array([task_id, project_id, task_group]),
|
|
113
|
+
"{{FM_ALIASES}}": _fm_array([task_id, project_id, task_group]),
|
|
114
|
+
"{{FM_TAGS}}": _fm_tags(doc_type),
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
|
|
50
118
|
def _resolve_workers(ctx: dict) -> list[str]:
|
|
51
119
|
return [w.strip() for w in ctx.get("SELECTED_REVIEWERS", "").split(",") if w.strip()]
|
|
52
120
|
|
|
@@ -830,6 +898,10 @@ def render_task_index(template_path: str, output_path: str, ctx: dict) -> None:
|
|
|
830
898
|
mapping = {
|
|
831
899
|
"{{TASK_KEY}}": task_manifest.get("taskKey", ctx.get("TASK_KEY", "")),
|
|
832
900
|
"{{TASK_TYPE}}": task_manifest.get("taskType", ctx.get("ANALYSIS_TYPE", "")),
|
|
901
|
+
"{{TASK_DATE}}": ctx.get("TASK_DATE", ""),
|
|
902
|
+
"{{PROJECT_ID}}": ctx.get("PROJECT_ID", ""),
|
|
903
|
+
"{{TASK_GROUP}}": ctx.get("TASK_GROUP", ""),
|
|
904
|
+
"{{TASK_ID}}": ctx.get("TASK_ID", ""),
|
|
833
905
|
"{{CURRENT_TASK_STATUS}}": task_manifest.get("currentStatus", ctx.get("CURRENT_TASK_STATUS", "")),
|
|
834
906
|
"{{CURRENT_RUN_STATUS}}": task_manifest.get("latestRunStatus", ctx.get("CURRENT_RUN_STATUS", "")),
|
|
835
907
|
"{{RELATED_TASKS_INLINE}}": ctx.get("RELATED_TASKS_INLINE", "None"),
|
|
@@ -870,6 +942,9 @@ def render_task_index(template_path: str, output_path: str, ctx: dict) -> None:
|
|
|
870
942
|
"{{WORKFLOW_PHASE_STATE_LINES}}": "\n".join(phase_state_lines),
|
|
871
943
|
"{{WORKFLOW_LAST_SAFE_CHECKPOINT_LINES}}": "\n".join(checkpoint_lines),
|
|
872
944
|
}
|
|
945
|
+
fm_ctx = dict(ctx)
|
|
946
|
+
fm_ctx.setdefault("DOC_TYPE", _doc_type_from_template_path(template_path))
|
|
947
|
+
mapping.update(_frontmatter_mapping(fm_ctx))
|
|
873
948
|
rendered = template
|
|
874
949
|
for k, v in mapping.items():
|
|
875
950
|
rendered = rendered.replace(k, v)
|
|
@@ -1035,10 +1110,6 @@ def render_template_file(template_path: str, output_path: str, ctx: dict) -> Non
|
|
|
1035
1110
|
|
|
1036
1111
|
mapping = {
|
|
1037
1112
|
"{{TEAM_CREATION_GATE}}": team_creation_gate_block,
|
|
1038
|
-
"{{PROJECT_ID}}": ctx.get("PROJECT_ID", ""),
|
|
1039
|
-
"{{TASK_GROUP}}": ctx.get("TASK_GROUP", ""),
|
|
1040
|
-
"{{TASK_ID}}": ctx.get("TASK_ID", ""),
|
|
1041
|
-
"{{TASK_KEY}}": ctx.get("TASK_KEY", ""),
|
|
1042
1113
|
"{{TASK_TYPE}}": ctx.get("ANALYSIS_TYPE", ""),
|
|
1043
1114
|
"{{ANALYSIS_PROFILE}}": ctx.get("REVIEW_PROFILE", ""),
|
|
1044
1115
|
"{{ANALYSIS_TYPE}}": ctx.get("ANALYSIS_TYPE", ""),
|
|
@@ -1143,6 +1214,9 @@ def render_template_file(template_path: str, output_path: str, ctx: dict) -> Non
|
|
|
1143
1214
|
"{{EXECUTOR_WORKTREE_STATUS}}": ctx.get("EXECUTOR_WORKTREE_STATUS", ""),
|
|
1144
1215
|
"{{EXECUTOR_WORKTREE_NOTE}}": ctx.get("EXECUTOR_WORKTREE_NOTE", ""),
|
|
1145
1216
|
}
|
|
1217
|
+
fm_ctx = dict(ctx)
|
|
1218
|
+
fm_ctx.setdefault("DOC_TYPE", _doc_type_from_template_path(template_path))
|
|
1219
|
+
mapping.update(_frontmatter_mapping(fm_ctx))
|
|
1146
1220
|
rendered = template
|
|
1147
1221
|
for k, v in mapping.items():
|
|
1148
1222
|
rendered = rendered.replace(k, v)
|
|
@@ -30,6 +30,10 @@ def _now_iso() -> str:
|
|
|
30
30
|
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
31
31
|
|
|
32
32
|
|
|
33
|
+
def _now_task_date() -> str:
|
|
34
|
+
return datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S")
|
|
35
|
+
|
|
36
|
+
|
|
33
37
|
def _okstra_home() -> Path:
|
|
34
38
|
home = os.environ.get("OKSTRA_HOME")
|
|
35
39
|
if home:
|
|
@@ -109,6 +113,7 @@ def compute_and_write_run_context(
|
|
|
109
113
|
run_seq_override=run_seq_override,
|
|
110
114
|
)
|
|
111
115
|
ctx["RUN_TIMESTAMP_ISO"] = _now_iso()
|
|
116
|
+
ctx["TASK_DATE"] = _now_task_date()
|
|
112
117
|
run_manifests_dir = Path(ctx["RUN_MANIFESTS_DIR"])
|
|
113
118
|
ctx_path = run_manifests_dir / _run_context_filename(
|
|
114
119
|
ctx["TASK_TYPE_SEGMENT"], ctx["RUN_MANIFESTS_SEQ"])
|
|
@@ -124,23 +124,24 @@ PHASE_RULES: dict[str, dict[str, str]] = {
|
|
|
124
124
|
},
|
|
125
125
|
"release-handoff": {
|
|
126
126
|
"allowed": (
|
|
127
|
-
" - entering this phase only when the cited final-verification report's
|
|
128
|
-
" - asking the user (via `AskUserQuestion` / interactive prompt) which delivery action to take: `
|
|
127
|
+
" - entering this phase only when the cited final-verification report's `Verdict Token` is exactly `accepted`\n"
|
|
128
|
+
" - asking the user (via `AskUserQuestion` / interactive prompt) which delivery action to take: `local only`, `push + PR`, or `skip` (end the run)\n"
|
|
129
129
|
" - asking the user to pick a PR base branch from `staging` | `preprod` | `prod` | `main` | `dev` | a user-supplied branch name\n"
|
|
130
|
-
" - drafting
|
|
131
|
-
" -
|
|
130
|
+
" - drafting PR title and PR body **inline as the Claude lead** (no drafter worker, no `Agent` dispatch); the lead reviews its own draft with the user before any mutating git / gh command runs\n"
|
|
131
|
+
" - read-only git inspection: `git status`, `git diff`, `git log`, `git rev-parse`\n"
|
|
132
132
|
" - pushing the current feature branch to its origin remote via `git push -u origin <current-branch>` (the feature branch only — NEVER the base branch)\n"
|
|
133
133
|
" - creating a pull request via `gh pr create --base <chosen-base> --head <current-branch>`; if a PR with the same head already exists, surface its URL and skip creation\n"
|
|
134
134
|
" - the lead writes the final report directly (no `Report writer worker` dispatch); the report still conforms to the standard final-report template"
|
|
135
135
|
),
|
|
136
136
|
"forbidden": (
|
|
137
|
-
" - entering this phase when the cited final-verification
|
|
138
|
-
" - any source-code edit, refactor, or scope expansion beyond what is strictly needed to author
|
|
137
|
+
" - entering this phase when the cited final-verification `Verdict Token` is `conditional-accept` or `blocked`, or when no final-verification report is cited\n"
|
|
138
|
+
" - any source-code edit, refactor, or scope expansion beyond what is strictly needed to author PR descriptions (the changes themselves are inherited from prior `implementation` runs)\n"
|
|
139
|
+
" - local commit commands (`git add`, `git commit`, `git stash`, `git restore --staged`); commits are produced by `implementation`, not release-handoff\n"
|
|
139
140
|
" - `git push --force`, `git push --force-with-lease`, or any rewriting of remote history\n"
|
|
140
141
|
" - pushing directly to a base branch (`main`, `master`, `prod`, `preprod`, `staging`, `dev`, or any branch the user named as the PR base)\n"
|
|
141
142
|
" - bypassing git hooks (`--no-verify`, `-n`), bypassing GPG signing, or otherwise disabling repo-configured safeguards\n"
|
|
142
143
|
" - release-publishing commands: `gh release`, `npm publish`, `cargo publish`, `pip publish`, `docker push`, `terraform apply`, `kubectl apply` against non-local clusters\n"
|
|
143
|
-
" - executing any command the user did NOT select (e.g. if the user picked `
|
|
144
|
+
" - executing any command the user did NOT select (e.g. if the user picked `local only`, opening a PR is forbidden; if the user picked `skip`, the run ends without git commands)\n"
|
|
144
145
|
" - `TeamCreate`, `Agent(...)` worker dispatch, parallel sub-agent fan-out, or any team-mode orchestration — this phase runs single-lead\n"
|
|
145
146
|
" - silently retrying a failed git/gh command with weaker flags (e.g. retrying `git push` with `--force` after a non-fast-forward rejection)"
|
|
146
147
|
),
|