Skip to content
Back to skills

Start Change

ASecurity

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.

  • 3 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 10, 2026
toolspythongobashgit

Works with

  • cli

Security analysis

A100/100

Pro scans all 9 files and shows the line behind each finding

Scanned September 10, 2026

npx -y skills add kapilvirenahuja/garura --skill start-change --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Start Change?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Start Change
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/kapilvirenahuja-start-change/badge)](https://www.skillsdirectory.com/skills/kapilvirenahuja-start-change)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
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.

Files in this skill

  • SKILL.md19.7 KB
  • reference/ice.md6.8 KB
  • scripts/check_stop_condition.py5 KB
  • scripts/init_stm.py2.1 KB
  • scripts/preflight.py6.4 KB
  • scripts/session_stamp.py4.9 KB
  • scripts/setup_branch.py3.2 KB
  • scripts/validate_issue.py3 KB
  • stop-condition.yaml789 B

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…