Open a unit of work cleanly — resolve or create the tracked issue, cut a feature branch off up-to-date main, set up a git worktree when config calls for it, and initialize the STM workspace. The start-of-pipeline play in the ProductOS command model. Use when beginning work on a change, starting an issue, or kicking off a feature.
Scanned 9/10/2026
Install to Claude Code
npx -y skills add kapilvirenahuja/garura --skill start-change --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Start Change?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/kapilvirenahuja-start-change)More formats (shields.io, HTML) on the badges page.
---
name: start-change
position: start
description: 'Open a unit of work cleanly — resolve or create the tracked issue, cut a feature branch off up-to-date main, set up a git worktree when config calls for it, and initialize the STM workspace. The start-of-pipeline play in the ProductOS command model. Use when beginning work on a change, starting an issue, or kicking off a feature.'
user-invocable: true
---
# start-change
Open a unit of work cleanly: ensure a tracked issue, a feature branch cut from an
up-to-date main, a git worktree when config calls for it, and an initialized STM
workspace — so every play that runs after start-change begins from a known, identified
context.
**Pipeline position: start.** start-change is the start-of-pipeline primitive. The D2
pipeline-position rule injects it into any other play declared `position: start`. As the
primitive itself, start-change does not get a start composed into it.
## Compiled From
This play was compiled from the start-change ICE (`reference/ice.md`) by play-editor
(#466 Batch A, Level 3 rollout per ADR 025; #467 Batch C gate-off recompile; #484 scripted
chain). Intent defines constraints (C1–C8) and failure conditions (F1–F8); the expectation
defines success scenarios (S1–S4), a Done means (D1–D4, baked to `stop-condition.yaml`), and
one recovery entry per failure condition.
To modify this play, update `reference/ice.md` and recompile with play-editor.
Do NOT edit this file manually — it is a compiled artifact.
## Role
You are the orchestrator. You own the workflow and the step order. **Issue** work goes to a
utility agent (the fresh-issue match from a description is genuine judgment); the mechanical
**branch** work runs in a bundled script calling git directly, not an agent (C8, #484). You
never reason out git/gh inline in play prose.
**Forbidden:** running the mechanical branch work (cut/worktree/push) through an agent
dispatch (the #484 bug); ad-hoc `gh`/`git` in play prose; reimplementing issue logic inline.
**Agent boundaries:**
| Agent | Domain | Skill it invokes | Phases |
|-------|--------|------------------|--------|
| `project-orchestrator` | Issues: resolve an existing issue, create a new one (fresh-match judgment) | `manage-issue` | Execution |
`project-orchestrator` is a **utility agent** (issue plumbing + the fresh-issue match),
exempt from the domain-agent budget. The branch work is the bundled `setup_branch.py` — no
agent (C8). This play uses **zero domain agents**.
## Pre-flight
| Check | Constraint | Action on Failure |
|-------|-----------|-------------------|
| Resolve working base + config (`.garura/core/config.yaml`) | — | Hard halt |
| Git repository present | C2 | Hard halt |
| Platform CLI available (`gh`) | C1 | Hard halt |
| Read `start-change.worktree` flag (default `false`) | C3 | — |
| Resume marker check for this issue's start-change | C6 | Resume |
Resolve the deterministic pre-flight facts with the bundled resolver — not by inference. The
orchestrator captures the current branch and passes it in:
```
python3 scripts/preflight.py --play start-change \
--config .garura/core/config.yaml --branch "$(git branch --show-current)"
```
It returns the facts as JSON — `stm_base`, `worktree` (the `start-change.worktree` flag,
absent ⇒ false), `branch`, `issue` (null when starting fresh), `on_default_branch`,
`evidence_record`, `ltm_project_target`. The orchestrator holds these for every later path;
the table keeps the policy. The live checks (git repo present, `gh` available) stay as the
orchestrator's environment reads, not the resolver.
**Resume check (C6, F5):** if an issue is already known (passed in, or a status marker /
existing `feature/<issue>-*` branch is found), resume — switch to the existing branch,
reuse the existing STM, and do NOT create a second issue or branch. Skip completed steps,
reset any in-progress step to pending, continue from the first incomplete one.
## Task DAG
Create ALL tasks immediately after resolving the working base — before any domain work.
The play owns this DAG; agents must not edit its top-level tasks.
```
[T1] Resolve Issue blockedBy: []
[T2] Confirm New Issue blockedBy: [T1]
[T3] Create / Finalize Issue blockedBy: [T2]
[T4] Set Up Branch + Worktree blockedBy: [T3]
[T5] Initialize STM Workspace blockedBy: [T4]
[T6] Scenario Validation blockedBy: [T5]
[T7] Close blockedBy: [T6]
```
Mark each task in-progress before its step and completed right after its eval passes.
No runtime reordering. On resume, skip completed and reset in-progress to pending.
## Workflow
### Phase: Execution
**Step 1 — Resolve Issue** · Owner: `project-orchestrator` · Depends on: pre-flight
Dispatch a JSON contract; the agent invokes `manage-issue` (`action: resolve`) to find
an existing issue matching the work (or the issue number passed in), and writes the
result to disk. The contract moves only paths, never data:
{
"task": "resolve an existing tracked issue for this change (do not create)",
"inputs": { "description": "<working>/work-description.txt",
"issue_number": "<optional, if provided>" },
"outputs": { "issue": "<working>/issue.json" }
}
`issue.json` records `{ resolved: true|false, issue_number, title, created: false }`.
**Step 2 — Confirm New Issue** · Owner: play · Depends on: Step 1
*Skip when `issue.json.resolved == true`.* When no issue was resolved, creating one is an
outward-facing action (class: standard). Resolve the gate switch per
`standards/rules/gate-config.md` FIRST:
- **off** (this project: `gates.plays.start-change: off`) → do not wait; record
`gate skipped by config (gates.plays.start-change)` as a Checkpoint Decisions row in
evidence and proceed. A skip is always recorded, never silent.
- **on** → present the proposed new issue (title + summary) and wait for a typed
approval. Approve → continue to Step 3; cancel → halt.
**Either way — the machine wall (C1/F7), never gated by the switch:** before Step 3
creates anything, run the bundled validator on the proposal record:
```
python3 scripts/validate_issue.py \
--issue-json <working>/issue.json \
--description <working>/work-description.txt
```
It checks the title is present, ≥15 chars, and tagged (`[ENH]/[BUG]/[FEAT]/[DOCS]/[TASK]`
or a conventional prefix), and that the work description is complete. A non-zero exit is
a **hard halt** regardless of the gate state — REC7 hands to a human (fix the
title/description, re-run). This is the deterministic check that replaced the human eye
when the gate went off (#467).
**Step 3 — Create / Finalize Issue** · Owner: `project-orchestrator` · Depends on: Step 2
When Step 1 resolved an issue, carry it forward unchanged. When Step 2 approved a new
issue, the agent invokes `manage-issue` (`action: create`) and writes the finalized
`issue.json` (`created: true`, `issue_number` set).
**SE-1 (F1/C1):** `issue.json` has a non-empty `issue_number` (an issue is anchored
before any branch work); on resume, `created == false` and no new issue exists (F5).
**SE-8 (F7/C1):** `validate_issue.py` ran BEFORE the create (its verdict predates
`created: true`), and it exits 0 when re-run on the created issue's record — title
present/tagged, description complete. A non-zero exit at either point halted the run
to a human (REC7); no issue exists over a failed validation.
**Step 4 — Set Up Branch + Worktree** · Owner: play (script) · Depends on: Step 3
Run the bundled script — it pulls `main` to its latest origin tip, cuts
`feature/<issue_number>-<slug>` from it (or switches to it if it already exists), sets up a
git worktree **iff** `start-change.worktree` is true, pushes, and writes `branch.json`. No
agent (C8):
```
python3 scripts/setup_branch.py --issue <issue_number> \
--branch-name "feature/<issue_number>-<slug>" --base main \
$( [ "<start-change.worktree>" = "true" ] && echo --worktree ) \
--out "<working>/branch.json"
```
`branch.json` records `{ branch_name, base_sha, on_default_branch: false, worktree_path|null }`.
**SE-2 (F2/C2):** current branch matches `feature/<issue_number>-*`, is not `main`, and
`branch.json.base_sha` equals the current `origin/main` tip.
**SE-3 (F3/C3):** `git worktree list` shows a worktree for the branch **iff**
`start-change.worktree` is true (present when on, absent when off).
**SE-6 (C5):** issue work went through the `manage-issue` skill (`issue.json` exists) and
the branch work through the bundled `setup_branch.py` (`branch.json` exists) — no inline
`gh`/`git` in play prose.
**SE-9 (F8/C8):** the branch cut/worktree/push ran as `setup_branch.py`, not an agent
dispatch — no `repo-orchestrator`/`setup-branch` was invoked for the branch work.
**Step 5 — Initialize STM Workspace** · Owner: play · Depends on: Step 4
Record the session identity stamp's start marker first (#463 — soft-fail, never a halt); the stamp start runs here rather than at pre-flight because pre-flight has no issue yet:
```
python3 scripts/session_stamp.py --phase start \
--marker "{stm_base}{issue}/status/session-stamp-start-change.json" \
--cwd "$(pwd)" --branch "$(git branch --show-current)"
```
Run the mechanical script (it creates the five permitted structure keys idempotently):
```
python3 scripts/init_stm.py --stm-base <stm.base-path> --issue <issue_number>
```
Then commit the run artifacts on the fresh feature branch (C7) — play-owned mechanical
work, run as a command: the workspace opens with a clean tree and its own record
(issue.json, branch.json, work-description) aboard:
```
git add "{stm_base}{issue}/context/"
git commit -m "chore(stm): record start-change workspace context (#<issue>)"
```
**SE-4 (F4/C4):** `<stm.base-path>/<issue_number>/` exists and contains the five
structure dirs (`specs`, `evidence`, `checkpoint`, `context`, `review`) before close.
### Phase: Scenario Validation
**Step 6 — Scenario Evals** · Owner: play · Depends on: Step 5
- **SCE-1 (S1 — developer, fresh start):** a new issue, a `feature/<issue>-*` branch off
the current `origin/main` tip, and the STM workspace all exist; the run artifacts are
committed on the branch; the stop-condition verdict reads held.
- **SCE-2 (S2 — developer, existing issue):** the provided issue was resolved (no new
issue created), its branch exists, and STM exists; the run artifacts are committed on
the branch; the stop-condition verdict reads held.
- **SCE-3 (S3 — developer, resume):** on a re-run, issue count and branch count are
unchanged and the session ends on the existing branch.
- **SCE-4 (S4 — developer, worktree by config):** `git worktree list` shows the branch's
worktree when `start-change.worktree` is true, and shows none when it is false.
- **SE-5 (F5/C6):** a re-run for the same issue created neither a duplicate issue nor a
duplicate branch (counts unchanged vs. before the run).
### Phase: Evidence & Close
**Step 7 — Close** · Owner: play · Depends on: Step 6
Run the Standard Play Close. It records evidence (when recording is on) and renders the
user-facing delivery report. Evidence recording is play-only and config-gated per the D1
evidence rule (`standards/rules/evidence-recording.md`).
**SE-7 (F6/C7):** the stop-condition verdict is held before the run closes COMPLETED;
the run artifacts are committed on the feature branch.
```bash
# --- Standard Play Close (canonical; see standards/rules/play-close.md) ---
# Path tokens resolved at pre-flight (resolve here if not already):
# ltm_project_target = yq '.ltm.project-target' .garura/core/config.yaml
# evidence_base, slug (project-scoped play):
# evidence_base="${stm_base}${issue}/evidence/start-change/" ; slug="#${issue}"
evidence_template=$(cat "${ltm_project_target}standards/templates/evidence-file.md")
delivery_template=$(cat "${ltm_project_target}standards/templates/delivery-report.md")
ts=$(date -u +%Y%m%d-%H%M%S)
evidence_dest="${evidence_base}${ts}.md"
mkdir -p "$(dirname "$evidence_dest")"
# Session identity stamp (#463) — close phase; start phase ran at Step 5
session_stamp=$(python3 scripts/session_stamp.py --phase close \
--marker "${stm_base}${issue}/status/session-stamp-start-change.json")
# Stop-condition gate (#464) — Step C0: this play carries a baked manifest, so the
# gate is LIVE. Evaluate the Done means here as the close's authoritative input.
python3 scripts/check_stop_condition.py \
--manifest "<play-dir>/stop-condition.yaml" \
--base "${stm_base}${issue}/" \
--out "${stm_base}${issue}/status/stop-condition-start-change.yaml"
sc_exit=$? # 0 held · 1 unmet · 2 error
```
**Step C0 — bind the verdict.** `sc_exit == 0` (held) permits `status: COMPLETED`.
Anything else closes `HALTED` with `exit_reason: stop_condition_unmet` and the
evidence's Stop Condition section names every unmet clause. An unevaluable verdict is
never a pass.
**Step C1 — Write evidence file.** Gated by the resolved `evidence.record` flag
(global + per-play `evidence.plays.start-change`; first match wins, absent ⇒ record).
When it resolves false, skip the write and record `evidence skipped (record=false)` in
the delivery report's pointer line. Otherwise fill the `evidence-file.md` slots (play
`start-change`, run_id `start-change-${ts}`, issue, started_at/completed_at, status,
artifacts produced: `issue.json`, `branch.json`, the STM workspace; step/scenario eval
results; checkpoint decisions from Step 2; commit reference `N/A — no commit step`; and
the session identity stamp fields from $session_stamp (#463): session_id, ledger_file,
ledger_start_offset, ledger_end_offset (null when unresolved — never blocks the close)) and
write to `$evidence_dest`. Do NOT hand-author the body.
**Step C2 — Render delivery report.** Also render the **Next** line: resolve this play in `standards/rules/pipeline-next.md` and emit `**Next:** /<command> — <why>. Or run /next to see all recommended actions.` (only /next pointer, or omit, when the mapped command is null), per `play-close.md`. Fill the `delivery-report.md` slots and output the
report to the user: `## start-change Delivered — #${issue}`, the Run Summary table, the
Pipeline Steps table built from the task DAG, the Artifacts Produced table, Next Steps
(the change is ready — run the build/implement play next), and a pointer to
`$evidence_dest`. Always emitted; never gated.
```bash
# --- end Standard Play Close ---
```
## Scenario Validation
The success scenarios and their evals (run in Step 6):
| Scenario | Persona | Eval |
|----------|---------|------|
| S1 — fresh start | developer | SCE-1 |
| S2 — existing issue | developer | SCE-2 |
| S3 — resume | developer | SCE-3 |
| S4 — worktree by config | developer | SCE-4 |
## Recovery
| For | Trigger | Direction | Handoff |
|-----|---------|-----------|---------|
| F1 | no issue resolved or created before branch work | create the issue from the work description via `manage-issue`, then continue | autonomous |
| F2 | branch base is not the latest main, or current branch is `main` | pull main to latest and re-cut the branch from it, or move off main | autonomous |
| F3 | worktree state does not match `start-change.worktree` | reconcile to config — remove the stray worktree, or create the missing one | autonomous |
| F4 | close check finds the STM workspace missing | run `scripts/init_stm.py` to initialize it before reporting done | autonomous |
| F5 | a second run would create a duplicate issue or branch | detect the existing issue and branch and resume them instead of creating new ones | autonomous |
| F6 | the close would report COMPLETED without the Done means held | evaluate the stop condition and surface the unmet clauses; the run closes HALTED until state is fixed | autonomous |
| F7 | `validate_issue.py` exits non-zero on the proposed or created issue record | a human fixes the title (present, ≥15 chars, tagged) and/or completes the work description, then re-runs; the play never creates over a failed validation | human |
| F8 | the mechanical branch work ran through an agent instead of `setup_branch.py` | route the cut/worktree/push through `setup_branch.py` and remove the agent dispatch | autonomous |
## Pause and Resume
Steps run top to bottom. On entry, resolve the issue (from input, status marker, or an
existing `feature/<issue>-*` branch), check the status marker, skip completed steps,
reset any in-progress step to pending, and continue. A fresh start with no marker runs
everything and creates the marker at Step 1. Resuming never duplicates the issue or
branch (C6, F5).
## Compilation Metadata
| Field | Value |
|-------|-------|
| fingerprint | sha256:56a552759a176c2d7ed0e176c9822276d8031b36797a0d5617007c7831e495b9 (of `reference/ice.md`) |
| compiled_by | play-editor (#466 Batch A; #467 Batch C; #484 scripted chain) |
| pipeline_position | start |
| workflow_structure | B (fast execution flow) |
| stop_condition | stop-condition.yaml (D1–D4), gate live at Step C0 |
| domain_agents | 0 |
| utility_agents | 1 (project-orchestrator — issue work + fresh-match judgment) |
| skills_reused | manage-issue |
| scripts | 6 (init_stm.py, preflight.py, session_stamp.py, check_stop_condition.py, validate_issue.py, setup_branch.py) |
| step_evals | 9 (SE-1…SE-9) |
| scenario_evals | 4 (SCE-1…SCE-4) |
| recovery_entries | 8 (one per failure condition; 7 autonomous / 1 human) |
## Recompile note (#484, scripted chain)
Intent change via `reference/ice.md` → recompile. Reworded C5 (issue work via
`manage-issue`; branch work is not a skill/agent) and added C8 (mechanical branch work runs
in the bundled `setup_branch.py`, never agent dispatch), F8, REC8, SE-9. Step 4 no longer
dispatches `repo-orchestrator` → `setup-branch`; it runs `setup_branch.py` (canonical copy
in `play-creator/references/`, stamped here). Issue work keeps `project-orchestrator` for
the fresh-issue match (genuine judgment). Utility agents 2 → 1; scripts 5 → 6. Guarantees
(issue-anchored, branch off latest main, worktree per config, resume-safe, Done-means
proven) unchanged. Fingerprint recomputed over the edited ICE.
## Direct-edit deviation note (#434, pre-flight resolver)
Pre-flight resolution moved from orchestrator inference to the bundled `scripts/preflight.py`
(harness-led: config/branch/issue/worktree/changeset resolution is a script returning JSON
facts; the play keeps only the halt policy; the live git-repo and `gh` checks stay in the
table). The script is the canonical resolver stamped from
`play-creator/references/preflight.py`; a rebuild reproduces it (play-creator step 4).
Non-intent change — no constraint, failure, scenario, eval, or `reference/ice.md` touched, so
the fingerprint stands and no recompile is required. Direct edit; no recompile needed.
## Compiled note (#467 Batch C)
The confirm-new-issue gate went **off by config** for this project
(`gates.plays.start-change: off`) in the same change that added its replacement machine
check: `scripts/validate_issue.py` (canonical copy:
`play-creator/references/validate_issue.py`) runs before any issue create, gate on or
off, and a non-zero exit hard-halts to a human (C1, F7, SE-8, REC7). The checkpoint
machinery is KEPT — the switch resolves per `standards/rules/gate-config.md`; flipping
the config back to on restores the wait; skips are always recorded in evidence. Owned
by the compiled play (ICE recompile, not a direct edit).
## Direct-edit deviation note (#463, session identity stamp)
Non-intent change: the Standard Play Close gained the session identity stamp — `scripts/session_stamp.py` (canonical copy: `play-creator/references/session_stamp.py`) runs `--phase start` at pre-flight and `--phase close` in the close block; the evidence frontmatter carries session_id / ledger_file / ledger_start_offset / ledger_end_offset. Source of truth: `standards/rules/play-close.md`; play-creator emits the same lines so a rebuild converges. No constraint, failure, scenario, or eval changed; the fingerprint stands.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!