From e292b4ebfb753beb028610aa5c4d7a41766f9e56 Mon Sep 17 00:00:00 2001 From: Willem van den Ende Date: Wed, 28 Oct 2026 23:14:18 +0000 Subject: [PATCH] yak(ratchets-deferred-decisions-handoff-practice-ccjp): ratchets, deferred decisions, handoff practice --- AGENTS.md | 61 +++++++++++++++++++++++++++++++++++++++++++++++-- app/Makefile | 10 ++++++-- blogex/Makefile | 10 ++++++-- 3 files changed, 75 insertions(+), 6 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 6610f29..3a274be 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -142,8 +142,65 @@ failure ride along: if it predates your change, stop and discuss it with the use rather than working around it. Browser tests (`make playwright*`) are *external* verification and are **not** -part of the Lens pass. Whether they ever join it is an open decision (see the yak -"ratchets, deferred decisions, handoff practice"). +part of the Lens pass. Decided 2026-10-28 (human, yak "ratchets, deferred +decisions, handoff practice"): it stays that way - Playwright is not wired into +`make verify` or into either `VERIFY_SENSORS`. The targets need a running +Phoenix server or the Docker Chrome container, and a gate that only runs with +docker or sudo is not a gate the Lens pass can rely on. Run them as their own +loop, by hand, when a change touches the browser surface. + +### Landing the plane + +The Lens pass is what you land with, so it is the last step of every session, +not a thing that only happens on push: + +- After any session that changed code, run `make precommit`. It is `make check` + under its session-ending name and costs about a second; there is no session + where it is not worth running. +- Before handing off - to a human, a reviewer, or the next agent - run + `make verify`, or push, which fires `.githooks/pre-push` and runs the same + pass on the same tree. A hand-off note that says "tests pass" without a Lens + pass behind it is a guess, not a hand-off. +- A failing Lens pass is triaged into the classes above (see *Triage classes*) + and **reported** in the hand-off note, with the class it fell in. A nit gets a + yak; it does not get fixed inline mid-build. Never re-run until green. +- `git push --no-verify` stays the deliberate override for a branch you know is + red. It is never how a session lands. + +## Ratchets + +The coverage threshold in `app/mix.exs` and `blogex/mix.exs` (the `threshold:` +inside each `test_coverage/0`) is a **floor**, not a target: + +- After each behavior-test yak lands, re-measure both apps - `make test.coverage` + then `make coverage` inside each app - and raise the floor to the new total. + The convention is integer truncation of the measurement: a measured 82.61% + means a floor of 82. Never above the measurement: a floor that cannot pass is + not a ratchet, it is a red gate. +- Never lower a floor. A legitimate drop is a conversation, not a config edit. + Lowering the number, or adding an `ignore_modules` entry to prop it up, hides + the behavior gap the floor exists to surface. +- The record of the last measurement and its date is the comment above + `test_coverage/0` in that app's `mix.exs`. Update it there, in the same commit + as the number. Do not copy the totals into this file: one record, in the + place the gate reads, is the whole point. + +Two further health numbers are baselines-to-be; neither is `make verify`'s +business yet, and neither is to be invented: + +- **CRAP offender count.** `make crap` in one app is the sensor: a passing run + prints `Summary: ... worst_score=...`, a failing run prints `High scores: N` + and the offending functions. Both apps sit under the default `--max-score + (30)`, and the decision (2026-10-28) not to lower it, plus the baseline + offender count, live in the comment above the `crap` target in + `app/Makefile` / `blogex/Makefile`. +- **Mutation survivor count.** Mutation is a slot comment only in + `app/Makefile` and `blogex/Makefile` ("yak: mutation testing with muex - a + sensor, not a gate"), and `muex` is a dependency of neither app. Nothing has + been mutated, so there is no survivor baseline; do not conjure one from a run + that did not happen. When the mutation yak lands, its survivors become the + baseline the same way the coverage floor did: measured, dated, recorded where + the sensor lives. # Development Always fix failing tests, credo issues and format isseus. diff --git a/app/Makefile b/app/Makefile index db6e82b..2bb9fb9 100644 --- a/app/Makefile +++ b/app/Makefile @@ -58,8 +58,14 @@ coverage: # report scores that are pessimistic instead of absent - a false failure is # less bad than a false pass, but neither is acceptable in the Lens pass. # -# Threshold is ex_crap's default (30); whether to ratchet it down is an open -# decision tracked in the "ratchets, deferred decisions, handoff practice" yak. +# Threshold is ex_crap's default (30), kept deliberately: decision 2026-10-28 +# (human, yak "ratchets, deferred decisions, handoff practice") is not to +# ratchet --max-score down yet - below 30 is a policy change, not a cleanup. +# Baseline at that threshold, same date: 0 offenders above 30, worst function +# FirehoseWeb.CoreComponents.table/1 at 20.00 ("Summary: files=23 functions=140 +# scored=140 worst_score=20.00"). A failing run prints "High scores: N" plus the +# offending functions; on a passing run read N=0 off the worst score rather than +# inventing a count. crap: @test -f cover/default.coverdata || { echo "crap: no exported coverage - run 'make test.coverage' first" >&2; exit 1; } MIX_ENV=test $(MISE_EXEC) mix crap diff --git a/blogex/Makefile b/blogex/Makefile index bcce088..00a20c3 100644 --- a/blogex/Makefile +++ b/blogex/Makefile @@ -54,8 +54,14 @@ coverage: # report scores that are pessimistic instead of absent - a false failure is # less bad than a false pass, but neither is acceptable in the Lens pass. # -# Threshold is ex_crap's default (30); whether to ratchet it down is an open -# decision tracked in the "ratchets, deferred decisions, handoff practice" yak. +# Threshold is ex_crap's default (30), kept deliberately: decision 2026-10-28 +# (human, yak "ratchets, deferred decisions, handoff practice") is not to +# ratchet --max-score down yet - below 30 is a policy change, not a cleanup. +# Baseline at that threshold, same date: 0 offenders above 30, worst functions +# Blogex.LinkValidator.validate_link/1 and validate_links/1 at 6.00 +# ("Summary: files=12 functions=61 scored=61 worst_score=6.00"). A failing run +# prints "High scores: N" plus the offending functions; on a passing run read +# N=0 off the worst score rather than inventing a count. crap: @test -f cover/default.coverdata || { echo "crap: no exported coverage - run 'make test.coverage' first" >&2; exit 1; } MIX_ENV=test $(MISE_EXEC) mix crap