diff --git a/CLAUDE.md b/CLAUDE.md index e31d310..f94825a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -168,23 +168,27 @@ Every git action follows gitflow — inside a skill AND for ad-hoc commits made outside one on direct request. The model is universal across all projects. ### Branch model -`main` (prod) · `develop` (integration, off main) · `feature/*` + `bugfix/*` -(off develop → develop) · `release/*` (off develop → main + back-merge develop) -· `hotfix/*` (off main → main + develop [+ any open release/*]). `master`→`main` -everywhere. +`main` (prod) · `develop` (integration, off main) · `feature/*` + `bugfix/*` + +`chore/*` (off develop → develop; `chore/*` = memory/doc maintenance, e.g. +standalone `/capitalize` `/prune-memory` `/reconcile`) · `release/*` (off develop → +main + back-merge develop) · `hotfix/*` (off main → main + develop [+ any open +release/*]). `master`→`main` everywhere. ### Rules for every git action - **Never commit code directly on `main` or `develop`.** Branch first from the correct base, named `/`. (`.claude/**` memory/config commits are - exempt — they follow the work, not the code's gitflow.) + hook-exempt — they follow the work; but *standalone* memory/doc skills branch to + `chore/*` via the aiguillage rather than lean on that exemption.) - **Branch + merge via the lib, never by hand** — the directed-merge + hotfix fan-out logic lives there once: `bash ~/.claude/lib/gitflow.sh start ` · `… finish`. - **`gitflow finish` (merge) only on an explicit human signal** ("merge it", "feature OK") — never because tests pass, a plan step says "merge", or a verb ("ship") implied it. -- **Assistance flows** (`/feat` `/bugfix` `/hotfix`) auto-branch on a protected - base (the aiguillage); on a working branch they commit in place, never finish. +- **Assistance flows** (`/feat` `/bugfix` `/hotfix`) AND **standalone memory/doc + skills** (`/capitalize` `/close` `/prune-memory` `/reconcile`, type `chore`) + auto-branch on a protected base (the aiguillage); on a working branch they commit + in place, never finish. - **New/onboarded projects** get the model + the versioned pre-commit hook via `gitflow init` (init-project STEP 5f, onboard STEP 2.6). diff --git a/lib/gitflow-aiguillage.md b/lib/gitflow-aiguillage.md index 20a7628..617bd1d 100644 --- a/lib/gitflow-aiguillage.md +++ b/lib/gitflow-aiguillage.md @@ -1,26 +1,41 @@ -# Gitflow aiguillage — assistance flows branch on a protected base +# Gitflow aiguillage — branch on a protected base before writing -Assistance flows (`/feat`, `/bugfix`, `/hotfix`) commit IN PLACE on a working -branch — the frequent case, behavior unchanged. But they must NEVER commit code -on a protected base (`main`/`develop`). Run this check **before editing any -file**. The caller passes its TYPE: feat→`feature`, bugfix→`bugfix`, -hotfix→`hotfix`. +Flows that WRITE — code, OR standalone memory/doc work — must NEVER commit on a +protected base (`main`/`develop`). Run this check **before editing any file**. ```bash bash "$HOME/.claude/lib/gitflow.sh" protected-base && echo PROTECTED || echo WORKING ``` -- **WORKING** (`feature/*`, `bugfix/*`, `hotfix/*`, or any non-protected branch) - → proceed; you commit in place on this branch. Nothing changes. +- **WORKING** (`feature/*`, `bugfix/*`, `hotfix/*`, `chore/*`, or any non-protected + branch) → proceed; you commit in place on this branch. Nothing changes. - **PROTECTED** (`main`/`develop`) → branch first, do NOT commit here: ```bash bash "$HOME/.claude/lib/gitflow.sh" start ``` `` derived from the request. Then do the work on the new branch. -**Never run `gitflow finish`** — assistance flows commit, they do not merge. -Integration is a separate, human-gated step (the `gitflow` skill). +The caller passes its TYPE: -Note: `hotfix` branches off **main** (prod) even when invoked from `develop` — -that is the gitflow definition of a hotfix. For a dev-scoped small fix, use -`/bugfix` (branches off develop). +| Caller | TYPE | Base | +|--------|------|------| +| `/feat` | `feature` | develop | +| `/bugfix` | `bugfix` | develop | +| `/hotfix` | `hotfix` | main | +| `/capitalize` · `/close` · `/prune-memory` · `/reconcile` | `chore` | develop | + +The `chore` row = **standalone memory/doc work**: the registry / TODO / doc +reconciliation & curation skills, run OUTSIDE an assistance flow. Inside `/feat` +`/bugfix` `/hotfix` `/ship-feature` a working branch already exists (this check +returns WORKING) and the memory commit rides it. The aiguillage only fires when +such a skill is invoked directly on `main`/`develop` — i.e. memory IS the work, +with no code branch to follow. That is the leak it closes: the `.claude/**` hook +exemption still lets a *manual* memory commit through on a protected base, but a +skill-driven one now branches to `chore/*` first. + +**Never run `gitflow finish`** — these flows commit, they do not merge. Integration +is a separate, human-gated step (the `gitflow` skill). + +Note: `hotfix` branches off **main** (prod) even when invoked from `develop` — that +is the gitflow definition of a hotfix. For a dev-scoped small fix, use `/bugfix` +(branches off develop). diff --git a/lib/gitflow-test.sh b/lib/gitflow-test.sh index 8a181fc..9b46394 100644 --- a/lib/gitflow-test.sh +++ b/lib/gitflow-test.sh @@ -32,6 +32,9 @@ chk "protected develop" 'gitflow_protected_base develop' chk "not protected feat" '! gitflow_protected_base feature/x' chk "base feature=develop" '[ "$(gitflow_base_for feature)" = develop ]' chk "base hotfix=main" '[ "$(gitflow_base_for hotfix)" = main ]' +chk "type chore" '[ "$(gitflow_branch_type chore/x)" = chore ]' +chk "base chore=develop" '[ "$(gitflow_base_for chore)" = develop ]' +chk "not protected chore" '! gitflow_protected_base chore/x' echo "T2 — init fresh (BLK-010 root commit)" newrepo fresh; echo scaffold > README.md; hookon @@ -92,6 +95,16 @@ chk "merged into develop" 'git log develop --oneline | grep -q "Merge feature/f1 chk "main untouched" "[ \"\$(git rev-parse main)\" = \"$main_before\" ]" chk "branch deleted" '! git rev-parse --verify -q refs/heads/feature/f1 >/dev/null' +echo "T6b — finish chore → develop only (standalone memory/doc maintenance)" +newrepo finchore; echo a>a; hookon; gitflow_init >/dev/null 2>&1 +gitflow_start chore c1 >/dev/null 2>&1 +mkdir -p .claude/memory; echo m>.claude/memory/x.md; git add -A; git commit -q -m "chore(memory)" +main_before="$(git rev-parse main)" +gitflow_finish >/dev/null 2>&1 +chk "chore merged into develop" 'git log develop --oneline | grep -q "Merge chore/c1 into develop"' +chk "chore main untouched" "[ \"\$(git rev-parse main)\" = \"$main_before\" ]" +chk "chore branch deleted" '! git rev-parse --verify -q refs/heads/chore/c1 >/dev/null' + echo "T7 — finish hotfix → main + develop fan-out" newrepo finhot; echo a>a; hookon; gitflow_init >/dev/null 2>&1 gitflow_start hotfix h1 >/dev/null 2>&1; echo p>patch.txt; git add patch.txt; git commit -q -m patch @@ -119,7 +132,7 @@ chk "idempotent 2nd run" "[ \"$before\" = \"\$(md5sum .gitignore)\" ]" echo "T10 — COHERENCE: hook verdict == lib predicate (drift detector, #4)" newrepo coh; echo a>a; hookon; gitflow_init >/dev/null 2>&1 -for br in main develop feature/x bugfix/y release/z hotfix/w master mainline qa; do +for br in main develop feature/x bugfix/y release/z hotfix/w chore/m master mainline qa; do if gitflow_protected_base "$br"; then lib=protected; else lib=open; fi git checkout -q -B "$br" 2>/dev/null printf 'x\n' >> a; git add a diff --git a/lib/gitflow.sh b/lib/gitflow.sh index ca2067c..54feec7 100644 --- a/lib/gitflow.sh +++ b/lib/gitflow.sh @@ -21,7 +21,7 @@ GITFLOW_GITIGNORE_TEMPLATE="${GITFLOW_GITIGNORE_TEMPLATE:-$_GITFLOW_LIB_DIR/../t # ── predicates / pure helpers ──────────────────────────────────────────────── -# echo the gitflow type of a branch: feature|bugfix|release|hotfix|main|develop|other +# echo the gitflow type of a branch: feature|bugfix|release|hotfix|chore|main|develop|other gitflow_branch_type() { local br="${1:-$(git symbolic-ref --short -q HEAD 2>/dev/null)}" case "$br" in @@ -31,6 +31,7 @@ gitflow_branch_type() { bugfix/*) echo bugfix ;; release/*) echo release ;; hotfix/*) echo hotfix ;; + chore/*) echo chore ;; *) echo other ;; esac } @@ -46,7 +47,7 @@ gitflow_protected_base() { # echo the base a given type must fork from. gitflow_base_for() { case "$1" in - feature|bugfix|release) echo "$GITFLOW_DEVELOP" ;; + feature|bugfix|release|chore) echo "$GITFLOW_DEVELOP" ;; hotfix) echo "$GITFLOW_MAIN" ;; *) echo "gitflow: unknown type '$1'" >&2; return 2 ;; esac @@ -103,7 +104,7 @@ gitflow_finish() { br="$(git symbolic-ref --short -q HEAD)" || { echo "gitflow_finish: detached HEAD" >&2; return 3; } type="$(gitflow_branch_type "$br")" case "$type" in - feature|bugfix) + feature|bugfix|chore) _gitflow_merge_into "$GITFLOW_DEVELOP" "$br" && _gitflow_delete "$br" ;; release) _gitflow_merge_into "$GITFLOW_MAIN" "$br" \ diff --git a/skills/capitalize/SKILL.md b/skills/capitalize/SKILL.md index 5dcd111..af7abce 100644 --- a/skills/capitalize/SKILL.md +++ b/skills/capitalize/SKILL.md @@ -50,6 +50,13 @@ Running `/capitalize` right after a ritual should propose (near) nothing. This skill is NOT `/prune-memory` (registry curation — merge, compress, mark-superseded). It only appends. +## Gitflow aiguillage (before any write) + +Before STEP 4 writes anything, follow `$HOME/.claude/lib/gitflow-aiguillage.md` +— this skill's TYPE = `chore`. On `main`/`develop` it branches to `chore/` +off develop, so the memory commit lands on a branch, never direct on a protected +base; on a working branch it proceeds in place. Never `gitflow finish` (human-gated). + ## STEP 0 — PRECHECK ```bash diff --git a/skills/close/SKILL.md b/skills/close/SKILL.md index 111f442..eb61451 100644 --- a/skills/close/SKILL.md +++ b/skills/close/SKILL.md @@ -34,4 +34,7 @@ Ritual answers are deduped like any other candidate — a dup is dropped and its existing ID shown, not re-logged. This is the upgrade over the legacy `/close`, which wrote ritual answers fresh with no dedup. +The gitflow aiguillage (branch to `chore/*` on a protected base before writing) +runs inside `capitalize` — not duplicated here. + → Use the Skill tool to launch `capitalize` with argument `--ritual`. diff --git a/skills/gitflow/SKILL.md b/skills/gitflow/SKILL.md index cddc507..8e73c0b 100644 --- a/skills/gitflow/SKILL.md +++ b/skills/gitflow/SKILL.md @@ -68,11 +68,13 @@ gives a **real-time, explicit go for THIS merge** — "merge it", "feature OK", All of these mean: present the merge as a question, then wait for the explicit go. -## Aiguillage (assistance skills) +## Aiguillage (assistance + standalone memory/doc skills) -On a protected base, assistance skills (`feat`/`bugfix`/`hotfix`) call -`start ` to branch first; on a working branch they commit in place. Same -`protected-base` predicate the out-of-skill hook uses. +On a protected base, assistance skills (`feat`/`bugfix`/`hotfix`) AND the standalone +memory/doc skills (`capitalize`/`close`/`prune-memory`/`reconcile`, TYPE `chore`) +call `start ` to branch first; on a working branch they commit in place. Same +`protected-base` predicate the out-of-skill hook uses. Caller→type map + rationale: +`lib/gitflow-aiguillage.md`. ## Common Mistakes diff --git a/skills/prune-memory/SKILL.md b/skills/prune-memory/SKILL.md index 9a26e33..3e0541a 100644 --- a/skills/prune-memory/SKILL.md +++ b/skills/prune-memory/SKILL.md @@ -62,6 +62,13 @@ If working tree is dirty on any registry file → STOP with: "Commit or stash pending changes in `.claude/memory/` first. Skill writes in-place. Git is the only backup." +## STEP 0b — Gitflow aiguillage (after PRECHECK, before any write) + +PRECHECK first (clean tree = the backup). Then follow +`$HOME/.claude/lib/gitflow-aiguillage.md` — this skill's TYPE = `chore`. On +`main`/`develop` it branches to `chore/` off develop so the curation lands +on a branch; on a working branch it proceeds in place. Never `gitflow finish`. + ## STEP 1 — AUDIT (per registry) For each target registry (filter by `$ARGUMENTS` or all 5): diff --git a/skills/reconcile/SKILL.md b/skills/reconcile/SKILL.md index f92ed14..7ac1949 100644 --- a/skills/reconcile/SKILL.md +++ b/skills/reconcile/SKILL.md @@ -34,6 +34,8 @@ Not for: curating/compressing registries → `/prune-memory`. The skill never ed Plus **contradiction candidates** — `reconcile_contradiction_candidates`: accepted-BDR ⇄ open-chantier overlap, surfaced for human review. ## The gate (mandatory) +**Before applying (A/B):** follow `$HOME/.claude/lib/gitflow-aiguillage.md` — TYPE `chore`. On `main`/`develop` the write-back branches to `chore/` off develop first, so a reconciled TODO never lands direct on a protected base; on a working branch it applies in place. Never `gitflow finish` (human-gated). + Reconciling the TODO edits a tracked file → never silent. Show the proposed diff, then ask: **A** apply all · **B** select a subset · **C** touch nothing. Registries stay READ-ONLY (append-only; curation is `/prune-memory`). ## Honest limits (do not over-read the guarantee)