yak(ratchets-deferred-decisions-handoff-practice-ccjp): ratchets, deferred decisions, handoff practice

This commit is contained in:
Willem van den Ende
2026-10-28 23:14:18 +00:00
parent b72446b80e
commit e292b4ebfb
3 changed files with 75 additions and 6 deletions
+59 -2
View File
@@ -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.
+8 -2
View File
@@ -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
+8 -2
View File
@@ -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