Skip to content

Latest commit

 

History

History
358 lines (297 loc) · 31 KB

CLAUDE.md

File metadata and controls

358 lines (297 loc) · 31 KB

mxcli-project-toolkit — Claude Context

What this repo is

Shared skills, stage-gate tooling, and learnings for Mendix migration and development projects. Serves four entry modes (see skills/conversion-runbook.md → "Entry Modes"): migration (legacy source, all stages), requirements-driven (specs/SME input, no legacy code, stages 1–6), greenfield (stage 5 onward), and change an existing app (a live .mpr, a slice being added or altered — stages P, 0–6 per slice, skills/existing-app-change.md). Plus à-la-carte use with no pipeline at all — auditing or regression/e2e-testing an existing app routes straight to skills/existing-app-assurance.md, skipping intake/stages/gates entirely.

The front door

  • CONVERSION-RUNBOOK.md (root) — thin "how to start" pointer.

  • toolkit-guide.html (root) — the visual onboarding page. First-touch rule — this is the single source of the rule; every other mention defers here:

    [ -e "<project-root>/.claude/.guide-shown" ] && DO NOT OPEN
    

    Open it in the user's browser (open toolkit-guide.html / xdg-open) only when that sentinel is absent, and immediately touch it so no later session repeats. bin/init-project.sh opens it at scaffold time and writes the sentinel then. The user may also ask for it by name at any time — that's always allowed and does not consult the sentinel.

    Do not re-open it on a hunch that the user "seems unsure" — a session cannot tell a first-timer from someone who has seen it fifty times, and every session that guesses guesses "yes". The sentinel is the only test. (Real incident, 2026-08-12: this rule was stated conditionally here and in README.md but unconditionally in skills/conversion-runbook.md, CONVERSION-RUNBOOK.md, and inside toolkit-guide.html itself. Because the runbook is the one file every session is required to read first, every pipeline session opened the guide, and the user got a browser tab per session.)

    It's also the shared CSS shell/tokens for every stage HTML surface — that use has nothing to do with opening it.

  • skills/conversion-runbook.md — the spine: 9-stage matrix, interview protocol, gates, entry modes. Start here when unsure what stage anything is in.

  • bin/init-project.sh <project-root> — the one-command install: intake.md, PROJECT.md, CLAUDE.local.md (runbook wiring), all six agent stubs, index.html dashboard; opens the guide. Idempotent.

  • bin/gate-check.sh <project-dir> [stage] — mechanical stage gates; regenerates the project dashboard from real files. Verdicts are PASS / PENDING (nothing there yet) / FAIL (there and wrong) / WAIVED (declared out of scope in the register, with a reason, via --adopt / --waive) / MANUAL. Never infer a project's position — read the register line, or ask. It also runs the obligation check (bin/lib/obligations.tsv + bin/lib/obligation-check.sh): per-module passes that owe a mark — LOOK, wiring sweep, journeys, coherence — each with the artifact that proves it and the denominator that artifact must state. A pass nobody performed reports PENDING/FAULT, never green-by-absence; --waive look/<Module> --reason "..." records a pass deliberately not performed, and obligations below an --adopt point report ADOPTED. Alongside it runs the artifact check (bin/lib/artifact-manifest.tsv + bin/lib/artifact-check.sh): the producer-side mirror — one row per artifact a stage's "Agent produces" row promises, each row citing the consumer that reads it, so a stage that never emitted its artifact reports PENDING out loud instead of being found by a human reading a failed project. File presence only, never content quality; waivers are register lines (Waived artifact <id>: reason, or the stage-level waiver covering its artifacts wholesale). Third, the source ledger (bin/source-ledger.sh, over the every-file inventory bin/source-sufficiency.sh init writes): each source file must name the artifact that consumed it — text and embedded images/pages — or carry a --waive source/<file> register line; this one blocks Stages 1–2, because "the triage already used the deck" is a grep, not a conversation (real incident, 2026-09-02).

  • bin/sync-project.sh <project-root> — run after every toolkit git pull: refreshes the artifacts that were copied into the project (new intake questions, untouched agent stubs) and flags stale baseline routing. Referenced skills need no sync — they update with the pull.

Live checklist — every stage, in the chat

Every stage and module build follows the Live Checklist Protocol (skills/conversion-runbook.md §1b): post the stage's checklist in chat at start, update with ✅/🔄/⬜/❌/⏭ marks as items land, full repost before every gate. No stage works silently between gates. This is not build-phase-only.

One decision register

