docs(6209): rulebooks, pull telemetry, lesson usage, duplicate arm, cited status, trigger shape
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 14s
CI & Build / TypeScript typecheck (push) Successful in 54s
CI & Build / integration (push) Successful in 1m3s
CI & Build / Python tests (push) Successful in 1m55s
CI & Build / Build & push image (push) Successful in 13s

Six more test files state their pinned properties without the issue and
milestone history: the pull-telemetry guard explains enumerating kinds
instead of listing two getter incidents, cited-record status explains why
a hint's id needs its status instead of recounting one misreported step,
and the trigger-shape guard keeps its reasoning about why prose heuristics
fail while dropping the dated corpus audit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-21 21:28:35 -04:00
co-authored by Claude Opus 5.5
parent ff08500128
commit 17e1931c0b
6 changed files with 91 additions and 112 deletions
+17 -21
View File
@@ -1,21 +1,18 @@
"""A lesson's surfaced-vs-opened counts reach a reader (#4196).
"""A lesson's surfaced-vs-opened counts reach a reader.
The lesson slot has recorded usage since it shipped, and until this nothing
showed it. `get_lesson`'s REST door attached it; the list did not, neither MCP
door did, and no view rendered it — so the one kind whose central question is
"is this trigger firing on the right situation" was the one kind whose answer
was unreadable.
Every door — the REST list and detail, both MCP doors, and the view — carries
the counts, because the one kind whose central question is "is this trigger
firing on the right situation" needs that answer readable.
WHY THE COUNTS MATTER MORE HERE THAN ON A SNIPPET. #4196 asks whether a lesson
repeatedly followed should become a rule, and names the trap: raw frequency
WHY THE COUNTS MATTER MORE HERE THAN ON A SNIPPET. Whether a lesson repeatedly
followed should become a rule has a trap: raw frequency
cannot tell "this should bind" from "this trigger is too broad", which is the
commoner reading. Surfaced-AND-opened can. Neither question is answerable by
anything that cannot see the numbers, which is why this is the first step and
not the threshold.
NO THRESHOLD IS ASSERTED ANYWHERE HERE, deliberately. At the time of writing
the corpus is 10 lessons with 7 recorded surfacings and 3 opens; a promotion
rule fitted to that would be fitting noise, and `UsageBadge` already declines
NO THRESHOLD IS ASSERTED ANYWHERE HERE, deliberately. A promotion rule fitted
to a small corpus would be fitting noise, and `UsageBadge` already declines
to render a verdict on fewer than three surfacings for the same reason.
"""
import inspect
@@ -115,11 +112,10 @@ async def test_get_lesson_reads_the_count_before_recording_its_own_pull():
assert out["usage"]["pull_count"] == 1
# ── the REST door, on structure (rule 167) ───────────────────────────────────
# ── the REST door, on structure ──────────────────────────────────────────────
#
# MOVED 2026-09-21 (#4230), not weakened. These used to look for
# `usage_for_notes(` and `empty_usage()` in the route body. Both now live in
# ONE seam, `note_usage.attach_usage`, which seven doors share — so the
# `usage_for_notes(` and `empty_usage()` live in ONE seam,
# `note_usage.attach_usage`, which seven doors share — so the
# zero-fill and the single aggregate are pinned once, against the seam, in
# tests/test_usage_attach_seam.py. What stays HERE is what only this route can
# get wrong: that it calls the seam at all, once, and before it records a pull.
@@ -166,7 +162,7 @@ def test_the_rest_detail_door_also_reads_before_it_records():
def test_these_guards_can_fail():
"""Rule 167: falsify the shape they assert against, so a guard that has
"""Falsify the shape they assert against, so a guard that has
quietly stopped describing anything cannot pass by describing nothing."""
src = _route_source("list_lessons_route")
assert "def list_lessons_route" in src
@@ -174,21 +170,21 @@ def test_these_guards_can_fail():
def test_the_detail_view_renders_the_badge_rather_than_respelling_it():
"""Snippet #3460: reach for UsageBadge and pass the kind's own advice; a
view's scoped re-spelling of `.usage-tag` is what that component replaced.
"""Reach for UsageBadge and pass the kind's own advice; a view's scoped
re-spelling of `.usage-tag` is what that component exists to prevent.
The advice is the kind-specific half. For a lesson it points at the
trigger, not at deletion — a lesson nobody opens is usually keyed to a
situation nobody is in, which is `update_lesson`'s own words and the
reading #4196 calls the commoner one.
commoner reading.
"""
view = (Path(__file__).resolve().parents[1]
/ "frontend/src/views/LessonDetailView.vue").read_text()
assert "UsageBadge" in view
assert "usage-tag" not in view, "re-spelled the chip instead of reusing it"
# The sentence moved out of this template into the shared table (#4230,
# recorded on #3460). Assert BOTH halves, so the guard cannot pass by the
# The sentence lives in the shared table, not this template. Assert BOTH
# halves, so the guard cannot pass by the
# view pointing at an entry that no longer says the right thing.
assert "DEAD_WEIGHT_ADVICE.lesson" in view, "the view no longer reads the lesson advice"
table = (Path(__file__).resolve().parents[1]