Green Gate
Autonomous quality-gate loop for Dart and Flutter packages. Runs four gates —
analyze, format, test, coverage — reads real tool output, edits code and tests
to fix failures, and loops until one final iteration proves all four pass
simultaneously with observed numbers. Acts autonomously on objective failures;
escalates only on stalls, genuine ambiguity, or infrastructure failure.
This skill orchestrates tools and edits files. It defers the how of writing
tests to the testing skill — it never duplicates mocking, structure, or
coverage-pattern guidance.
Core Standards
Apply these to ALL green-gate work:
- MCP tools only, never the Bash equivalent — analyze via
mcp__dart__analyze_files, format via mcp__dart__dart_format, test and coverage
via mcp__very-good-cli__test. Every gate has an MCP tool; none of them runs
through a shell command. The Bash test path (very_good test, flutter test,
dart test) is hook-blocked by block-cli-workarounds.sh and will be denied, and
dart analyze / dart format via Bash are redundant with the MCP tools.
Bash is reserved for parsing coverage/lcov.info — nothing else. This MCP-only
rule is a Claude Code constraint enforced by that hook; the hook does not run on
other hosts. Cross-harness fallback: on a host without the hook and without the
MCP servers connected, run the equivalent dart analyze, dart format, and
very_good test CLI commands instead — never block on a missing MCP server.
- A plan-only request is still this skill's job — when the user asks which
tools, which arguments, or what order the gates run in and does not want a run
yet, answer from this skill: the same tool calls (
mcp__dart__analyze_files
with applyFixes: true, mcp__dart__dart_format, mcp__very-good-cli__test
with the coverage triple), in gate order, with the precedence rule that makes
the order matter. Never substitute an improvised shell plan of flutter analyze / flutter test --coverage for the tools the loop actually runs.
- Never cache green — re-evaluate every gate every round. Fixing analyze or
test failures and writing new test files shifts both formatting and the
coverage denominator, so a previously green gate can regress.
- Exit only on observed numbers — the loop terminates only after a single
final iteration in which analyze is clean, format reports zero changes, all
tests pass, and
min_coverage is satisfied, all observed in the same round.
Declaring success from memory is forbidden; confirm success only with the
actual numbers observed in that final round.
- Pass
coverage: true, min_coverage, and check_ignore: true together —
omitting coverage: true silently produces no lcov.info (mimics a
misconfiguration); omitting check_ignore: true makes the // coverage:ignore
remedy a no-op.
- Defer test-writing to the
testing skill — when a fix requires authoring
tests, follow skills/testing/SKILL.md for structure, mocking, and naming.
- Fix root causes, not symptoms — never weaken a gate to pass it (do not
delete failing assertions, lower the target to dodge work, or
// coverage:ignore
reachable code). Escalate genuine product/API decisions instead of guessing.
- Act autonomously on objective failures — analyzer errors, red tests, and
coverage gaps are fixed without re-prompting. Escalate only per the matrix.
The Loop
For each package root (see Recursive / Monorepo), run this algorithm:
- Discover — resolve the package root (the
directory argument or the
workspace root); confirm a pubspec.yaml exists. Initialize loop state
(iteration counter 0, empty fingerprints, empty touched-files set).
- Analyze — run
mcp__dart__analyze_files with applyFixes: true. If any
errors remain, this is the active gate. Fix, record the fingerprint, go to
step 7.
- Format — run
mcp__dart__dart_format on the package root. It reformats
the whole package in place. If it reports changed files, the gate is now
green for the next round.
- Test — only if analyze is green this round. Run
mcp__very-good-cli__test
with the coverage parameters from Test Gate. If tests fail, fix, record
the fingerprint, go to step 7.
- Coverage — only if tests pass this round. The MCP
min_coverage result is
authoritative pass/fail. Parse coverage/lcov.info for the displayed
percentage and per-file fix targets (advisory). If below target, author tests
for the ranked under-covered files (via the testing skill), go to step 7.
- Exit — if all four gates are green in this same iteration, confirm
success with the observed numbers and stop. This is the only exit-green path.
- Re-verify — increment the iteration counter, recompute the failure
fingerprint, check escalation triggers (no progress, oscillation, cap). If a
trigger fires, escalate; otherwise loop back to step 2 and re-evaluate every
gate.
One-pass no-op path — invoked on an already-green package, iteration 1
finds analyze clean, format reporting zero changes, all tests passing, and
coverage at or above target. The loop confirms green and exits without
editing a single file. A green package costs exactly one verify iteration.
Loop State
State carried across iterations — without it, "no progress" is undecidable:
| State |
Purpose |
| Iteration counter |
Enforce the cap (default 5, per package) |
| Per-gate failure fingerprint (prior round) |
Detect no-progress and oscillation |
| Files touched this round |
Distinguish a no-op round from a no-progress round; populate escalation reports |
Fingerprint keys per gate:
| Gate |
Fingerprint |
| Analyze |
Sorted set of diagnosticCode @ file:line |
| Format |
Set of files the format gate rewrote (empty = green) |
| Test |
Set of failing test IDs / names |
| Coverage |
Observed percentage + sorted set of under-covered SF files |
Definitions:
- No progress — the current failure fingerprint is identical to the prior
round's, or its failure count did not decrease. Escalate.
- Oscillation — the same two gates trade green/red across two consecutive
rounds (e.g. a format fix re-breaks analyze, whose fix re-breaks format).
Escalate.
Gate Precedence
Fixed order: analyze → format → test → coverage. Two rules govern it:
- A downstream gate is not assessed until the upstream gate is green this
round. A red analyzer can make tests fail to compile; coverage is
meaningless when tests do not pass — never parse
lcov.info after a compile
failure. Format runs after analyze so its reformatting does not churn over
code that analyze's applyFixes is about to rewrite.
- Every gate is re-evaluated every round; green is never cached. Exit
requires all four green in the same final iteration.
Analyze Gate
- Run
mcp__dart__analyze_files with applyFixes: true so quick fixes are
applied before diagnostics return. roots takes [{ root: "file:///abs/path" }]
— a file: URI of the package root. There is no recursive flag; the skill
enumerates package roots itself and passes each.
- The gate is green when zero errors remain. Treat error-severity diagnostics
as blocking; address warnings and infos as well when they are within scope of
the fix, but do not let an unrelated pre-existing info block the gate.
- Hook interplay — the PostToolUse
analyze.sh hook fires on every
Edit/Write and exits 2 (blocking) when a fix introduces a new analyze error.
Treat that rejection as analyze-gate feedback in the same round, not a separate
failure mode: the edit did not land, so revise it.
Format Gate
- Run
mcp__dart__dart_format each round. roots takes the same
[{ root: "file:///abs/path" }] shape as analyze_files; omit the optional
paths so it formats the whole package in place. That makes the gate
observation-based (it catches manual edits and pre-existing drift, not just
files the loop edited) and self-fixing (one call leaves the package green; a
second call reports zero changes).
- Read the changed count, not the status — the tool succeeds whether or not it
rewrote anything, so a non-error result proves nothing. Its output ends in
Formatted N files (M changed); the gate is green only when M is 0. On a
round where M is non-zero the files are already fixed, so re-run the gate next
round to observe the 0 rather than declaring it green from the fix.
- Why format is a real gate, not just the hook — the PostToolUse
format.sh
hook fires only on files the loop edits via Edit/Write. A hand-edited or
pre-existing unformatted file the loop never touches would otherwise pass
green-gate and then fail CI. The whole-package format closes that gap.
Test Gate
- Run
mcp__very-good-cli__test with coverage: true, min_coverage: <target>,
check_ignore: true, and the exclude_coverage glob (below).
- Dart vs Flutter — omit
dart to let the tool auto-detect (Flutter is run
when a Flutter project is detected). Pass dart: true only for a pure Dart
package the tool would otherwise misclassify.
directory — pass it when the package is not at the workspace root
(monorepo sub-packages); omit it only when pubspec.yaml is at the root.
timeout_seconds — always set a cap (e.g. 120). Flutter tests hang
indefinitely when pumpAndSettle() runs without a timeout; the cap kills the
run instead of stalling the loop. A timeout kill is a tool failure, not a test
failure — escalate it per the matrix.
- The gate is green when every test passes. A failing test is fixed
autonomously unless it encodes a genuine product decision (escalate per the
matrix).
Coverage Gate
The MCP min_coverage result is authoritative for pass/fail. The
coverage/lcov.info parse is advisory — it supplies the displayed
percentage and per-file fix targets.
- Default target is
100 (VGV's house standard), overridable per
invocation (e.g. 80) for legacy or non-template packages. Generated files
leave the denominator via exclude_coverage and check_ignore: true honors
// coverage:ignore, so 100% means 100% of testable, hand-written code.
- Default
exclude_coverage — **/*.{g,freezed,gen}.dart plus generated and
l10n directories. One glob string (brace expansion; fall back to **/*.g.dart
if a CLI build does not honor it).
- Pass
coverage: true, min_coverage, and check_ignore: true together —
see Core Standards.
- When
coverage/lcov.info is missing or below target, follow the decision
tree and parsing rules in references/coverage.md —
it covers the three absence causes, stale-lcov handling, the lcov record fields
(SF/LF/LH/DA), the advisory-mirrors-the-gate rule, and the
check_ignore Dart-only limitation.
Fixing
- Fix only failing items — address the diagnostics, tests, or under-covered
files surfaced this round. Do not refactor unrelated code (YAGNI).
- Coverage fixes = author tests — for each ranked under-covered
SF file,
write tests following skills/testing/SKILL.md. Prioritize files by uncovered
line count (LF - LH).
- Bound files per round — fix a coherent batch, then re-verify. Re-running
the gates after each batch is what makes "no progress" detectable and prevents
fixing one gate while silently breaking another.
- Never weaken a gate — no deleted assertions, no lowered target to dodge
work, no
// coverage:ignore on reachable code.
Escalation
Stop and surface to the user when:
| Trigger |
Detail |
| No progress between rounds |
Identical or non-decreasing failure fingerprint |
| Oscillation |
The same two gates trade green/red across two rounds |
| Cap reached, gates still red |
Terminal — report remaining failures per gate with their fingerprints |
| Real-bug red test |
A failure that looks like a genuine product decision rather than a coding mistake |
| Ambiguous fix |
Multiple valid resolutions (e.g. change the API vs suppress the lint) — prefer root-cause; escalate when it is a product/API decision |
| Unreachable-code coverage gap |
Suggest // coverage:ignore (requires check_ignore: true, Dart-only) rather than chasing 100% |
| Denominator hygiene |
A generated file not matched by the exclude glob — widen exclude_coverage, not // coverage:ignore |
| Tool / hook failure |
MCP test timeout (timeout_seconds kill), analyzer crash, CLI-missing hook denial (escalate with the install hint dart pub global activate very_good_cli), or a repeated analyze.sh rejection that still blocks a needed edit after revision — a single rejection is in-loop analyze-gate feedback (see Analyze Gate), not an escalation |
When escalating, name the gate that is red — analyze, format, test, or coverage —
then give its fingerprint entries verbatim (diagnosticCode @ file:line for
analyze, failing test names for test, under-covered SF paths for coverage),
the files touched, the iteration count, and the one decision the user must make.
A summary count ("4 errors remain") is not a report; the user cannot act on it.
A standing instruction to keep going does not override a trigger. "Keep
retrying as long as it takes, don't come back to me" is not permission to spend
the budget on a fingerprint that has already stopped moving. Stop at the trigger,
report, and wait.
Recursive / Monorepo
- All packages must pass — continue on failure (fix every failing package),
then confirm each package's result. One package's red gate does not abort the others.
- Per-package iteration budget — the cap of 5 is per package, not global, so
a 12-package monorepo does not exhaust a global budget on package one.
- Shared package-root discovery — walk for
pubspec.yaml files. The
analyze_files roots set must match the package set
mcp__very-good-cli__test --recursive (recursive: true) covers.
- lcov path is
<package>/coverage/lcov.info — resolved per discovered root.
- Single
min_coverage applies to all packages — documented limitation: the
tool schema has no per-package coverage override. State the shared target when
confirming coverage.
Additional Resources
references/coverage.md — green-gate's coverage-gate
detail (default target, exclude globs, lcov fields, decision tree,
check_ignore, stale lcov).
skills/testing/SKILL.md — how to write Dart unit, Flutter widget, and golden
tests (structure, mocktail mocking, naming).
skills/testing/references/coverage.md — coverage-driven test patterns
(copyWith, branches, error paths) for closing per-file gaps.
hooks/scripts/block-cli-workarounds.sh — why the Bash test path is blocked and
every gate runs through its MCP tool.
1---2name: green-gate3description: Drives a Dart or Flutter package fully green through an autonomous verify-fix-rerun loop across four quality gates: analyze, format, test, and coverage, exiting only when one final iteration proves all four pass with observed numbers. It also owns gate configuration, so plan-only questions belong here: which tool and arguments run each gate, in what order, the coverage target, what leaves the coverage denominator, coverage ignore comments, "just re-check coverage", and "confirm the package is green". Use it when the user says "green gate", "make it green", "get CI green", "fix all the analyze and test failures", "clean this package up before I open a PR", "bring coverage to 100", or "loop until everything passes", and when a run stalls with the same failures repeating round after round. Prefer it over the single-gate testing or analysis skills when a request spans multiple gates or asks to fix and re-verify until clean.4---56# Green Gate78Autonomous quality-gate loop for Dart and Flutter packages. Runs four gates —9analyze, format, test, coverage — reads real tool output, edits code and tests10to fix failures, and loops until one final iteration proves all four pass11simultaneously with observed numbers. Acts autonomously on objective failures;12escalates only on stalls, genuine ambiguity, or infrastructure failure.1314This skill orchestrates tools and edits files. It defers the *how* of writing15tests to the `testing` skill — it never duplicates mocking, structure, or16coverage-pattern guidance.1718---1920## Core Standards2122Apply these to ALL green-gate work:2324- **MCP tools only, never the Bash equivalent** — analyze via25 `mcp__dart__analyze_files`, format via `mcp__dart__dart_format`, test and coverage26 via `mcp__very-good-cli__test`. Every gate has an MCP tool; none of them runs27 through a shell command. The Bash test path (`very_good test`, `flutter test`,28 `dart test`) is hook-blocked by `block-cli-workarounds.sh` and will be denied, and29 `dart analyze` / `dart format` via Bash are redundant with the MCP tools.30 **Bash is reserved for parsing `coverage/lcov.info` — nothing else.** This MCP-only31 rule is a Claude Code constraint enforced by that hook; the hook does not run on32 other hosts. **Cross-harness fallback:** on a host without the hook and without the33 MCP servers connected, run the equivalent `dart analyze`, `dart format`, and34 `very_good test` CLI commands instead — never block on a missing MCP server.35- **A plan-only request is still this skill's job** — when the user asks which36 tools, which arguments, or what order the gates run in and does not want a run37 yet, answer from this skill: the same tool calls (`mcp__dart__analyze_files`38 with `applyFixes: true`, `mcp__dart__dart_format`, `mcp__very-good-cli__test`39 with the coverage triple), in gate order, with the precedence rule that makes40 the order matter. Never substitute an improvised shell plan of `flutter41 analyze` / `flutter test --coverage` for the tools the loop actually runs.42- **Never cache green** — re-evaluate every gate every round. Fixing analyze or43 test failures and writing new test files shifts both formatting and the44 coverage denominator, so a previously green gate can regress.45- **Exit only on observed numbers** — the loop terminates only after a single46 final iteration in which analyze is clean, format reports zero changes, all47 tests pass, and `min_coverage` is satisfied, all observed in the same round.48 Declaring success from memory is forbidden; confirm success only with the49 actual numbers observed in that final round.50- **Pass `coverage: true`, `min_coverage`, and `check_ignore: true` together** —51 omitting `coverage: true` silently produces no `lcov.info` (mimics a52 misconfiguration); omitting `check_ignore: true` makes the `// coverage:ignore`53 remedy a no-op.54- **Defer test-writing to the `testing` skill** — when a fix requires authoring55 tests, follow `skills/testing/SKILL.md` for structure, mocking, and naming.56- **Fix root causes, not symptoms** — never weaken a gate to pass it (do not57 delete failing assertions, lower the target to dodge work, or `// coverage:ignore`58 reachable code). Escalate genuine product/API decisions instead of guessing.59- **Act autonomously on objective failures** — analyzer errors, red tests, and60 coverage gaps are fixed without re-prompting. Escalate only per the matrix.6162---6364## The Loop6566For each package root (see **Recursive / Monorepo**), run this algorithm:67681. **Discover** — resolve the package root (the `directory` argument or the69 workspace root); confirm a `pubspec.yaml` exists. Initialize loop state70 (iteration counter `0`, empty fingerprints, empty touched-files set).712. **Analyze** — run `mcp__dart__analyze_files` with `applyFixes: true`. If any72 errors remain, this is the active gate. Fix, record the fingerprint, go to73 step 7.743. **Format** — run `mcp__dart__dart_format` on the package root. It reformats75 the whole package in place. If it reports changed files, the gate is now76 green for the next round.774. **Test** — only if analyze is green this round. Run `mcp__very-good-cli__test`78 with the coverage parameters from **Test Gate**. If tests fail, fix, record79 the fingerprint, go to step 7.805. **Coverage** — only if tests pass this round. The MCP `min_coverage` result is81 authoritative pass/fail. Parse `coverage/lcov.info` for the displayed82 percentage and per-file fix targets (advisory). If below target, author tests83 for the ranked under-covered files (via the `testing` skill), go to step 7.846. **Exit** — if all four gates are green in *this same iteration*, confirm85 success with the observed numbers and stop. This is the only exit-green path.867. **Re-verify** — increment the iteration counter, recompute the failure87 fingerprint, check escalation triggers (no progress, oscillation, cap). If a88 trigger fires, escalate; otherwise loop back to step 2 and re-evaluate **every**89 gate.9091**One-pass no-op path** — invoked on an already-green package, iteration 192finds analyze clean, format reporting zero changes, all tests passing, and93coverage at or above target. The loop confirms green and exits without94editing a single file. A green package costs exactly one verify iteration.9596---9798## Loop State99100State carried across iterations — without it, "no progress" is undecidable:101102| State | Purpose |103| ---------------------------------------------- | ------------------------------------------------------------------------------- |104| **Iteration counter** | Enforce the cap (default 5, per package) |105| **Per-gate failure fingerprint (prior round)** | Detect no-progress and oscillation |106| **Files touched this round** | Distinguish a no-op round from a no-progress round; populate escalation reports |107108Fingerprint keys per gate:109110| Gate | Fingerprint |111| ------------ | ------------------------------------------------------------ |112| **Analyze** | Sorted set of `diagnosticCode @ file:line` |113| **Format** | Set of files the format gate rewrote (empty = green) |114| **Test** | Set of failing test IDs / names |115| **Coverage** | Observed percentage + sorted set of under-covered `SF` files |116117Definitions:118119- **No progress** — the current failure fingerprint is identical to the prior120 round's, or its failure count did not decrease. Escalate.121- **Oscillation** — the same two gates trade green/red across two consecutive122 rounds (e.g. a format fix re-breaks analyze, whose fix re-breaks format).123 Escalate.124125---126127## Gate Precedence128129Fixed order: **analyze → format → test → coverage**. Two rules govern it:1301311. **A downstream gate is not assessed until the upstream gate is green this132 round.** A red analyzer can make tests fail to compile; coverage is133 meaningless when tests do not pass — never parse `lcov.info` after a compile134 failure. Format runs after analyze so its reformatting does not churn over135 code that analyze's `applyFixes` is about to rewrite.1362. **Every gate is re-evaluated every round; green is never cached.** Exit137 requires all four green in the same final iteration.138139---140141## Analyze Gate142143- Run `mcp__dart__analyze_files` with `applyFixes: true` so quick fixes are144 applied before diagnostics return. `roots` takes `[{ root: "file:///abs/path" }]`145 — a `file:` URI of the package root. There is **no recursive flag**; the skill146 enumerates package roots itself and passes each.147- The gate is green when zero **errors** remain. Treat error-severity diagnostics148 as blocking; address warnings and infos as well when they are within scope of149 the fix, but do not let an unrelated pre-existing info block the gate.150- **Hook interplay** — the PostToolUse `analyze.sh` hook fires on every151 Edit/Write and exits 2 (blocking) when a fix introduces a new analyze error.152 Treat that rejection as analyze-gate feedback in the same round, not a separate153 failure mode: the edit did not land, so revise it.154155---156157## Format Gate158159- Run `mcp__dart__dart_format` each round. `roots` takes the same160 `[{ root: "file:///abs/path" }]` shape as `analyze_files`; omit the optional161 `paths` so it formats the **whole package** in place. That makes the gate162 observation-based (it catches manual edits and pre-existing drift, not just163 files the loop edited) and self-fixing (one call leaves the package green; a164 second call reports zero changes).165- **Read the changed count, not the status** — the tool succeeds whether or not it166 rewrote anything, so a non-error result proves nothing. Its output ends in167 `Formatted N files (M changed)`; the gate is green only when `M` is `0`. On a168 round where `M` is non-zero the files are already fixed, so re-run the gate next169 round to observe the `0` rather than declaring it green from the fix.170- **Why format is a real gate, not just the hook** — the PostToolUse `format.sh`171 hook fires only on files the loop edits via Edit/Write. A hand-edited or172 pre-existing unformatted file the loop never touches would otherwise pass173 green-gate and then fail CI. The whole-package format closes that gap.174175---176177## Test Gate178179- Run `mcp__very-good-cli__test` with `coverage: true`, `min_coverage: <target>`,180 `check_ignore: true`, and the `exclude_coverage` glob (below).181- **Dart vs Flutter** — omit `dart` to let the tool auto-detect (Flutter is run182 when a Flutter project is detected). Pass `dart: true` only for a pure Dart183 package the tool would otherwise misclassify.184- **`directory`** — pass it when the package is not at the workspace root185 (monorepo sub-packages); omit it only when `pubspec.yaml` is at the root.186- **`timeout_seconds`** — always set a cap (e.g. `120`). Flutter tests hang187 indefinitely when `pumpAndSettle()` runs without a timeout; the cap kills the188 run instead of stalling the loop. A timeout kill is a tool failure, not a test189 failure — escalate it per the matrix.190- The gate is green when every test passes. A failing test is fixed191 autonomously unless it encodes a genuine product decision (escalate per the192 matrix).193194---195196## Coverage Gate197198The MCP `min_coverage` result is **authoritative** for pass/fail. The199`coverage/lcov.info` parse is **advisory** — it supplies the displayed200percentage and per-file fix targets.201202- **Default target is `100`** (VGV's house standard), **overridable per203 invocation** (e.g. `80`) for legacy or non-template packages. Generated files204 leave the denominator via `exclude_coverage` and `check_ignore: true` honors205 `// coverage:ignore`, so 100% means 100% of testable, hand-written code.206- **Default `exclude_coverage`** — `**/*.{g,freezed,gen}.dart` plus generated and207 l10n directories. One glob string (brace expansion; fall back to `**/*.g.dart`208 if a CLI build does not honor it).209- **Pass `coverage: true`, `min_coverage`, and `check_ignore: true` together** —210 see **Core Standards**.211- **When `coverage/lcov.info` is missing or below target**, follow the decision212 tree and parsing rules in [`references/coverage.md`](references/coverage.md) —213 it covers the three absence causes, stale-lcov handling, the lcov record fields214 (`SF`/`LF`/`LH`/`DA`), the advisory-mirrors-the-gate rule, and the215 `check_ignore` Dart-only limitation.216217---218219## Fixing220221- **Fix only failing items** — address the diagnostics, tests, or under-covered222 files surfaced this round. Do not refactor unrelated code (YAGNI).223- **Coverage fixes = author tests** — for each ranked under-covered `SF` file,224 write tests following `skills/testing/SKILL.md`. Prioritize files by uncovered225 line count (`LF - LH`).226- **Bound files per round** — fix a coherent batch, then re-verify. Re-running227 the gates after each batch is what makes "no progress" detectable and prevents228 fixing one gate while silently breaking another.229- **Never weaken a gate** — no deleted assertions, no lowered target to dodge230 work, no `// coverage:ignore` on reachable code.231232---233234## Escalation235236Stop and surface to the user when:237238| Trigger | Detail |239| --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |240| **No progress between rounds** | Identical or non-decreasing failure fingerprint |241| **Oscillation** | The same two gates trade green/red across two rounds |242| **Cap reached, gates still red** | Terminal — report remaining failures per gate with their fingerprints |243| **Real-bug red test** | A failure that looks like a genuine product decision rather than a coding mistake |244| **Ambiguous fix** | Multiple valid resolutions (e.g. change the API vs suppress the lint) — prefer root-cause; escalate when it is a product/API decision |245| **Unreachable-code coverage gap** | Suggest `// coverage:ignore` (requires `check_ignore: true`, Dart-only) rather than chasing 100% |246| **Denominator hygiene** | A generated file not matched by the exclude glob — widen `exclude_coverage`, not `// coverage:ignore` |247| **Tool / hook failure** | MCP test timeout (`timeout_seconds` kill), analyzer crash, CLI-missing hook denial (escalate with the install hint `dart pub global activate very_good_cli`), or a *repeated* `analyze.sh` rejection that still blocks a needed edit after revision — a single rejection is in-loop analyze-gate feedback (see **Analyze Gate**), not an escalation |248249When escalating, name the gate that is red — analyze, format, test, or coverage —250then give its fingerprint entries verbatim (`diagnosticCode @ file:line` for251analyze, failing test names for test, under-covered `SF` paths for coverage),252the files touched, the iteration count, and the one decision the user must make.253A summary count ("4 errors remain") is not a report; the user cannot act on it.254255**A standing instruction to keep going does not override a trigger.** "Keep256retrying as long as it takes, don't come back to me" is not permission to spend257the budget on a fingerprint that has already stopped moving. Stop at the trigger,258report, and wait.259260---261262## Recursive / Monorepo263264- **All packages must pass** — continue on failure (fix every failing package),265 then confirm each package's result. One package's red gate does not abort the others.266- **Per-package iteration budget** — the cap of 5 is per package, not global, so267 a 12-package monorepo does not exhaust a global budget on package one.268- **Shared package-root discovery** — walk for `pubspec.yaml` files. The269 `analyze_files` `roots` set must match the package set270 `mcp__very-good-cli__test --recursive` (`recursive: true`) covers.271- **lcov path is `<package>/coverage/lcov.info`** — resolved per discovered root.272- **Single `min_coverage` applies to all packages** — documented limitation: the273 tool schema has no per-package coverage override. State the shared target when274 confirming coverage.275276---277278## Additional Resources279280- [`references/coverage.md`](references/coverage.md) — green-gate's coverage-gate281 detail (default target, exclude globs, lcov fields, decision tree,282 `check_ignore`, stale lcov).283- `skills/testing/SKILL.md` — how to write Dart unit, Flutter widget, and golden284 tests (structure, `mocktail` mocking, naming).285- `skills/testing/references/coverage.md` — coverage-driven test patterns286 (`copyWith`, branches, error paths) for closing per-file gaps.287- `hooks/scripts/block-cli-workarounds.sh` — why the Bash test path is blocked and288 every gate runs through its MCP tool.