All gate decisions land in the consuming project's PROJECT.md, marked CONFIRMED or ASSUMED. The skills/checkpoints/ CAC files are the packaged mechanism that runs the runbook's interview protocol at the six busiest transitions — they write to PROJECT.md, never to a separate state file.

Ask, then stop. Gate questions are actually asked in chat — on Claude Code via AskUserQuestion (clickable options), on any other harness as plain numbered markdown with "something else" always available — and either way the agent ends its turn and waits for the answer. AskUserQuestion is a Claude Code built-in, not something a Copilot/Cursor/Windsurf session can install; the rendering is tool-specific, the asking and the stopping are not. skills/interview-protocol.md §3 → "Asking on a non-Claude agent" is the single source of that rule; every other mention defers there. ASSUMED is earned by asking (user said "you decide"), never by skipping the question — finding the answer in the source justifies the recommendation, not silence. Unattended runs are opt-in only (Interview mode: auto in PROJECT.md, alias unattended, at the user's explicit request; modes in skills/interview-protocol.md).

Key skills and when to load them

Load skill files on demand when the task calls for it — not all upfront. Full routing table: README.md → "When to use which skill". The always-on set (README.md → "Baseline routing"): query-the-model.md, learned-mdl-preflight.md (the mdl helper instead runs bin/mdl-rules.sh for the statement kinds it writes; learned-microflow-patterns.md is on demand), and bin/bug-lookup.sh (a CE code, BUG-n, keyword or the whole pasted error message → the matching ledger entries; bug-logs/mxcli-bugs.md itself is read on demand — it outgrew the always-on budget).

Task Read this file
Any conversion/build — what stage, what gate, who owns it skills/conversion-runbook.md
Audit / lint / regression-test an existing app (no pipeline) skills/existing-app-assurance.md
Deciding what source answers a question, before asking the user skills/query-the-model.md
Running a stage-transition checkpoint (2+1 questions) skills/checkpoints/checkpoint-*.md
Extract-vs-not, extractor coverage, scoping a large source skills/source-triage.md
Building/validating a new extractor for an uncovered stack skills/extractor-quality-loop.md
Running or explaining the extraction pipeline skills/migration-pipeline.md
Mendix module boundaries (Stage 3, before create module) skills/modularize-domain.md
Architecture blueprint: diagrams, fit-gap, marketplace, security, NFRs, integrations skills/architecture-blueprint.md
UI/brand layer: design system, wireframes, branding interview skills/design-artifacts.md
BRDs + architecture → ordered build plan skills/brd-to-build-plan.md
Per-module build discipline (gates, coverage checklist) skills/iterative-build-loop.md
Writing/validating/enriching BRD JSON skills/brd-generation.md, skills/brd-validation.md
Document folder scan / Excel-Word-PDF extraction skills/document-discovery.md, skills/kb-generation.md
OS XML structure skills/source-os11.md + skills/os-xml-schema.md
MDL microflow scripting patterns skills/mdl-cookbook-microflows.md
Page/snippet pre-flight (wireframe → tokens → StyleGallery) skills/ui-preflight-pages.md, skills/learned-stylegallery.md
Diagnosing a mxcli CLI error bug-logs/mxcli-bugs.md
Generating a new project's CLAUDE.md skills/bootstrap-project.md (run mxcli init FIRST — init overwrites, bootstrap merges)
Setting up dev-process subagents (ba/architect/mdl/gate/test/review) skills/agent-roles.md
Past process decisions process/process-learnings.md

Migration assessment (assess-migration) is bundled with mxcli (.ai-context/skills/); skills/assess-migration.md here is a pointer plus toolkit-specific deltas only — the toolkit never duplicates bundled skills.

Pipelines (extraction tooling — code lives in this repo)

Source platform Pipeline Run
OutSystems pipelines/outsystems/ cd pipelines/outsystems/pipeline && npm install — see its README.md / pipeline-guide.html
Java + Angular / Spring Boot pipelines/java-angular/ cd pipelines/java-angular/pipeline && npm install — see its README.md
Node/Express + React pipelines/node-express-react/ — regex-based, proven on one source shape only; read its README's "Known gap" sections first cd pipelines/node-express-react/pipeline && npm install
Mendix (an existing .mpr, rehost or move a module) pipelines/mendix-mpr/ — build-green on a small app only; read its README's "Field runs" first MXCLI=/path/to/mxcli pipelines/mendix-mpr/rehost-extract.sh <source.mpr> <target.mpr> <out-dir> — Python 3 stdlib, no npm install; see its README.md

node_modules/ is gitignored — npm install locally per pipeline. Set local paths in pipelines/<x>/pipeline/config.json; never commit real local paths. Curated sample outputs live under each pipeline.

Consuming this toolkit (reference model)

Clone once, point projects at it — no copies, no drift:

git clone https://github.com/MendixMau/mxcli-project-toolkit.git ~/Mendix/mxcli-project-toolkit

Each project scaffolded with bin/init-project.sh gets the Baseline routing table rendered into its CLAUDE.local.md (kept current by bin/sync-project.sh); its CLAUDE.md carries only a short pointer to that file, never a copy of the table (skills/bootstrap-project.md Step 2). Only a project with no CLAUDE.local.md convention copies the table from README.md into CLAUDE.md directly. For a self-contained handoff, use a git submodule.

Company-specific material never lives here either — own conventions, client-named learnings, house lint rules, approved MPKs go to a company brain, a private repo instantiated from templates/company-brain/ (skills/company-brain.md). The public toolkit never cites one: bin/check-no-private-citations.sh runs in CI.

Project output never lives here — analysis/, sources/, knowledge-base/, *.mpr are gitignored. A project's build plan, PROJECT.md, and session notes live in that project's own repo; promote reusable patterns into skills/learned-*.md instead of accumulating project docs here.

Writing in this toolkit — generic first

Authoring rule. This section governs work on this repo. It is not shipped guidance: consuming projects load their own generated CLAUDE.local.md, and the per-tool files (AGENTS.md, .github/copilot-instructions.md, .cursorrules, .windsurfrules) are POINTERS, never copies — see project-bin/check-root-clean.sh.

Build it generic. Source-specific work stops at the extractor. Everything downstream of the BRD must work for any input — extracted code, documents, SME interview, or requirements typed by hand — and therefore lives in bin/, not in pipelines/<platform>/.

The BRD is the platform boundary. Upstream of it a source is OutSystems, Java, or a folder of epics. Downstream of it there are only entities, microflows, pages and use cases. This repo serves four entry modes (migration, requirements-driven, greenfield, change an existing app) plus à-la-carte use, and only the first of those runs a pipeline at all.

Where things go.

Kind of thing Home Why
Reading a specific source shape pipelines/<platform>/extractors/ genuinely per-stack; extractor-quality-loop.md governs building one for a new stack
Anything reading BRDs bin/, via bin/lib/discover-brds.sh BRDs arrive from extractors, kb-generation.md, SME interviews, or by hand — the reader must not care
Judgement, verdicts, gates skills/ skills-over-scripts.md is the governing rule and it wins over this one

Specialisation is the consumer's job, and it is already supported — extractor-quality-loop.md (any stack: interface, validator contract, run.sh, 6 scored dimensions, 95% gate) plus three worked pipelines to copy. Do not add a scaffold that stamps those files out; a prose spec and worked examples are the correct form here.

Why this is written down (2026-08-19). generate-enrichment-report.js — the Stage 2 surface — was built three times, once inside each pipeline, because nothing said it shouldn't be. The three copies drifted onto three different BRD shapes (module vs modules[], attributeCount vs attributes[], a.to vs target), so a BRD written by hand per brd-generation.md rendered a page of undefined and zeros that looked complete. Requirements-driven and greenfield projects got no Stage 2 surface at all, though conversion-runbook.md promises one unconditionally. The same disease had already been diagnosed one layer down a week earlier — see the header of bin/lib/discover-brds.sh: "That is what duplication costs: the fix does not travel."

Before adding any file under pipelines/, ask: does this read a source, or does it read a BRD? If it reads a BRD, it is in the wrong directory.

Shipping an instrument — field-proof before merge

Every defect in the F-042 cluster (2026-08-28) shipped the same way: an instrument written against imagined input and merged without ever executing against real input. The selftests all passed — selftests prove the logic, and the logic was fine; what was wrong was the assumptions. So, before a new or materially changed instrument under bin/, project-bin/ or project-tests/ merges:

  1. Golden input is captured, never hand-written. A parser of tool output (SHOW PAGES, SHOW MODULES, mxbuild logs) gets a verbatim capture from a real model committed beside its fixture, with the parsed counts asserted. Hand-written test input encodes the same assumption the code makes — F-042's header-row bug (6 real pages counted as 13) was invisible to anything but real output, because real output had columns nobody imagined.
  2. Both layouts. Paths resolve through _common.sh / config-derived roots, probed on single-tree AND two-tree (.mpr under app/) checkouts. F-020 and F-042 are the same bug found twice, three weeks apart, in different files.
  3. Both platforms. Bash runs under Git Bash on Windows, where MSYS maps foo → foo.exe transparently — but Node does not: never execFileSync a bare wrapper name without an .exe/.cmd probe or env override. Guard mac-only tools (sips, /usr/libexec/java_home) behind mxtk_platform / process.platform.
  4. One field run, cited. The commit that ships an instrument names the real project it ran against and what it measured there. An instrument nobody has run is a claim, not a check.
  5. A producer for every consumer. If the instrument reads an artifact (page-scope.json, stack.env), say in its header which mandatory chain step produces that artifact — "documented in a skill" is how design-audit.js sat installed and never ran for an entire build. If no mandatory step produces it, wire one before shipping the consumer.

For an instrument that blocks (a guard, a refusing gate), two extra rules — learned when a context guard blocked a session that had done everything right except use the guard's own checkpoint script, and an expert user's reaction was "I can't even compact?":

  1. A blocking guard must accept evidence it did not itself create — a recent commit, a recently modified handoff doc — never only its own stamp file.
  2. A guard must never block the action that resolves it. Blocking the remedy is perverse: warn once, then let it through.

Merging a PR — independent review before every merge

A merge go from the maintainer is not a review. Before any PR merges, an agent that did not write it reviews the diff and returns a verdict. The builder's own report and green CI never count: the builder grades its own work, and CI proves only the tests the builder wrote.

  1. Who reviews, and with what. A fresh agent given the PR diff, the linked issues and the PR body's description of what changed — never the builder's verdict, risk assessment or "all checks pass" claims. One reviewer per PR, plus one composition check when several PRs in a batch touch the same files. The reviewer may run the fixtures the PR adds or that target its changed files, in a detached worktree — that is part of the review the maintainer asked for, not a free-standing test run (see "Testing this toolkit").
  2. What it answers, each with file:line. Value: does it fix the issue as reported, and is anything overbuilt? Regression risk: what changes for an existing project after sync-project.sh (new FAIL / BLIND / refusals, exit codes, output another tool parses, gate verdicts that flip), across both layouts and macOS / Git Bash? Correctness: real bugs, tests that do not test what they claim, hand-written golden input where a capture was required. Context cost: words added to always-loaded skill files — re-measured by the reviewer, not taken from the PR body.
  3. Verdict. MERGE, MERGE-AFTER-FIX (exact fixes listed; the fixes are pushed and re-reviewed before merge) or HOLD (goes back to the maintainer with the reason). Only MERGE merges.
  4. Record the verdict durably. The squash commit body carries a line Review: MERGE — <one-line top finding or "no findings">, and the merge announcement repeats it next to CI. A merge with no Review: line was not reviewed.
  5. No exceptions for size. A one-line fix, a docs-only PR and an outside contributor's PR all get the same review. "Small", "only skill text" and "CI is green" are how the exceptions start. The one exception is guard rule 7's: a fix whose only purpose is to unblock master (a broken CI check, a leak guard that blocks every commit) may merge first, with its review within 24 hours and a Review: PENDING (unblock) line; the follow-up verdict is recorded as a comment on the merged PR (and a follow-up fix PR if it finds anything), since a squash commit cannot be edited.

Why (real incident, 2026-10-13). Nine PRs merged in one day on the builders' own reports and green CI, each carrying its builder's "all checks pass". When the maintainer asked whether value and regression risk had been checked, the honest answer was no: nobody who had not written the code had read it. The after-the-fact reviews found, among others, a journey preflight that made every synced project's journey rung FAULT, and an exec lock that let three writers in at once. The same day showed the cost of unread diffs in another form — the always-loaded skill text hit its 80,000-word cap because each PR added words that nobody outside the PR weighed.

Always-loaded skill text — the word budget

bin/render-routing.sh --check caps the always-loaded (baseline) skill set at MXTK_BASELINE_BUDGET_WORDS — a ratchet, explained in that script's header: lowered as text moves out, never raised to admit growth. Room is made by moving text, never by deleting the text that does the work.

  1. Protected — never removed to make room: rules, STOP rows, denominators / completion criteria, and the one-line failure example each rule is paired with. Removing one needs its own PR that argues the rule itself is obsolete, with an independent review.
  2. Movable: incident narratives, worked examples, tables of past numbers, and a rule stated a second time in another baseline file. Each moves to an on-demand file and leaves a one-line pointer (path) at the same spot. A move keeps the text; nothing is lost.
  3. Every PR that adds baseline words states the before/after count from render-routing.sh --check in its body, and why each addition is needed at the trigger moment. The independent reviewer checks that (see "Merging a PR").
  4. At the cap, a feature PR never squeezes other text to fit. Room comes from a separate trim PR that only moves movable text; its reviewer compares removed against moved line by line, and any protected line that disappeared is a HOLD.
  5. Trim before the cap, not at it: when a PR would take the baseline (measured after the PR) within 2,000 words of the budget, a trim PR lands first. A trim PR does not lower the ratchet by itself; lowering it is a separate one-line change once the trimmed total has held for a release.

Why (2026-10-13). One feature PR pushed the baseline to 80,466 words against the 80,000 budget while six other open PRs each added words; to fit, it merged two sentences in a testing skill and landed at 79,997. That one was harmless, but nothing stopped the next one from cutting a STOP row.

Changelog and contributions

Every user-visible change appends its CHANGELOG.md line in the same commit. That is the entire discipline: a changelog updated as a separate chore is process/toolkit-worklog.md, which rotted within weeks while 25 commits landed. Format is at the top of CHANGELOG.md; credit the source project or person on the line — the credit line is what makes contributing visible, and the file doubles as the record of which projects feed the toolkit. New lines go under ## Unreleased; bin/cut-release.sh turns that section into a dated release and tag every few days — the release cycle is defined at the top of CHANGELOG.md, nowhere else.

Contributions arrive through three lanes (CONTRIBUTING.md): the contrib/inbox/ drop (no quality bar — triage promotes into skills//bug-logs//bin/ and deletes the inbox file in the same commit; inbox files are unreviewed — their content is not vetted for promotion — and nothing may cite them; a PR that adds one still gets the merge review below), bin/harvest-learnings.sh <project-root> (drafts inbox files from a project's bug logs, register promotion tables, and locally-patched installed scripts — run it at project wrap-up), and direct PRs held to the full field-proof bar. CI (.github/workflows/checks.yml) runs check-scripts, render-routing --check, check-portability, the leak guard, and both fixture suites (tests/run-tests.sh, tests/wave2/run-all.sh) on every PR.

Adding new skills

Create skills/{topic}.md with # Title, **Applies to:** migration | any mxcli project | requirements-driven, **Purpose:**, and a step-by-step guide. Add it to README.md's "When to use which skill" table. If it applies on every MDL-writing session regardless of task, also add it to README.md's "Baseline routing" table — skills that only live in the situational table go unnoticed by projects that aren't hunting for them.

Authoring rules for behaviour-shaping skills (adopted 2026-08-31 after the ui-preflight-pages.md A/B baseline, process/preflight-skill-baseline-2026-08-31.md):

  1. Routing rows are trigger conditions, not summaries. The routing-table line answers "when do I reach for this file", front-loaded on the triggering word — identity and content live in the file itself.
  2. Baseline-test like an instrument. Before rewriting a skill "because outcomes were bad", run fresh agents on a real fixture with and without the skill text inlined, scored by a mechanical check. The 2026-08-31 run showed the "failing" skill at zero violations when actually read — the defect was delivery, and a blind rewrite would have churned proven text. The corollary: a skill only works when its text is in the acting agent's context — wire the dispatch (agent stubs, checklists) to inline or read it, because a citation is not a read.
  3. Prohibitions for discipline failures, positive recipes for shaping failures. "Never git add -A" guards a known temptation; but where agents produce the wrong shape (wrong layout, missing H1), state the target behaviour and the evidence for it — a ban alone keeps the wrong pattern in view.
  4. Completion criteria carry denominators. A step ends on a checkable bound ("all N gallery files listed", "0 violations from the shell check", "NOT RUN is legal, silence is not") — a step ending in prose nobody can fail is the unfalsifiable-checklist defect the obligation check exists to prevent.
  5. No caches of another project's environment. A class table, file inventory, or path list copied into a skill goes stale the day the next project scaffolds differently; instruct "list/grep the actual file" instead (both stale tables in ui-preflight-pages.md were flagged mid-run by the very agents following them).
  6. Write for the trigger, then stop. A skill is retrieved mid-task by an agent with a specific trigger; everything not needed at that moment is dilution — one skill, one situation. (The arm-C verification of the 2026-08-31 baseline saw a possible dilution effect from ~800 words of growth in a single revision.)
  7. Show the failure, not just the rule. Pair each rule with the concrete bad output it prevents — a before/after or a verbatim wrong answer. This repo does it consistently in CLAUDE.md incident notes and inconsistently inside skills/*.md; the skills are where the acting agent reads.
  8. Test retrieval, not just content. A skill can be perfect and unreachable: periodically check that its routing line actually fires for the phrasings users and agents really produce (rule 1 makes the row a trigger condition; this rule is the check that the trigger works). The coverage audit in render-routing.sh --check catches the unrouted case mechanically; wrong-trigger routing it cannot see. tests/advisory/authoring-audit.py routing is that check (BM25 over trigger text vs the labelled routing-phrasings.tsv); its dupes lists rules stated twice. Advisory, never a gate.

(Rules 6–8 adapted 2026-08-31 from public skill-authoring guidance in obra/superpowers writing-skills and Matt Pocock's writing-for-agents — patterns only. Their fourth point, "degrade to a pointer", is already this repo's "single source of the rule; every other mention defers here" convention plus rule 5 — one principle, stated there.)

Committing in this clone — never git add -A

Several Claude sessions routinely have this clone open at once, in one working tree and therefore one git index. So:

Stage and commit in the same breath, or pass explicit paths: git commit -- <paths>. Never git add -A. Before resetting a commit, look at what else is in the index.

Why (real incident, 2026-08-21). A session ran git commit with no pathspec while another session had 29 unrelated files staged. Git takes the whole index, so that work landed inside a commit titled "Log BUG-95: …". The resulting git reset restored the 29 files — and silently took the committer's own bug-log entry out of the working tree with it, where it survived only in a dangling commit. Authorship is not readable from mtimes either: eight files were mislabelled as a peer's until someone read the diffs.

Same root cause as the testing rule below — a shared, moving tree makes any whole-tree operation untrustworthy. Coordinate with ListAgents / SendMessage before committing, and if a peer's untracked scratch file trips the pre-commit leak guard (bin/check-no-client-data.sh scans untracked files too), ask them to fix it. Do not gitignore it and do not narrow the denylist.

Testing this toolkit — scoped by default

tests/wave2/run-all.sh runs every fixture in the suite. Do not run it as a reflex "did I break anything" check after a normal edit. Run only the fixture(s) that exercise the file(s) you actually touched (e.g. changed bin/open-questions.sh → run test-open-questions.sh; changed bin/gate-check.sh → also run test-bug03-gates.sh, test-source-sufficiency-gate.sh, test-source-ledger.sh, whatever else targets it). Grep tests/wave2/ for the filename you changed if you're unsure which fixtures cover it.

There are two separate suites. tests/run-tests.sh [-v] is self-contained (guard-script fixtures with no external argument). Each tests/wave2/test-*.sh fixture instead takes its subject-under-test as $1 — read its own usage: comment rather than assuming a fixed target; several take something other than gate-check.sh (exec.sh, sync-project.sh, check-docs-numbering.sh, a skill file). Once you have permission for a single fixture (see below), run it as e.g. bash tests/wave2/test-bug02-register.sh bin/gate-check.sh.

Why: this repo is routinely edited by more than one agent session at once (real incident, 2026-08-19 — a peer session mid-edit reported known, unrelated failures already present in the shared working tree). A full-suite run over a moving target mixes someone else's in-flight breakage into your result and tells you nothing trustworthy about your own change — it looks like verification but isn't. It is also strictly slower for no more signal than the scoped run, since the untouched fixtures were never at risk.

Run the full suite only when the user explicitly asks for it in that message. There is no other trigger, and none of the following is one: about to open a PR, closing out a multi-file change, or a change that looks like it had wide blast radius. Those read as licence to self-authorize, and they were — the rule said "ask first" and then handed out three reasons not to. Wide blast radius is a reason to pick the right scoped check, not to run everything: for bin/lib/skill-routing.tsv and its re-render (README.md, ROUTING.md, every agent template, and gate-check.sh's stage map at once) that check is bin/render-routing.sh --check.

One standing exception: the independent reviewer of a PR (see "Merging a PR") may run the fixtures the PR adds or that target its changed files, without asking, in a detached worktree of the PR branch. That isolation is what avoids the shared-tree problem above; the reviewer still never runs the full suites.

Outside that reviewer exception, asking is also required for a single fixture — name it, say what it will tell us, and wait.

Where a fixture's assertions are greps over a file you changed, prefer verifying by inspection: read the fixture, replicate its greps against the changed file, and report that. Same signal, no suite run, no interference with whatever another session has in the tree right now.