forked from bchanot/claude
Run C2 of manual-push mode (BDR-111/BDR-112). The four flows that pushed on their own, or claimed the branch was on origin, now read the truth after the fact and hand the user the exact command: - client-handover-writer: the "Push to origin now?" question and its push block are gone (the hooks had already pushed in auto-push mode; push-guard denies it in manual mode). A reusable PUSH STATE READ (branch, origin probe, `git rev-list --count origin/<br>..<br>`, the verb only to word the reason) runs after commit-change, at the top of the deploy pause, after "Deployed" and before each end report. The branch name is validated against an allowlist before it is placed in any command or hint (a hostile branch name is otherwise a shell injection). Pending → the user pushes BEFORE the deploy pause; the deploy brief says "after your push". `Push:` line in both reports. - release-candidate STEP 6: two ahead counts + the verb; anything other than auto with both counts 0 prints one user command `! git push --atomic origin main develop v<X.Y.Z>` and stops; the tag gate stays for auto mode; `hold` notes --follow-tags; version regex. - release-executor: push claims qualified (auto-push mode, best effort). - tour: mode-agnostic rule; STEP 3 reads one `git -C <project>` fact per project (suffix-aware branch, --remotes=origin, origin probe) and the summary row says on origin / local only with the user command.
353 lines
18 KiB
Markdown
353 lines
18 KiB
Markdown
---
|
|
name: tour
|
|
effort: xhigh
|
|
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 — 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".
|
|
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)
|
|
|
|
## MODEL GATE (blocking — run before any other step)
|
|
|
|
Run `$HOME/.claude/lib/model-gate.md`. Reflection here (planning, audit
|
|
judgment, loop decisions) requires Fable/Opus. Verdict `small` → STOP: the
|
|
gate prints the remedy; end the turn — no later step, no dispatch. Nominal
|
|
(big) path is silent.
|
|
EFFORT SHIFTS: follow `$HOME/.claude/lib/effort-shift.md` (BDR-107): medium when a dispatch span starts, own level before challenge synthesis, low at the bookkeeping tail, max at escalation; every shift goes in the same message as the step's first tool call, a lone Skill call is a no-op.
|
|
|
|
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 to `main`/`develop`).
|
|
|
|
## 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. No paths → current repo only.
|
|
- ONE project → run STEP 1 → STEP 3 inline in this loop (nominal path,
|
|
unchanged).
|
|
- TWO OR MORE projects → parallel fan-out, STEP 0b: the repos are
|
|
independent working trees on independent chore branches — nothing the
|
|
runners write can collide.
|
|
- `--report-only` → run every audit, apply NO fix, write reports only
|
|
(the flag is forwarded to every runner).
|
|
- A failure in one project never aborts the tour: record it in that
|
|
project's report section — or as its global-summary row when the
|
|
runner itself died — and move on.
|
|
|
|
## STEP 0b — MULTI-PROJECT FAN-OUT (one runner per project, BDR-084)
|
|
|
|
Dispatch ONE runner per project, ALL in a SINGLE message — sequential
|
|
dispatch of independent repos defeats the purpose.
|
|
|
|
Model discipline (the user-fixed invariant behind this mode):
|
|
|
|
- The runner is dispatched with **NO `model` override** — it inherits the
|
|
session model, already validated big by the MODEL GATE above. The runner
|
|
carries this skill's reflection (fix decisions, convergence calls); it
|
|
must never be pinned down to an executor tier.
|
|
- Inside a runner, every dispatched agent keeps the tier this skill
|
|
already defines: security-auditor (sonnet frontmatter), the Phase B
|
|
audit (analyzer opus pin or `model="opus"`), doc-syncer (audit on
|
|
`model="opus"`, patch on its sonnet frontmatter — BDR-077).
|
|
|
|
Runner dispatch, one per project:
|
|
|
|
```
|
|
Agent(subagent_type="general-purpose",
|
|
description="tour runner — <project basename>",
|
|
prompt="Read ~/.claude/skills/tour/SKILL.md and execute STEP 1 → STEP 3
|
|
for EXACTLY ONE project: <absolute path>. Flags: <--report-only|none>.
|
|
Skip STEP 0/0b (routing) and the global summary — the dispatcher owns
|
|
them. Every rule of the skill applies unchanged: max 3 iterations,
|
|
never merge/finish/push main|develop, scoped commits, report appended to that
|
|
project's own .claude/audits/TOUR.md. Return EXACTLY: the project's
|
|
one-line global-summary row (STEP 3 format), then BRANCH: <name|no
|
|
branch>, then REPORT: <path>.")
|
|
```
|
|
|
|
The main loop then:
|
|
|
|
1. Collects each runner's summary row. A dead or mute runner becomes the
|
|
row `<path> : RUNNER FAILED — <reason> | no report` — never a silent
|
|
absence (a mute runner is NEVER a pass).
|
|
2. Prints the global summary (STEP 3 format) once ALL runners returned.
|
|
3. Runs the gated capitalize offer — MAIN LOOP ONLY, never inside a
|
|
runner: registries are shared state, and gate decisions stay here
|
|
(LRN-083).
|
|
|
|
LRN-083 derogation, recorded in BDR-084: the per-project iteration loop
|
|
(fix decisions, convergence) runs INSIDE its dispatched runner. Bounded
|
|
because nothing a runner decides touches shared state — independent
|
|
repos, per-repo chore branches, branches left UNMERGED for human review
|
|
exactly as in inline mode.
|
|
|
|
## 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 NOT applied — a
|
|
breaking change is the human's call (CLAUDE.md: confirm before a
|
|
breaking change). Its report row becomes
|
|
`open — needs decision (BREAKING)` with the proposed patch attached,
|
|
and the global summary line carries the **BREAKING** count.
|
|
Behaviour-preserving CRITICAL/HIGH fixes stay auto-applied.
|
|
4. Commit scoped: `git add <files touched>` (never `-A`),
|
|
`fix(security): …`.
|
|
|
|
### Phase B — CLEAN
|
|
|
|
1. Dispatch a read-only cleanup audit (analyzer — opus-pinned, BDR-076 —
|
|
or general-purpose with `model="opus"`; NOT the sonnet code-cleaner,
|
|
which is now a fix executor): 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** — two-mode doc-syncer, mirrors /ship-feature STEP 8
|
|
(BDR-077: audit judgment on opus, patch on the sonnet pin):
|
|
`Agent(subagent_type="doc-syncer", model="opus")` with `MODE: audit`
|
|
+ `auto-mode scope: <files this tour touched>`; public docs only
|
|
(README, INSTALL, USAGE, CHANGELOG…), never `.claude/**`, never
|
|
CLAUDE.md. NONE → done. `[MINOR]` PATCH PLAN → re-dispatch
|
|
`Agent(subagent_type="doc-syncer")` (sonnet frontmatter) with
|
|
`MODE: patch` + the plan verbatim, then commit its `PATCHED_FILES:`
|
|
via `bash ~/.claude/lib/doc-commit.sh` when available, else a scoped
|
|
`docs: …` commit of exactly those paths. SIGNIFICANT → not applied:
|
|
report row `suggested` with the plan item (the tour has no human
|
|
gate mid-run).
|
|
|
|
### 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`) —
|
|
EXCEPT in `--report-only` mode: no chore branch exists there, so the
|
|
commit would land on the user's current branch (possibly develop — the
|
|
red flag below forbids that). Report-only leaves TOUR.md uncommitted
|
|
and says so in the summary.
|
|
4. Confirm `git status --porcelain` is clean (runtime junk the sandbox
|
|
cannot delete, e.g. `__pycache__/`, becomes a report residual line).
|
|
5. Push state, only when a branch exists (report-only, skipped or
|
|
dirty-tree projects have none: their row keeps `no branch`, no push
|
|
column). Two read-only calls, probe first:
|
|
`git -C <abs project> remote get-url origin >/dev/null 2>&1 || echo no-origin`
|
|
then
|
|
`git -C <abs project> rev-list --count <branch> --not --remotes=origin 2>/dev/null || echo unknown`
|
|
(`<branch>` = the name `gitflow start` returned, suffixed `-2`/`-3` on a
|
|
same-day re-run — never the bare `chore/tour-<date>`). 0 → `on origin`;
|
|
else `local only → ! git -C <abs project> push -u origin <branch>` (probe
|
|
printed `no-origin` → `local only (no origin remote)`).
|
|
|
|
```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 | open — needs decision (BREAKING): new required X-Backup-Token header, patch attached |
|
|
| 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: SEC-2 (needs decision). Commits: 4. BREAKING: 1 (SEC-2).
|
|
```
|
|
|
|
Global summary inline, one line per project (append `BREAKING: n` to
|
|
any project line with contract-changing fixes left open for decision):
|
|
|
|
```
|
|
TOUR COMPLETE — 2026-07-04
|
|
~/proj/api : CONVERGED (2 it.) — 3 fixed, 1 suggested | chore/tour-2026-07-04, 4 commits | on origin
|
|
~/proj/site : NOT CONVERGED (3 it.) — 2 open residuals | chore/tour-2026-07-04, 6 commits | local only → ! git -C ~/proj/site push -u origin chore/tour-2026-07-04
|
|
~/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 `main`/`develop`** — "the tour is green" is not a signal.
|
|
The gitflow hooks push the chore branch in auto-push mode only; when it
|
|
is not on origin (manual push mode, or a `push FAILED` warning) the USER
|
|
pushes it — `! git -C <abs project> push -u origin <branch>` — the tour
|
|
never pushes or retries.
|
|
- 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 auto-applied | Not applied: row `open — needs decision (BREAKING)` + proposed patch; **BREAKING** count in 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 push `main`/`develop`.
|
|
- 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 writing-skills (vendored superpowers skill;
|
|
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.
|