Skip to content
Back to skills

Commit Change

ASecurity

Commit all uncommitted work on the feature branch, grouped by concern, with conventional messages that reference the issue — leaving a clean tree ready to raise. Commits only; does not push. The first step of the end sequence in the ProductOS change pipeline. Use when work is done and needs committing before propose-change.

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

Security analysis

A100/100

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

Scanned September 10, 2026

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

Installs into .claude/skills of the current project.

Are you the author of Commit Change?

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

Security grade badge for Commit Change
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/kapilvirenahuja-commit-change/badge)](https://www.skillsdirectory.com/skills/kapilvirenahuja-commit-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: commit-change
position: end
description: 'Commit all uncommitted work on the feature branch, grouped by concern, with conventional messages that reference the issue — leaving a clean tree ready to raise. Commits only; does not push. The first step of the end sequence in the ProductOS change pipeline. Use when work is done and needs committing before propose-change.'
user-invocable: true
---

# commit-change

Commit all uncommitted work on the feature branch, grouped by concern, with conventional
messages that reference the tracked issue — leaving a clean tree ready to raise. This play
**commits only; it does not push** (propose-change pushes when it opens the PR).

**Pipeline position: end.** commit-change is the first step of the end sequence
(commit-change → propose-change → review-change → merge-change) that the D2 pipeline-position
rule injects into any play declared `position: end`. Positions are start and end only —
there is no middle.

**The pilot (#465).** This is the first play compiled on the Level 3 model (ADR 025):
the skeleton — pre-flight, one loop, conditional checkpoint, gated close — is deterministic
and guaranteed; the interior of the loop is the agent's, bounded by four walls: the baked
stop condition (`stop-condition.yaml`), the bundled scripts as the checker, an iteration
cap of 5, and the run's evidence. The play ends by PROVING its Done means (C9), never by a
step list running out.

## Compiled From

This play was compiled from the commit-change ICE (`reference/ice.md`) by play-editor
(#465, Level 3 pilot recompile; #467 Batch C gate-off recompile). Intent defines
constraints (C1–C9) and failure conditions (F1–F9); the expectation defines success
scenarios (S1–S6), 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 skeleton — pre-flight, the loop's walls, the
checkpoint policy, the gated close — and the agent owns the interior of each round.
Mechanical work runs as bundled scripts (C8): the changeset scan and triviality
classification (`scripts/analyze_changeset.py`), commit execution over the decided plan
(`scripts/execute_commits.py`), and the stop-condition evaluation
(`scripts/check_stop_condition.py`). Inference is reserved for the one genuine judgment —
grouping a multi-concern changeset — delegated to `repo-orchestrator` (invoking
`analyze-changes`) via a JSON contract over files on disk. Issue resolution, when not
derivable from the branch, goes to `project-orchestrator` (`resolve-issues`,
`manage-issue`). **You never push** — that is propose-change's job (C7).

**Forbidden:** ad-hoc `git`/`gh` commands reasoned out inline — git runs only inside the
bundled scripts or the dispatched skills; spawning an analyze agent for a changeset the
script classified trivial (C8/F7); any push (`git push`, `submit-pr`, or equivalent — the
execution script contains no push path); closing COMPLETED without the stop condition
held (C9); exceeding the iteration cap (F8).

**Agent boundaries:**

| Agent | Domain | Skill it invokes | Phases | Dispatched when |
|-------|--------|------------------|--------|-----------------|
| `repo-orchestrator` | Multi-concern grouping judgment | `analyze-changes` | Loop | `analyze_changeset.py` reports `needs_judgment: true` |
| `project-orchestrator` | Resolve change groups to the tracked issue | `resolve-issues`, `manage-issue` | Loop | issue not derivable from the branch |

Both are **utility agents**, exempt from the domain-agent budget. This play uses **zero
domain agents**, and on a trivial changeset it spawns **zero agents at all** (S5).

**The plan contract (C3, #465 defect 1).** The plan (`analysis.yaml`) carries each group's
subject BARE — no type prefix, no scope, no issue reference; those live in the group's
separate `commit_type` / `scope` / `issue` fields. The executor renders the conventional
message and defensively normalizes a subject that arrives pre-formatted (strips a doubled
prefix or trailing issue ref) instead of doubling it.

## Pre-flight

| Check | Constraint | Action on Failure |
|-------|-----------|-------------------|
| Resolve working base + config (`.garura/core/config.yaml`) | — | Hard halt |
| Branch guard (not on `main`/default) | C1 | Hard halt |
| Changes exist (`git status --porcelain` non-empty) | C5 | Graceful exit |
| Sensitive-file scan (loop's scan script, `analyze_changeset.py` patterns) | C6 | Hard block |
| Issue resolvable (from the `feature/<issue>-*` branch, else open issues) | C3 | Hard halt |

Resolve the pre-flight facts mechanically with the bundled resolver — do not derive them by
inference. The orchestrator captures the two live reads and hands them to the script:

```
git branch --show-current                          # current branch
git status --porcelain > <working>/porcelain.txt    # changeset
python3 scripts/preflight.py --play commit-change \
    --config .garura/core/config.yaml \
    --branch "<current branch>" \
    --porcelain-file <working>/porcelain.txt
```

It returns one JSON object of facts: `stm_base`, `ltm_project_target`, `evidence_record`,
`platform`, `branch`, `issue`, `on_default_branch`, `changes_present`. The orchestrator reads
those and applies the **policy** in the table above (the script resolves facts, the play
decides halts): `on_default_branch == true` → hard halt (C1); `changes_present == false` →
graceful exit (C5); a non-null `issue` short-circuits issue resolution
(`auto_issue_resolved = true`) and skips the checkpoint (C3). The sensitive-file scan runs
inside the loop's scan script (same porcelain file), not the resolver.

Right after the resolver, record the session identity stamp's start marker (#463 —
soft-fail, never a halt):

```
python3 scripts/session_stamp.py --phase start \
    --marker "{stm_base}{issue}/status/session-stamp-commit-change.json" \
    --cwd "$(pwd)" --branch "$(git branch --show-current)"
```

**Resume check:** if `{stm_base}/{issue}/status/commit-change.json` exists, resume — the
loop re-enters at the next round with the recorded round count; the cap counts all rounds
across resumes.

## 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. The DAG is the skeleton:
the loop is ONE task, however many rounds it takes (C9 — the interior is the agent's).

```
[T1] Commit Loop (≤5 rounds)      blockedBy: []
[T2] Scenario Validation          blockedBy: [T1]
[T3] Close (stop-condition gated) blockedBy: [T2]
```

Mark each task in-progress before its phase and completed right after its eval passes. On
resume, the loop task re-enters with its round counter.

## Workflow

### Phase: The Loop

**Loop — Commit until Done means holds** · Owner: play (scripts) + agent on judgment ·
Depends on: pre-flight · **Cap: 5 rounds (C9/F8)**

Each round the agent decides its own moves toward the goal — *every change committed or
excluded-with-reason, tree clean, nothing pushed* — using the bundled scripts as its hands.
A round typically:

1. **Scan** — always the script first:

```
python3 scripts/analyze_changeset.py \
    --porcelain-file <working>/porcelain.txt \
    --issue {issue} --repo-root . \
    --out <working>/analysis.yaml
```

   Policy on its summary: `sensitive` (blocking) non-empty → **hard block** (C6/F5),
   human recovery (REC5) — the block is a machine wall, never gated by the checkpoint
   switch. `warnings` non-empty (content-pattern matches in prose/standards
   artifacts — pattern carriers, #438) → never a block; surfaced at the checkpoint when
   the gate is on, carried into the delivery report's Run Summary when it is off (F9).
   `needs_judgment: false` → the script's plan IS the plan; a `subject_source: script-draft`
   subject may be reworded in place (one line, no agent); **no agent is dispatched (C8)**.
   `needs_judgment: true` → dispatch `repo-orchestrator` → `analyze-changes` for the
   grouping judgment, standard JSON contract (subjects BARE per the plan contract):

```
    {
      "task":    "group the changeset by concern; flag sensitive/risky files",
      "inputs":  { "scan": "<working>/analysis.yaml" },
      "outputs": { "analysis": "<working>/analysis.yaml" }
    }
```

2. **Resolve issue** — *skip when derived from the branch (`auto_issue_resolved`)*;
   otherwise `project-orchestrator` invokes `manage-issue` + `resolve-issues` and writes
   `issue-mappings.yaml`.

3. **Checkpoint** — *only when the skip condition fails* (a sensitive flag, scanner
   warnings, or low-confidence mapping) (class: standard). When it fires, resolve the
   gate switch per `standards/rules/gate-config.md` FIRST:

   - **off** (this project: `gates.plays.commit-change: off`) → do not wait; record
     `gate skipped by config (gates.plays.commit-change)` as a Checkpoint Decisions row
     in evidence and proceed to execute. The off path drops nothing silently (C9/F9):
     scanner **warnings** are carried into the delivery report's Run Summary (a visible
     surface); a **low-confidence mapping** proceeds with the mapping recorded as
     low-confidence in `issue-mappings.yaml` and the run record — never silently
     upgraded. The **sensitive-file block** is untouched by the switch — it already
     hard-halted at the scan (a machine wall, never a gate).
   - **on** → present the brief — groups, mapping + confidence, risk flags, warnings —
     and wait for typed approval. Approve → execute; cancel → halt.

4. **Execute** — the decided plan, mechanically, run-state finalization included
   (#465 defects 2+3 — ignored paths become recorded exclusions; the play's own run
   artifacts land as the final chore group):

```
python3 scripts/execute_commits.py \
    --analysis <working>/analysis.yaml \
    --issue {issue} \
    --out <working>/commits.yaml \
    --run-state-dir <working>
```

5. **Prove or go again** — evaluate the stop condition:

```
python3 scripts/check_stop_condition.py \
    --manifest <play-dir>/stop-condition.yaml \
    --base "{stm_base}{issue}/" \
    --out "{stm_base}{issue}/status/stop-condition-commit-change.yaml"
```

   **Held (exit 0)** → leave the loop. **Unmet (exit 1)** → the verdict's unmet clauses
   are the next round's work list (fresh porcelain, plan the remainder); increment the
   round counter and sync it to the status marker. **Round 5 still unmet** → halt HALTED,
   `exit_reason: loop_cap_exhausted`, verdict recorded (F8/REC8 — human).

**Loop evals (each round's exit is checked, the loop's exit is proven):**
- **SE-1 (F1/C2):** each change group in `analysis.yaml` holds files of a single concern.
- **SE-2 (F2/C3):** each commit message is conventional `type(scope): subject (#issue)`
  with NO doubled prefix and exactly one issue reference — the executor normalized any
  pre-formatted subject.
- **SE-3 (F3/C4):** `commits.yaml` reads `leftover_count: 0` and `tree_clean: true`, or
  the loop is still running / halted with the verdict naming the leftovers. Ignored paths
  appear as recorded exclusions, never as add-failures.
- **SE-4 (F4/C1):** the commits were made on the feature branch, not `main`.
- **SE-5 (F5/C6):** no file flagged BLOCKING by the scan is included; a blocking flag
  halts the run. Prose/standards content matches are surfaced warnings, not blocks (#438).
- **SE-6 (F6/C7):** no push occurred — `commits.yaml` reads `pushed: false` and the remote
  tip is unchanged by this run.
- **SE-7 (F7/C8):** the dispatch matches the classification — `needs_judgment: false` and
  zero analyze-agent spawns, or `true` and exactly one (per round). A trivial-classified
  analysis whose group spans unrelated file sets triggers REC7.
- **SE-8 (F8/C9):** the loop exited by a held stop condition, or halted at the cap with
  the verdict recorded — the round counter never exceeds 5 and a cap-halt never closes
  COMPLETED.
- **SE-9 (F9/C9):** every scanner warning the run produced is visible somewhere — at a
  fired checkpoint (gate on) or in the delivery report's Run Summary Scanner Warnings
  row (gate off); a run with warnings and neither surface fails this eval (REC9
  re-renders the report).

### Phase: Scenario Validation

**Scenario Evals** · Owner: play · Depends on: the loop
- **SCE-1 (S1 — developer, multi-concern):** each concern is a separate conventional
  commit referencing the issue with no doubled prefix, the run artifacts are committed,
  the tree is clean, and nothing was pushed.
- **SCE-2 (S2 — developer, nothing to commit):** a clean tree caused a graceful exit at
  pre-flight with no commit created.
- **SCE-3 (S3 — reviewer, log readability):** every commit follows conventional format with
  an issue reference, reviewable from the log alone.
- **SCE-4 (S4 — developer, sensitive file):** a sensitive file blocked the run
  with no commit created.
- **SCE-5 (S5 — developer, trivial changeset):** a single-concern changeset ran entirely
  through the bundled scripts — plan and commits script-emitted, zero analyze/commit agent
  dispatches, same C2–C4 guarantees as the agent path.
- **SCE-6 (S6 — developer, loop convergence):** an unmet round fed its verdict to the next
  round as the work list, and the run converged (verdict held) or halted with the unmet
  clauses named and rounds ≤ 5.

### Phase: Evidence & Close

**Close** · Owner: play · Depends on: Scenario Validation
Run the Standard Play Close. Evidence recording is play-only and config-gated per the D1
evidence rule (`standards/rules/evidence-recording.md`).

```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/commit-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 pre-flight
session_stamp=$(python3 scripts/session_stamp.py --phase close \
    --marker "${stm_base}${issue}/status/session-stamp-commit-change.json")
# Stop-condition gate (#464) — Step C0: this play carries a baked manifest, so the
# gate is LIVE. The loop already wrote the verdict; re-read it 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-commit-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` (or
`loop_cap_exhausted` when the loop capped) 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.commit-change`; first match wins, absent ⇒ record). When false,
skip the write and record `evidence skipped (record=false)` in the report's pointer line.
Otherwise fill the `evidence-file.md` slots (play `commit-change`, run_id
`commit-change-${ts}`, issue, started_at/completed_at, status per C0, exit_reason;
artifacts produced: `analysis.yaml`, `issue-mappings.yaml`, `commits.yaml`, the
stop-condition verdict; step/scenario eval results; checkpoint decisions from the loop's
checkpoint rounds; commit reference = the created commit SHAs incl. the run-state commit;
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 stop_condition per C0 with the Stop Condition section filled) 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:
`## commit-change Delivered — #${issue}`, the Run Summary table (incl. rounds used, the
stop-condition verdict, and a **Scanner Warnings** row — every warning the scan produced,
carried here whenever the checkpoint did not fire or was skipped by config; `none` when
the scan was clean (F9/SE-9)), the Pipeline Steps table from the task DAG, the Artifacts Produced
table (the commits created), Next Steps (run propose-change to push and open the PR), and a
pointer to `$evidence_dest`. Always emitted; never gated.

```bash
# --- end Standard Play Close ---
```

## Scenario Validation

| Scenario | Persona | Eval |
|----------|---------|------|
| S1 — multi-concern | developer | SCE-1 |
| S2 — nothing to commit | developer | SCE-2 |
| S3 — log readability | reviewer | SCE-3 |
| S4 — sensitive file | developer | SCE-4 |
| S5 — trivial changeset, scripts only | developer | SCE-5 |
| S6 — loop convergence | developer | SCE-6 |

## Recovery

| For | Trigger | Direction | Handoff |
|-----|---------|-----------|---------|
| F1 | a commit covers more than one concern | regroup so each commit holds a single concern; re-stage by group and recommit (a loop round) | autonomous |
| F2 | a commit subject is generic or missing the issue reference | rewrite the subject to describe the change and add the issue reference | autonomous |
| F3 | changed files remain uncommitted with no exclusion reason | the unmet verdict is the next round's work list — commit the remainder or record the exclusion | autonomous |
| F4 | the current branch is main | halt; move the work to a feature branch (start-change) before committing | human |
| F5 | a sensitive file is staged | unstage the sensitive file and confirm it is excluded before committing | human |
| F6 | the play attempted a push | this play commits only — drop the push; leave pushing to propose-change | autonomous |
| F7 | the script's triviality classification disagrees with the changeset | discard the fast-path analysis and re-run the scan through the analyze agent (or record a needless dispatch); the agent's grouping wins | autonomous |
| F8 | the loop hit its cap with the Done means unmet | halt HALTED (`loop_cap_exhausted`) with the verdict recorded; a human inspects the unmet clauses and fixes state or knowingly raises the cap | human |
| F9 | scan warnings exist but no checkpoint fired and the report carries no Scanner Warnings row | re-render the delivery report with the warnings carried into the Run Summary before closing | autonomous |

## Pause and Resume

On entry, resolve the issue from the branch name, check the status marker, and re-enter
the loop at the recorded round count — the cap counts all rounds across resumes. A fresh
start with no marker begins at round 1 and creates the marker.

## Compilation Metadata

| Field | Value |
|-------|-------|
| fingerprint | sha256:5e3b5afb2b9b097c99d610feda8d9b8bb749bf3de04b9c5583c3471995bfe95d (of `reference/ice.md`) |
| compiled_by | play-editor (#465, Level 3 pilot recompile per ADR 025; #467 Batch C) |
| pipeline_position | end |
| workflow_structure | loop (skeleton: pre-flight → loop ≤5 → validation → gated close) |
| stop_condition | stop-condition.yaml (D1–D4), gate live at Step C0 |
| iteration_cap | 5 |
| domain_agents | 0 |
| utility_agents | 2 (repo-orchestrator — judgment path only; project-orchestrator) |
| skills_reused | analyze-changes (judgment path), resolve-issues, manage-issue |
| scripts | 5 (preflight.py, analyze_changeset.py, execute_commits.py, check_stop_condition.py, session_stamp.py) |
| step_evals | 9 (SE-1…SE-9) |
| scenario_evals | 6 (SCE-1…SCE-6) |
| recovery_entries | 9 (one per failure condition; 6 autonomous / 3 human) |

## Compiled note (#484, scripted chain — already compliant)

commit-change already embodies the #484 tool-first rule and needs no rewiring: C8 mandates
"mechanical work runs as bundled scripts, not model inference" — the changeset scan
(`analyze_changeset.py`), a trivial changeset's whole analysis, and commit execution
(`execute_commits.py`) all run as scripts, and an agent is dispatched ONLY when the script
classifies the changeset multi-concern, i.e. when grouping genuinely requires judgment. That
is exactly #484's principle (mechanical git/gh in scripts; agents only for judgment). Unlike
the four sibling chain plays, no operation here was an agent wrapper over a fixed git
sequence, so no ICE constraint changed and the fingerprint stands. This note records the
audit; the play is unchanged.

## Compiled note (#467 Batch C)

The loop's conditional checkpoint went **off by config** for this project
(`gates.plays.commit-change: off`) with its replacement visibility checks in the same
change: scanner warnings are carried into the delivery report's Run Summary (F9, SE-9,
REC9 — never silently dropped); a low-confidence mapping proceeds recorded as
low-confidence, never silently upgraded; the sensitive-file BLOCK stays a hard halt at
the scan, untouched by the switch. 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. No new scripts. Owned by the compiled
play (ICE recompile, not a direct edit).

## Compiled note (#438, absorbed on recompile)

`analyze_changeset.py`'s secret scanner distinguishes artifact types: content-pattern
matches in prose/standards artifacts (pattern carriers by design) are non-blocking warnings
surfaced at the checkpoint; filename matches and content matches in runtime/config paths
hard-block. C6's guarantee is unchanged: actual secrets never commit. Formerly a direct-edit
deviation note; owned by the compiled play since the #465 recompile.

Files in this skill

  • SKILL.md22.1 KB
  • reference/ice.md9.1 KB
  • scripts/analyze_changeset.py10.6 KB
  • scripts/check_stop_condition.py5 KB
  • scripts/execute_commits.py13 KB
  • scripts/preflight.py6.4 KB
  • scripts/session_stamp.py4.9 KB
  • stop-condition.yaml931 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…