feat(board): give --plan a focus cutoff that reports what it held back

--focus "<prose>" narrows the day plan to repos whose STATE.md DECLARES a
matching topic marker. Measured on the real tree: 26 blocks to 6.

The filter alone was never the feature. --plan documents that it takes one
position (the order), hides nothing, and labels what it cannot rank rather
than dropping it, so a silent cutoff would break a property the format had
already written down. The same run prints the slugs the prose resolved to,
how many blocks it removed, the repos that MENTION a resolved slug with no
marker line - named, not counted - and how many STATE.md it searched. Each
surviving block carries the declaration it survived on.

Enumerated rather than counted because the decisive find behind this feature
was invisible to every string measurement until the held-back population was
listed. "nevner", never "dekker": this reports text found, and board.sh has
no grounds for a claim about relevance. The slug vocabulary comes from the
scanned STATE.md themselves, so the "no other file" invariant survives.

Emitted as key=value, not '#' comments: the consumer's parser runs in
paragraph mode and drops any block without tab=, so a comment would reach the
operator on the terminal path and vanish on the driver path - the one case
where the cutoff is applied unseen.

board-selftest 89 -> 113. The narrowing check is comparative, since every
presence-only assertion passes against a no-op filter.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186vKCzuUEN5WcJB82kddzF
This commit is contained in:
Kjell Tore Guttormsen 2026-08-02 19:58:25 +02:00
commit f9d2c927ab
4 changed files with 438 additions and 0 deletions

View file

@ -5,6 +5,59 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- **`--focus "<prose>"` — the plan's first cutoff, shipped with the disclosure
that was its precondition.** `--plan` narrows to the repos whose `STATE.md`
*declares* a topic marker (`<slug>: <status>`) matching the operator's prose.
Measured on the real tree: 26 blocks to 6.
The filter alone was never the feature. `--plan` documents that it takes one
position (the order), hides nothing, and labels what it cannot rank rather
than dropping it — so a silent filter would break a property the format had
already written down. The same run therefore prints `fokus=` (the slugs the
prose resolved to), `fokus_droppet=` (blocks removed), `fokus_utenfor=` (the
repos that *mention* a resolved slug with no marker line, **named**) and
`fokus_rekkevidde=` (how many `STATE.md` were searched). Every surviving
block carries `fokus_treff=`, the declaration it survived on — the per-block
evidence that closed `topics=`.
Four constraints are load-bearing rather than stylistic. The held-back class
is **enumerated, not counted**, because in the measurement behind this
feature the decisive find — a heavy consumer pinning the library in its build
file — was invisible to every string measurement and appeared only once the
population was listed. The wording says **"nevner", never "dekker"**: this
reports text found in a file, and `board.sh` has no grounds for a claim about
relevance. The reach line states **which files were searched**, because one
repo's strongest evidence lives in a `README` this scan never opens. And the
slug vocabulary comes from the scanned `STATE.md` themselves, so the
"reads `STATE.md` and no other file" invariant survives — `register.md` owns
the grammar, this only ever reads a declaration written in it.
The reader accepts the marker as operators actually write it (bulleted, bold,
backticked), not only the strict form the register's own grep looks for —
that grep losing a bold declaration is precisely the class that hid the
decisive find.
Prose matching no declared slug prints the **full** plan plus
`fokus_ikke_brukt=`. The driver forwards operator prose verbatim, so a typo
must not silently produce a morning with no tabs. Ambiguous prose widens to
the union and names every slug, since picking one silently would make the
cutoff lie about its own size.
Emitted as `key=value` rather than `#` comments deliberately: the format's
second consumer drops comment lines by rule, so a disclosure written as a
comment would reach the operator on the terminal path and vanish on the
driver path — the one case where the cutoff would be applied unseen.
`board-selftest.sh` 89 -> 113 checks. The narrowing check is comparative
(focused block count strictly below unfocused, and non-zero): every
presence-only assertion passes against a no-op filter, which is exactly the
state the flag was in while it was being swallowed by the unknown-argument
fallback.
## [0.17.0] - 2026-08-02
### Added