diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d73b6d..e690e41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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-` 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 ` ` 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". diff --git a/CLAUDE.md b/CLAUDE.md index f94825a..f17c452 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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 diff --git a/skills/tour/SKILL.md b/skills/tour/SKILL.md new file mode 100644 index 0000000..1d96f9f --- /dev/null +++ b/skills/tour/SKILL.md @@ -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-` 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 `. 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: \nREPORT: .claude/audits/.tour-semgrep.md\nFollow agents/security-auditor.md exactly. Pinned rulesets, no login. Write ONLY to REPORT. End with REPORT_WRITTEN: .") + ``` + 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 ` (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.