feat(skills): /tour — grouped all-axes sweep (clean+security+reconcile+doc), auto mode, bounded convergence loop

Orchestrator over 1..N projects: security-auditor (pinned semgrep) + cso
(gstack ON) -> clean -> re-verify -> reconcile (report-only) -> doc-syncer
silent mode, looping until a zero-fix pass, max 3 iterations. Fixes commit
on chore/tour-<date>, never merged by the skill; per-project append-only
.claude/audits/TOUR.md with BREAKING tags on contract-changing fixes.
TDD per superpowers:writing-skills: 6 baseline gaps countered + 2 GREEN-run
holes patched (scratch-file self-block, unflagged breaking fix).
CLAUDE.md routing line + CHANGELOG Unreleased entry.
This commit is contained in:
Bastien Chanot
2026-07-04 19:12:38 +02:00
parent d9ae200ba5
commit 73e6a1c6da
3 changed files with 268 additions and 0 deletions
+3
View File
@@ -6,6 +6,9 @@ Format follows [Keep a Changelog](https://keepachangelog.com/).
## [Unreleased]
### Added
- `/tour` skill — grouped all-axes sweep over one or several projects: security (pinned-semgrep `security-auditor` agent + `/cso` posture when gstack is ON) → cleanup → re-verify → reconcile (report-only, never edits the target TODO/registries) → doc sync, looping until a full pass applies zero fixes (bounded at 3 iterations). Fixes land on a `chore/tour-<date>` branch the skill never merges; each project gets an append-only `.claude/audits/TOUR.md` report with BREAKING tags on contract-changing security fixes. Built TDD (superpowers:writing-skills): baseline run showed silent TODO rewrites, autonomous registry writes, grep-as-security-pass, no persistent report, scope creep and an unbounded loop — each countered and verified on a seeded fixture.
### Fixed
- `gitflow_finish` ignored its `<type> <name>` arguments and always merged the checked-out branch — naming a different branch silently merged the wrong one. The arguments are now an optional safety assertion: if given and not equal to the current branch, `finish` refuses with a clear error instead of merging. No-argument calls (the only real caller) are unchanged.
- `doctor.sh` false-warnings removed (a check that cries wolf is one you learn to ignore): `cargo` absence no longer claims "RTK unavailable" (RTK ships as a prebuilt binary); `check_symlink` no longer flags files reached through directory-level symlinks (e.g. `hooks/session-start.sh`); the GStack check counts the per-skill symlinks instead of a `skills/gstack` link that `link.sh` deliberately removes; the token-budget estimate is measured against the ~200k context window instead of a mis-framed "~11k session budget" that produced a false "92% CRITICAL".
+2
View File
@@ -269,6 +269,8 @@ only the non-obvious cases: gstack fallbacks, disambiguation, cryptic names.
- Cut a release / tag a version (develop ahead of main) → release-candidate
- Docs post-ship → document-release (doc if gstack off); stale-doc audit → doc
- Audit of changes since last run → audit-delta
- Grouped all-axes sweep (clean+security+reconcile+doc, "tir groupé",
tour of one or more projects, fix + loop until clean) → tour
- Open-work inventory / "queue empty?" / stale TODO vs real git → reconcile
- Design / UI (build, system, audit, polish) → see "Design work" below
- Architecture review → plan-eng-review
+263
View File
@@ -0,0 +1,263 @@
---
name: tour
description: |
Use when the user wants ONE grouped pass over a whole project (or a
list of projects) covering all hygiene axes together: code cleanup +
security (semgrep/cso) + TODO-vs-reality check + doc sync, auto-fixing
and re-auditing until a clean pass. Use it whenever the user asks for
a "tour" of their projects, a grouped/combined audit-and-fix, or a
periodic all-axes sweep — even without naming the axes.
NOT one axis alone (/code-clean, /cso, /audit-delta, /reconcile, /doc),
one bug (/hotfix, /bugfix), dashboard (/health), branch diff (/review).
Triggers: "tour", "tir groupé", "grand ménage", "fais un tour sur les
projets", "sweep", "full pass", "vérifie et corrige tout", "passe
tout au propre".
argument-hint: "[project paths… — blank = current repo] [--report-only]"
allowed-tools:
- Read
- Edit
- Write
- Bash
- Grep
- Glob
- Agent
- AskUserQuestion
---
# /tour — grouped multi-axis sweep (clean + security + reconcile + doc)
One pipeline per project: **security → clean → re-verify → reconcile →
doc → convergence re-audit**, looping until a full pass applies zero new
fixes. Auto mode by design: fixes are committed on a dedicated
`chore/tour-<date>` branch that this skill **never merges** — the branch
plus its report IS the approval gate, reviewed by the human afterwards.
Core principle: **autonomy on the working branch, never on shared
state.** The skill may edit code freely on its own branch; it may NOT
silently rewrite declared state (target TODO, memory registries) or
integrate anything (merge/finish/push).
## When NOT to use
| Situation | Skill |
|-----------|-------|
| One axis only (cleanup / security / TODO / doc) | `/code-clean`, `/cso`, `/reconcile`, `/doc` |
| Recurring single-axis audit scoped to the delta | `/audit-delta` |
| One obvious bug | `/hotfix`, `/bugfix` |
| Quality dashboard, no fixes | `/health` |
| Review a branch/PR diff | `/review`, `/code-review` |
| All axes, fix, loop to clean, 1..N projects | **this skill** |
## STEP 0 — ARGS & PROJECT LIST
- Paths in `$ARGUMENTS` → project list, processed **sequentially** in
the given order. No paths → current repo only.
- `--report-only` → run every audit, apply NO fix, write reports only.
- A failure in one project never aborts the tour: record it in that
project's report section and move to the next.
## STEP 1 — PRECONDITIONS (per project)
All git commands use `git -C <project>`. Check, in order:
1. Is a git repository → else SKIP (recorded, not an error).
2. Working tree **clean** (`git status --porcelain` empty) → else this
project runs **report-only**: never mix the user's WIP with tour
fixes, never stash someone else's work.
3. `develop` exists and `~/.claude/lib/gitflow.sh` is available → start
the working branch via the lib, never by hand:
`bash ~/.claude/lib/gitflow.sh start chore tour-YYYY-MM-DD`
(append `-2`, `-3`… if the branch already exists). Missing develop
or lib → **report-only** + suggest `gitflow init` in the report.
4. Detect project checks once (tests, lint, build, type-check — from
package.json/Makefile/CLAUDE.md). Record what exists; "none found"
is itself a report line.
Report file: `.claude/audits/TOUR.md` in the target project (create
`.claude/audits/` if absent). Append-only — never rewrite past runs.
## STEP 2 — ITERATION LOOP (max 3 per project)
Each iteration runs phases A→D in fixed order. **Convergence** = one
full iteration that applies **zero fixes** and finds **zero new
findings** with project checks green. Converged → STEP 3. Not converged
after 3 iterations → STOP, residuals stay `open` in the report, say so
honestly in the summary. Never loop past 3.
### Phase A — SECURITY (deterministic floor first)
1. Dispatch the SAST gate (fresh every iteration):
```
Agent(subagent_type="security-auditor", description="tour security — semgrep SAST",
prompt="MODE: audit\nSCOPE: project (full tree, respect .gitignore)\nPROJECT: <path>\nREPORT: .claude/audits/.tour-semgrep.md\nFollow agents/security-auditor.md exactly. Pinned rulesets, no login. Write ONLY to REPORT. End with REPORT_WRITTEN: <path>.")
```
semgrep ABSENT → DEGRADED (checklist only) is surfaced in the
report, not a silent downgrade and not a blocker.
2. gstack ON (`/cso` available) → **iteration 1 only**, dispatch a cso
posture audit (deps CVE, OWASP) in audit mode; fold its findings in.
3. Fix policy (skip in `--report-only`): CRITICAL/HIGH → fix now.
MEDIUM/LOW → fix only if local and behavior-preserving, else leave
`open`. Every fix minimal, CLAUDE.md security defaults apply.
A CRITICAL/HIGH fix that changes the API contract (new required
header/param, changed status codes, moved paths) is still applied —
but its report row and the global summary line carry a **BREAKING**
tag, so the human review cannot miss it.
4. Commit scoped: `git add <files touched>` (never `-A`),
`fix(security): …`.
### Phase B — CLEAN
1. Dispatch a read-only cleanup audit (code-cleaner agent if available,
else analyzer/general): dead code, unused imports/exports,
commented-out blocks, stale flags, norm violations. Findings as
`id | file:line | finding | proposed fix`.
2. Apply **behavior-preserving** fixes only. A finding that would change
behavior is a bug, not cleanup → log to
`.claude/audits/BUGS-FOUND.md`, leave the code alone.
3. Commit scoped: `chore(clean): …`.
### Phase C — RE-VERIFY (after any fix)
1. Run the project checks found in STEP 1. Lint alone is NOT
verification when tests/build exist.
2. Fresh read-only subagent re-audits the files modified this
iteration (same axis prompts). Pass = approved findings resolved AND
zero new findings introduced.
3. Fail → fix → recheck, max 3 attempts inside the iteration; still
failing → **revert this phase's commits** (fail closed), findings
back to `open`, recorded in the report.
### Phase D — RECONCILE + DOC
1. **Reconcile — REPORT-ONLY, always, even in auto mode.** Confront
declared state (target TODO checkboxes, registry statuses) against
real state (git log, files, branches) — reuse `lib/reconcile.sh`
oracles when available. Every gap goes in the report as
`declared X | real Y | suggested edit`. **Never check a box, never
restructure, never edit the target project's TODO.md or
`.claude/memory/`** — an inferred checkbox is exactly the lie
/reconcile exists to catch. The human applies suggestions via
`/reconcile` later.
2. **Doc sync** — dispatch doc-syncer in AUTOMATIC (silent) mode:
public docs only (README, INSTALL, USAGE, CHANGELOG…), never
`.claude/**`, never CLAUDE.md. Commit its `PATCHED_FILES:` via
`bash ~/.claude/lib/doc-commit.sh` when available, else a scoped
`docs: …` commit of exactly those paths.
### End of iteration
Fixes were applied (any phase) OR new findings appeared → run another
iteration (fixes can invalidate earlier audits — that is the point of
the loop). Otherwise → converged.
## STEP 3 — REPORT, CLEANUP & SUMMARY (per project, then global)
Append to `.claude/audits/TOUR.md`, then close the run in this exact
order:
1. Write the run section (template below).
2. **Delete the scratch audit files** this run created
(`.claude/audits/.tour-semgrep*` and similar) — their content is
folded into TOUR.md. A tree left dirty here forces the NEXT tour
into report-only: the skill must not self-block.
3. Commit the report as the run's final commit (`docs(tour): report`).
4. Confirm `git status --porcelain` is clean (runtime junk the sandbox
cannot delete, e.g. `__pycache__/`, becomes a report residual line).
```markdown
## Tour 2026-07-04 — branch chore/tour-2026-07-04 — 2 iterations — CONVERGED
| ID | Axis | File | Sev | Finding | Status |
|----|------|------|-----|---------|--------|
| SEC-1 | security | app.py:17 | high | shell=True + concat | fixed |
| SEC-2 | security | app.py:14 | high | no authz on POST /backup | fixed — **BREAKING**: new required X-Backup-Token header |
| CLN-1 | clean | utils.py:9 | - | dead legacy_md5 | fixed |
| REC-1 | reconcile | TODO.md | - | "/health" unchecked, shipped 2d92696 | suggested |
| DOC-1 | doc | README.md | - | phantom /status endpoint | fixed |
Checks: pytest PASS, ruff PASS. Residuals: none. Commits: 5. BREAKING: 1 (SEC-2).
```
Global summary inline, one line per project (append `BREAKING: n` to
any project line whose fixes changed an API contract):
```
TOUR COMPLETE — 2026-07-04
~/proj/api : CONVERGED (2 it.) — 3 fixed, 1 suggested | chore/tour-2026-07-04, 4 commits
~/proj/site : NOT CONVERGED (3 it.) — 2 open residuals | chore/tour-2026-07-04, 6 commits
~/proj/lib : report-only (dirty tree) | no branch
Branches left UNMERGED — review each, then `gitflow finish` on your GO.
Reconcile suggestions pending — apply via /reconcile.
```
Then offer to capitalize (gated, per CLAUDE.md): recurring cross-project
patterns → learnings, tour verdict → evals. Never write registries
without that approval — neither this repo's nor any target project's.
## Rules
- Branch via the gitflow lib; **never `gitflow finish`, never merge,
never push** — no exceptions, "the tour is green" is not a signal.
- Scoped pathspecs only; `git add -A` is forbidden.
- Target TODO.md and target `.claude/memory/` are READ-ONLY. Reconcile
produces suggestions, not edits.
- Only the four axes. No unrequested bootstrap (.gitignore, registries,
configs, features) — infrastructure gaps are report lines, not work.
- Security floor = security-auditor (pinned semgrep + checklist). An
ad-hoc grep is never "the security pass".
- Max 3 iterations per project; max 3 fix attempts per re-verify.
Residuals are reported, not silently retried forever.
- Dirty tree / no develop / no gitflow lib → report-only, stated in the
report. Never stash, never branch by hand.
- Reports append-only. One report per project, in that project.
## Common mistakes
| Mistake | Fix |
|---------|-----|
| Checking TODO boxes "obviously done" during reconcile | Report-only. Suggested edits, human applies. |
| Writing BDR/LRN/journal entries in the target project | Registries only via the gated capitalize offer, end of tour. |
| grep/ruff pass = security done | security-auditor agent (pinned semgrep) is the floor, every iteration. |
| Findings live only in the final chat message | TOUR.md is what the human reviews before merging. Write it. |
| "Bonus hygiene" (.gitignore, templates, bootstrap) | Out of scope. Report line, not work. |
| Loop "until clean" with no bound | Max 3 iterations, then honest residuals. |
| Merging/finishing because everything is green | Green ≠ GO. Branch stays; human merges. |
| Stashing a dirty tree to proceed | Report-only for that project. |
| One TOUR.md for all projects in the config repo | Each project gets its own `.claude/audits/TOUR.md`. |
| Fixing a behavior-changing "cleanup" finding | That is a bug → BUGS-FOUND.md, untouched code. |
| Scratch audit files left untracked at the end | Delete them in STEP 3.2 — a dirty tree self-blocks the next tour. |
| Contract-changing security fix reported as plain "fixed" | Tag **BREAKING** in the row AND the summary line. |
## Red flags — STOP
- About to `Edit` a target project's TODO.md or `.claude/memory/*`.
- About to run `gitflow finish`, `git merge`, or `git push`.
- About to `git add -A` or commit on `main`/`develop`.
- Starting iteration 4, or "just one more loop, it's almost clean".
- Security phase done without the security-auditor agent and without a
DEGRADED notice in the report.
- Creating any file the audit did not require (.gitignore, templates).
- Ending a project's run with `git status --porcelain` non-empty and no
residual line explaining every leftover path.
## TDD note (skill itself)
Baseline-tested per superpowers:writing-skills (2026-07-04, seeded
fixture, no skill): the agent branched correctly via gitflow and did not
merge, BUT (1) silently rewrote the target TODO (checked boxes,
restructured) during "reconcile"; (2) authored BDR/journal registry
entries autonomously; (3) ran security as ad-hoc grep + ruff — no
semgrep, no pinned rulesets; (4) left findings only in its final chat
message — no persistent report to review before merge; (5) bootstrapped
unrequested .gitignore + memory registries ("bonus hygiene");
(6) looped without a stated bound (converged at pass 2 by luck). Phase
D.1, the registry rule, Phase A.1, STEP 3, the scope rule and the
3-iteration bound counter each observed failure.
GREEN run (same day, fresh fixture, skill followed): all six gaps
closed — TODO zero-diff, no registry writes, semgrep every iteration,
TOUR.md committed, no scope creep, converged in 3 bounded iterations on
an unmerged chore branch. Two new holes surfaced and patched
(REFACTOR): scratch semgrep files left untracked (would self-block the
next run — STEP 3.2) and a contract-changing security fix not flagged
(BREAKING tag in template). The REFACTOR additions are
template-structural and were not re-run through a third full fixture
pass — re-test on first real use.