Spaces:
Runtime error
Runtime error
File size: 31,677 Bytes
a6b96c2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 | <purpose>
Execute a phase prompt (PLAN.md) and create the outcome summary (SUMMARY.md).
</purpose>
<required_reading>
Read STATE.md before any operation to load project context.
Read config.json for planning behavior settings.
@/Users/theogengineer/Projects/Multilingual-Absa/.opencode/gsd-core/references/git-integration.md
</required_reading>
<atomic_close_out_invariant>
For each executed plan, the only complete close-out order is:
`production-code commit(s) -> SUMMARY commit -> STATE/ROADMAP update`.
For a synchronous executor, the only legal half-state is mid-production-commits
while the executor is still actively working. Once production commits for a plan
exist, returning without a committed SUMMARY.md is an illegal partial-plan state.
The next execute-phase resume must detect that condition before dispatching
another executor.
**Async exception β `external_job_waiting`.** When an executor dispatches an
async external job (long-running compute) it commits an async-job manifest at
`.planning/async-jobs/<job>.json` and returns *without* SUMMARY.md. With a
manifest recording a non-terminal job for this plan, the SUMMARY-absent state is
a **legal deferred state** (`external_job_waiting`), not an illegal partial.
SUMMARY.md is deferred until the external job reaches a terminal state and its
output is verified. Resume reconciles against the manifest and must NOT
re-dispatch a fresh executor for a plan with a non-terminal manifest (that would
duplicate the external job). The manifest schema is the stability contract in
`docs/reference/planning-artifacts.md`; the scheduler adapter that *writes* it is
a capability (#1164), not core.
</atomic_close_out_invariant>
<available_agent_types>
Valid GSD subagent types (use exact names β do not fall back to 'general-purpose'):
- gsd-executor β Executes plan tasks, commits, creates SUMMARY.md
</available_agent_types>
<process>
<step name="init_context" priority="first">
Load execution context (paths only to minimize orchestrator context):
```bash
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "/Users/theogengineer/Projects/Multilingual-Absa/.opencode/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="/Users/theogengineer/Projects/Multilingual-Absa/.opencode/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
INIT=$(gsd_run query init.execute-phase "${PHASE}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
```
Extract from init JSON: `executor_model`, `commit_docs`, `sub_repos`, `phase_dir`, `phase_number`, `plans`, `summaries`, `incomplete_plans`, `state_path`, `config_path`.
If `.planning/` missing: error.
</step>
<step name="identify_plan">
```bash
# Use plans/summaries from INIT JSON, or list files
(ls .planning/phases/XX-name/*-PLAN.md 2>/dev/null || true) | sort
(ls .planning/phases/XX-name/*-SUMMARY.md 2>/dev/null || true) | sort
```
Find first PLAN without matching SUMMARY. Decimal phases supported (`01.1-hotfix/`).
**Exclude `external_job_waiting` plans from selection.** When choosing the first PLAN that lacks a matching SUMMARY, skip any plan whose `plan_id` matches an async-job manifest in `.planning/async-jobs/` (any status) β that plan is `external_job_waiting` or awaiting reconciliation, never work to (re-)dispatch (re-dispatching would duplicate the external job). Reconcile via the manifest / safe_resume_gate instead.
```bash
PHASE=$(echo "$PLAN_PATH" | grep -oE '[0-9]+(\.[0-9]+)?-[0-9]+')
# config settings can be fetched via gsd-tools.cjs query config-get if needed
```
<if mode="yolo">
Auto-approve: `β‘ Execute {phase}-{plan}-PLAN.md [Plan X of Y for Phase Z]` β parse_segments.
</if>
<if mode="interactive" OR="custom with gates.execute_next_plan true">
Present plan identification, wait for confirmation.
</if>
</step>
<step name="record_start_time">
```bash
PLAN_START_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
PLAN_START_EPOCH=$(date +%s)
```
</step>
<step name="parse_segments">
```bash
# Count tasks β match <task tag at any indentation level
TASK_COUNT=$(grep -cE '^\s*<task[[:space:]>]' .planning/phases/XX-name/{phase}-{plan}-PLAN.md 2>/dev/null || echo "0")
INLINE_THRESHOLD=$(gsd_run query config-get workflow.inline_plan_threshold 2>/dev/null || echo "2")
grep -n "type=\"checkpoint" .planning/phases/XX-name/{phase}-{plan}-PLAN.md
```
**Primary routing: task count threshold (#1979)**
If `INLINE_THRESHOLD > 0` AND `TASK_COUNT <= INLINE_THRESHOLD`: Use Pattern C (inline) regardless of checkpoint type. Small plans execute faster inline β avoids ~14K token subagent spawn overhead and preserves prompt cache. Configure threshold via `workflow.inline_plan_threshold` (default: 2, set to `0` to always spawn subagents).
Otherwise: Apply checkpoint-based routing below.
**Checkpoint-based routing (plans with > threshold tasks):**
| Checkpoints | Pattern | Execution |
|-------------|---------|-----------|
| None | A (autonomous) | Single subagent: full plan + SUMMARY + commit |
| Verify-only | B (segmented) | Segments between checkpoints. After none/human-verify β SUBAGENT. After decision/human-action β MAIN |
| Decision | C (main) | Execute entirely in main context |
**Pattern A:** init_agent_tracking β capture `EXPECTED_BASE=$(git rev-parse HEAD)` β print `Spawning executor agent (runs in a subagent β no output until it returns, ~1β5 min; expected, not a freeze)` β spawn Agent(subagent_type="gsd-executor", model=executor_model) with prompt: execute plan at [path], autonomous, all tasks + SUMMARY + commit, follow deviation/auth rules, report: plan name, tasks, SUMMARY path, commit hash β track agent_id β wait β update tracking β report. **Include `isolation="worktree"` only if `workflow.use_worktrees` is not `false`** (read via `config-get workflow.use_worktrees`). **When using `isolation="worktree"`, embed the `<worktree_branch_check>` block from `gsd-core/references/worktree-branch-check.md` into the prompt, substituting `{EXPECTED_BASE}` with the captured base SHA.** That guard is **verify-only and fail-closed** (#48): it asserts a per-agent `worktree-agent-*` branch and the exact base, forbids `git update-ref` self-recovery (#2924), and on any mismatch prints `FATAL:` and `exit 42` so the orchestrator can recover β the sub-agent never rewrites a worktree it did not create. This supersedes the former self-recovery (#2015), whose destructive base rewrite could fail silently under a deny rule; the base-drift it addressed affects all platforms, and base correction is now the orchestrator's responsibility.
**Pattern B:** Execute segment-by-segment. Autonomous segments: spawn subagent for assigned tasks only (no SUMMARY/commit). Checkpoints: main context. After all segments: aggregate, create SUMMARY, commit. See segment_execution.
**Pattern C:** Execute in main using standard flow (step name="execute").
Fresh context per subagent preserves peak quality. Main context stays lean.
</step>
<step name="init_agent_tracking">
```bash
if [ ! -f .planning/agent-history.json ]; then
echo '{"version":"1.0","max_entries":50,"entries":[]}' > .planning/agent-history.json
fi
rm -f .planning/current-agent-id.txt
if [ -f .planning/current-agent-id.txt ]; then
INTERRUPTED_ID=$(cat .planning/current-agent-id.txt)
echo "Found interrupted agent: $INTERRUPTED_ID"
fi
```
If interrupted: ask user to resume (Task `resume` parameter) or start fresh.
**Tracking protocol:** On spawn: write agent_id to `current-agent-id.txt`, append to agent-history.json: `{"agent_id":"[id]","task_description":"[desc]","phase":"[phase]","plan":"[plan]","segment":[num|null],"timestamp":"[ISO]","status":"spawned","completion_timestamp":null}`. On completion: status β "completed", set completion_timestamp, delete current-agent-id.txt. Prune: if entries > max_entries, remove oldest "completed" (never "spawned").
Run for Pattern A/B before spawning. Pattern C: skip.
</step>
<step name="segment_execution">
Pattern B only (verify-only checkpoints). Skip for A/C.
1. Parse segment map: checkpoint locations and types
2. Per segment:
- Subagent route: spawn gsd-executor for assigned tasks only. Prompt: task range, plan path, read full plan for context, execute assigned tasks, track deviations, NO SUMMARY/commit. Track via agent protocol.
- Main route: execute tasks using standard flow (step name="execute")
3. **Critical ordering β write and commit SUMMARY.md as one atomic block.** Do NOT
emit narrative output between the Write tool call and the commit tool call.
Truncation at this boundary is a known failure mode (see #2070 rescue logic in
execute-phase.md step 5.5).
After ALL segments: aggregate files/deviations/decisions β create SUMMARY.md β self-check:
- Verify key-files.created exist on disk with `[ -f ]`
- Check `git log --oneline --all --grep="{phase}-{plan}"` returns β₯1 commit
- Re-run ALL `<acceptance_criteria>` from every task β if any fail, fix before finalizing SUMMARY
- Re-run the plan-level `<verification>` commands β log results in SUMMARY
- Append `## Self-Check: PASSED` or `## Self-Check: FAILED` to SUMMARY
Then commit (no narrative between Write and commit).
**Known Claude Code bug (classifyHandoffIfNeeded):** If any segment agent reports "failed" with `classifyHandoffIfNeeded is not defined`, this is a Claude Code runtime bug β not a real failure. Run spot-checks; if they pass, treat as successful.
</step>
<step name="load_prompt">
```bash
cat .planning/phases/XX-name/{phase}-{plan}-PLAN.md
```
This IS the execution instructions. Follow exactly. If plan references CONTEXT.md: honor user's vision throughout.
**If plan contains `<interfaces>` block:** These are pre-extracted type definitions and contracts. Use them directly β do NOT re-read the source files to discover types. The planner already extracted what you need.
</step>
<step name="previous_phase_check">
```bash
gsd_run query phases.list --type summaries --raw
# Extract the second-to-last summary from the JSON result
```
**Text mode (`workflow.text_mode: true` in config or `--text` flag):** Set `TEXT_MODE=true` if `--text` is present in `$ARGUMENTS` OR `text_mode` from init JSON is `true`. When TEXT_MODE is active, replace every `question` call with a plain-text numbered list and ask the user to type their choice number. This is required for non-the agent runtimes (OpenAI Codex, Gemini CLI, etc.) where `question` is not available.
If previous SUMMARY has unresolved "Issues Encountered" or "Next Phase Readiness" blockers: question(header="Previous Issues", options: "Proceed anyway" | "Address first" | "Review previous").
</step>
<step name="execute">
Deviations are normal β handle via rules below.
1. Read @context files from prompt
2. **MCP tools:** If AGENTS.md or project instructions reference MCP tools (e.g. jCodeMunch for code navigation), prefer them over Grep/Glob when available. Fall back to Grep/Glob if MCP tools are not accessible.
3. Per task:
- **MANDATORY read_first gate:** If the task has a `<read_first>` field, you MUST read every listed file BEFORE making any edits. This is not optional. Do not skip files because you "already know" what's in them β read them. The read_first files establish ground truth for the task.
- `type="auto"`: if `tdd="true"` β TDD execution. Implement with deviation rules + auth gates. Verify done criteria. Commit (see task_commit). Track hash for Summary.
- `type="checkpoint:*"`: STOP β checkpoint_protocol β wait for user β continue only after confirmation.
- **HARD GATE β acceptance_criteria verification:** After completing each task, if it has `<acceptance_criteria>`, you MUST run a verification loop before proceeding:
1. For each criterion: execute the grep, file check, or CLI command that proves it passes
2. Log each result as PASS or FAIL with the command output
3. If ANY criterion fails: fix the implementation immediately, then re-run ALL criteria
4. Repeat until all criteria pass β you are BLOCKED from starting the next task until this gate clears
5. If a criterion cannot be satisfied after 2 fix attempts, log it as a deviation with reason β do NOT silently skip it
This is not advisory. A task with failing acceptance criteria is an incomplete task.
3. Run `<verification>` checks
4. Confirm `<success_criteria>` met
5. Document deviations in Summary
</step>
<authentication_gates>
## Authentication Gates
Auth errors during execution are NOT failures β they're expected interaction points.
**Indicators:** "Not authenticated", "Unauthorized", 401/403, "Please run {tool} login", "Set {ENV_VAR}"
**Protocol:**
1. Recognize auth gate (not a bug)
2. STOP task execution
3. Create dynamic checkpoint:human-action with exact auth steps
4. Wait for user to authenticate
5. Verify credentials work
6. Retry original task
7. Continue normally
**Example:** `vercel --yes` β "Not authenticated" β checkpoint asking user to `vercel login` β verify with `vercel whoami` β retry deploy β continue
**In Summary:** Document as normal flow under "## Authentication Gates", not as deviations.
</authentication_gates>
<deviation_rules>
## Deviation Rules
Apply deviation rules from the gsd-executor agent definition (single source of truth):
- **Rules 1-3** (bugs, missing critical, blockers): auto-fix, test, verify, track as deviations
- **Rule 4** (architectural changes): STOP, present decision to user, await approval
- **Scope boundary**: do not auto-fix pre-existing issues unrelated to current task
- **Fix attempt limit**: max 3 retries per deviation before escalating
- **Priority**: Rule 4 (STOP) > Rules 1-3 (auto) > unsure β Rule 4
</deviation_rules>
<deviation_documentation>
## Documenting Deviations
Summary MUST include deviations section. None? β `## Deviations from Plan\n\nNone - plan executed exactly as written.`
Per deviation: **[Rule N - Category] Title** β Found during: Task X | Issue | Fix | Files modified | Verification | Commit hash
End with: **Total deviations:** N auto-fixed (breakdown). **Impact:** assessment.
</deviation_documentation>
<tdd_plan_execution>
## TDD Execution
For `type: tdd` plans β RED-GREEN-REFACTOR:
1. **Infrastructure** (first TDD plan only): detect project, install framework, config, verify empty suite
2. **RED:** Read `<behavior>` β failing test(s) β run (MUST fail) β commit: `test({phase}-{plan}): add failing test for [feature]`
3. **GREEN:** Read `<implementation>` β minimal code β run (MUST pass) β commit: `feat({phase}-{plan}): implement [feature]`
4. **REFACTOR:** Clean up β tests MUST pass β commit: `refactor({phase}-{plan}): clean up [feature]`
Errors: RED doesn't fail β investigate test/existing feature. GREEN doesn't pass β debug, iterate. REFACTOR breaks β undo.
See `/Users/theogengineer/Projects/Multilingual-Absa/.opencode/gsd-core/references/tdd.md` for structure.
</tdd_plan_execution>
<precommit_failure_handling>
## Pre-commit Hook Failure Handling
Your commits may trigger pre-commit hooks. Auto-fix hooks handle themselves transparently β files get fixed and re-staged automatically.
**If running as a parallel executor agent (spawned by execute-phase):**
Run commits normally β let pre-commit hooks run. Do NOT use `--no-verify` by default
(#2924). Hooks should run so issues surface at the introducing commit, and silent
bypass violates project AGENTS.md guidance. If a project explicitly opts out via
`workflow.worktree_skip_hooks=true`, the orchestrator will surface that flag in the
prompt; absent that signal, hooks run normally. If a hook fails, follow the
sequential-mode handling below.
**If running as the sole executor (sequential mode):**
If a commit is BLOCKED by a hook:
1. The `git commit` command fails with hook error output
2. Read the error β it tells you exactly which hook and what failed
3. Fix the issue (type error, lint violation, secret leak, etc.)
4. `git add` the fixed files
5. Retry the commit
6. Budget 1-2 retry cycles per commit
</precommit_failure_handling>
<task_commit>
## Task Commit Protocol
Canonical per-task commit rules live in **`agents/gsd-executor.md`** (`<task_commit_protocol>`). Follow that section for staging, `{type}({phase}-{plan})` messages, `commit-to-subrepo` when `sub_repos` is set, post-commit checks, and untracked-file handling β do not duplicate or paraphrase the full protocol here (single source of truth).
**Orchestrator note:** After each task, the spawned executor reports commit hashes; this workflow does not re-specify commit semantics beyond pointing at the executor.
</task_commit>
<step name="checkpoint_protocol">
On `type="checkpoint:*"`: automate everything possible first. Checkpoints are for verification/decisions only.
Display: `CHECKPOINT: [Type]` box β Progress {X}/{Y} β Task name β type-specific content β `YOUR ACTION: [signal]`
| Type | Content | Resume signal |
|------|---------|---------------|
| human-verify (90%) | What was built + verification steps (commands/URLs) | "approved" or describe issues |
| decision (9%) | Decision needed + context + options with pros/cons | "Select: option-id" |
| human-action (1%) | What was automated + ONE manual step + verification plan | "done" |
After response: verify if specified. Pass β continue. Fail β inform, wait. WAIT for user β do NOT hallucinate completion.
See /Users/theogengineer/Projects/Multilingual-Absa/.opencode/gsd-core/references/checkpoints.md for details.
</step>
<step name="checkpoint_return_for_orchestrator">
When spawned via Task and hitting checkpoint: return structured state (cannot interact with user directly).
**Required return:** 1) Completed Tasks table (hashes + files) 2) Current Task (what's blocking) 3) Checkpoint Details (user-facing content) 4) Awaiting (what's needed from user)
Orchestrator parses β presents to user β spawns fresh continuation with your completed tasks state. You will NOT be resumed. In main context: use checkpoint_protocol above.
</step>
<step name="verification_failure_gate">
If verification fails:
**Check if node repair is enabled** (default: on):
```bash
NODE_REPAIR=$(gsd_run query config-get workflow.node_repair 2>/dev/null || echo "true")
```
If `NODE_REPAIR` is `true`: invoke `@./.opencode/gsd-core/workflows/node-repair.md` with:
- FAILED_TASK: task number, name, done-criteria
- ERROR: expected vs actual result
- PLAN_CONTEXT: adjacent task names + phase goal
- REPAIR_BUDGET: `workflow.node_repair_budget` from config (default: 2)
Node repair will attempt RETRY, DECOMPOSE, or PRUNE autonomously. Only reaches this gate again if repair budget is exhausted (ESCALATE).
If `NODE_REPAIR` is `false` OR repair returns ESCALATE: STOP. Present: "Verification failed for Task [X]: [name]. Expected: [criteria]. Actual: [result]. Repair attempted: [summary of what was tried]." Options: Retry | Skip (mark incomplete) | Stop (investigate). If skipped β SUMMARY "Issues Encountered".
</step>
<step name="record_completion_time">
```bash
PLAN_END_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
PLAN_END_EPOCH=$(date +%s)
DURATION_SEC=$(( PLAN_END_EPOCH - PLAN_START_EPOCH ))
DURATION_MIN=$(( DURATION_SEC / 60 ))
if [[ $DURATION_MIN -ge 60 ]]; then
HRS=$(( DURATION_MIN / 60 ))
MIN=$(( DURATION_MIN % 60 ))
DURATION="${HRS}h ${MIN}m"
else
DURATION="${DURATION_MIN} min"
fi
```
</step>
<step name="generate_user_setup">
```bash
grep -A 50 "^user_setup:" .planning/phases/XX-name/{phase}-{plan}-PLAN.md | head -50
```
If user_setup exists: create `{phase}-USER-SETUP.md` using template `/Users/theogengineer/Projects/Multilingual-Absa/.opencode/gsd-core/templates/user-setup.md`. Per service: env vars table, account setup checklist, dashboard config, local dev notes, verification commands. Status "Incomplete". Set `USER_SETUP_CREATED=true`. If empty/missing: skip.
</step>
<step name="create_summary">
**Critical ordering β write and commit SUMMARY.md as one atomic block.** Do NOT
emit narrative output between the Write tool call and the commit tool call.
Truncation at this boundary is a known failure mode (see #2070 rescue logic in
execute-phase.md step 5.5).
Create `{phase}-{plan}-SUMMARY.md` at `.planning/phases/XX-name/`. Use `/Users/theogengineer/Projects/Multilingual-Absa/.opencode/gsd-core/templates/summary.md`.
**Frontmatter:** phase, plan, subsystem, tags | requires/provides/affects | tech-stack.added/patterns | key-files.created/modified | key-decisions | requirements-completed (**MUST** copy `requirements` array from PLAN.md frontmatter verbatim) | duration ($DURATION), completed ($PLAN_END_TIME date).
Title: `# Phase [X] Plan [Y]: [Name] Summary`
One-liner SUBSTANTIVE: "JWT auth with refresh rotation using jose library" not "Authentication implemented"
Include: duration, start/end times, task count, file count.
Next: more plans β "Ready for {next-plan}" | last β "Phase complete, ready for next step".
</step>
<step name="update_current_position">
**Skip this step if running in parallel mode** (the orchestrator in execute-phase.md
handles STATE.md/ROADMAP.md updates centrally after merging worktrees to avoid
merge conflicts).
Update STATE.md using gsd-tools.cjs query (or legacy gsd-tools) state mutations:
```bash
# Auto-detect parallel mode: .git is a file in worktrees, a directory in main repo
IS_WORKTREE=$([ -f .git ] && echo "true" || echo "false")
# Skip in parallel mode β orchestrator handles STATE.md centrally
if [ "$IS_WORKTREE" != "true" ]; then
# Advance plan counter (handles last-plan edge case)
gsd_run query state.advance-plan
# Recalculate progress bar from disk state
gsd_run query state.update-progress
# Record execution metrics
gsd_run query state.record-metric \
--phase "${PHASE}" --plan "${PLAN}" --duration "${DURATION}" \
--tasks "${TASK_COUNT}" --files "${FILE_COUNT}"
fi
```
</step>
<step name="extract_decisions_and_issues">
From SUMMARY: Extract decisions and add to STATE.md:
```bash
# Add each decision from SUMMARY key-decisions
# Prefer file inputs for shell-safe text (preserves `$`, `*`, etc. exactly)
gsd_run query state.add-decision \
--phase "${PHASE}" --summary-file "${DECISION_TEXT_FILE}" --rationale-file "${RATIONALE_FILE}"
# Add blockers if any found
gsd_run query state.add-blocker --text-file "${BLOCKER_TEXT_FILE}"
```
</step>
<step name="update_session_continuity">
Update session info using gsd-tools.cjs query (or legacy gsd-tools):
```bash
gsd_run query state.record-session \
--stopped-at "Completed ${PHASE}-${PLAN}-PLAN.md" \
--resume-file "None"
```
Keep STATE.md under 150 lines.
</step>
<step name="issues_review_gate">
If SUMMARY "Issues Encountered" β "None": yolo β log and continue. Interactive β present issues, wait for acknowledgment.
</step>
<step name="update_roadmap">
Run this step only when NOT executing inside a git worktree (i.e.
`use_worktrees: false`, the bug #2661 reproducer). In worktree mode each
worktree has its own ROADMAP.md, so per-plan writes here would diverge
across siblings; the orchestrator owns the post-merge sync centrally
(see execute-phase.md Β§5.7, single-writer contract from #1486 / dcb50396).
```bash
# Auto-detect worktree mode: .git is a file in worktrees, a directory in main repo.
# This mirrors the use_worktrees config flag for the executing handler.
IS_WORKTREE=$([ -f .git ] && echo "true" || echo "false")
if [ "$IS_WORKTREE" != "true" ]; then
# use_worktrees: false β this handler is the sole post-plan sync point (#2661)
gsd_run query roadmap.update-plan-progress "${PHASE}"
fi
```
Counts PLAN vs SUMMARY files on disk. Updates progress table row with correct count and status (`In Progress` or `Complete` with date).
</step>
<step name="update_requirements">
Mark completed requirements from the PLAN.md frontmatter `requirements:` field:
```bash
gsd_run query requirements.mark-complete ${REQ_IDS}
```
Extract requirement IDs from the plan's frontmatter (e.g., `requirements: [AUTH-01, AUTH-02]`). If no requirements field, skip.
</step>
<step name="git_commit_metadata">
**Critical ordering β write and commit SUMMARY.md as one atomic block.** Do NOT
emit narrative output between the Write tool call and the commit tool call.
Truncation at this boundary is a known failure mode (see #2070 rescue logic in
execute-phase.md step 5.5).
Task code already committed per-task. Commit plan metadata:
```bash
# Auto-detect parallel mode: .git is a file in worktrees, a directory in main repo
IS_WORKTREE=$([ -f .git ] && echo "true" || echo "false")
# In parallel mode: exclude STATE.md and ROADMAP.md (orchestrator commits these)
if [ "$IS_WORKTREE" = "true" ]; then
gsd_run query commit "docs({phase}-{plan}): complete [plan-name] plan" --files .planning/phases/XX-name/{phase}-{plan}-SUMMARY.md .planning/REQUIREMENTS.md
else
gsd_run query commit "docs({phase}-{plan}): complete [plan-name] plan" --files .planning/phases/XX-name/{phase}-{plan}-SUMMARY.md .planning/STATE.md .planning/ROADMAP.md .planning/REQUIREMENTS.md
fi
```
</step>
<step name="update_codebase_map">
If .planning/codebase/ doesn't exist: skip.
```bash
FIRST_TASK=$(git log --oneline --grep="feat({phase}-{plan}):" --grep="fix({phase}-{plan}):" --grep="test({phase}-{plan}):" --reverse | head -1 | cut -d' ' -f1)
git diff --name-only ${FIRST_TASK}^..HEAD 2>/dev/null || true
```
Update only structural changes: new src/ dir β STRUCTURE.md | deps β STACK.md | file pattern β CONVENTIONS.md | API client β INTEGRATIONS.md | config β STACK.md | renamed β update paths. Skip code-only/bugfix/content changes.
```bash
gsd_run query commit "" --files .planning/codebase/*.md --amend
```
</step>
<step name="offer_next">
If `USER_SETUP_CREATED=true`: display `β οΈ USER SETUP REQUIRED` with path + env/config tasks at TOP.
```bash
(ls -1 .planning/phases/[current-phase-dir]/*-PLAN.md 2>/dev/null || true) | wc -l
(ls -1 .planning/phases/[current-phase-dir]/*-SUMMARY.md 2>/dev/null || true) | wc -l
```
| Condition | Route | Action |
|-----------|-------|--------|
| summaries < plans | **A: More plans** | Find next PLAN without SUMMARY β skip any plan whose `plan_id` matches a non-terminal async-job manifest (`external_job_waiting`; see `identify_plan`). Yolo: auto-continue. Interactive: show next plan, suggest `/gsd-execute-phase {phase}` + `/gsd-verify-work`. STOP here. |
| summaries = plans, current < highest phase | **B: Phase done** | Show completion, suggest `/gsd-plan-phase {Z+1}` + `/gsd-verify-work {Z}` + `/gsd-discuss-phase {Z+1}` |
| summaries = plans, current = highest phase | **C: Milestone done** | Show banner, suggest `/gsd-complete-milestone` + `/gsd-verify-work` + `/gsd-add-phase` |
All routes: `/clear` first for fresh context.
</step>
</process>
<success_criteria>
- All tasks from PLAN.md completed
- All verifications pass
- USER-SETUP.md generated if user_setup in frontmatter
- SUMMARY.md created with substantive content
- STATE.md updated (position, decisions, issues, session) β unless parallel mode (orchestrator handles)
- ROADMAP.md updated β unless parallel mode (orchestrator handles)
- If codebase map exists: map updated with execution changes (or skipped if no significant changes)
- If USER-SETUP.md created: prominently surfaced in completion output
</success_criteria>
|