docs(6209): preference door, systems and notes tools, lesson surfacing, rule versions, backup round trip, JSON reader, charter search, provenance, location filter

Ten more test files: issue, milestone, rule, decision and snippet numbers
come out of docstrings, comments and messages; the preference door states
what a preference is instead of quoting the framing; the JSON reader says
why jq is not assumed instead of what every hook used to do; the backup
round trip's column warning no longer claims the import side is unguarded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-21 21:33:08 -04:00
co-authored by Claude Opus 5.5
parent fa41e006a7
commit ead4a7e2ad
10 changed files with 69 additions and 74 deletions
+11 -13
View File
@@ -1,12 +1,10 @@
"""JSON, read and written by the hooks without jq (#4107).
"""JSON, read and written by the hooks without jq.
WHAT THIS REPLACED, AND WHY IT NEEDS TESTING AT ALL. Every hook used to open
`command -v jq >/dev/null 2>&1 || exit 0`, so on a machine without jq the
operator got no session context, no rules, no prior art and no process sync,
and not one word saying why — `exit 0` is indistinguishable from "ran fine,
nothing to say". jq is absent by default on macOS, on the Debian/Ubuntu slim
images, on Alpine and in most CI containers. The parser that replaced it is
`plugin/hooks/scribe_json.awk`, written in POSIX awk, and a parser is exactly
WHY NOT jq, AND WHY THIS NEEDS TESTING AT ALL. jq is absent by default on
macOS, on the Debian/Ubuntu slim images, on Alpine and in most CI containers,
and a hook that exits quietly without it gives the operator no session context,
no rules, no prior art and no process sync, with not one word saying why. The
parser is `plugin/hooks/scribe_json.awk`, written in POSIX awk, and a parser is exactly
the kind of thing that works on the six documents you tried it on.
So the centre of this file is a DIFFERENTIAL against Python's `json`: the same
@@ -16,7 +14,7 @@ is one the hooks can be handed an arbitrary event with.
The rest pins the three jobs around it — writing the hook envelope,
percent-encoding a URL, and bounding a turn in a transcript — and that each
guard here can fail (rule 167).
guard here can fail.
"""
from __future__ import annotations
@@ -74,7 +72,7 @@ def reference_flatten(value, path="", out=None):
shell reads it back with. Every array also reports its LENGTH at `[#]`,
including an empty one, and an empty object reports `{#}` 0 — that is what
lets a caller tell "the server answered with zero notes" from "the server
did not answer", which #2932 built an outage marker around.
did not answer", which the outage marker is built around.
"""
out = [] if out is None else out
if isinstance(value, dict):
@@ -221,7 +219,7 @@ def test_an_absent_field_and_a_null_field_both_read_as_empty():
def test_an_absent_array_length_is_empty_not_zero():
"""NOT the same answer as 0, and the distinction is load-bearing: the
record nudge fires when the server said "zero notes" and must not fire
when the server said nothing at all (#2932)."""
when the server said nothing at all."""
out = sh(r"""flat=$(scribe_json_flat)
printf '[%s][%s]\n' "$(scribe_json_len "$flat" '.note_ids')" \
"$(scribe_json_len "$flat" '.missing')" """,
@@ -272,7 +270,7 @@ def test_urlenc_percent_encodes_exactly_the_unreserved_set(value):
def test_urlenc_and_the_envelope_can_fail():
"""Rule 167 — a guard that cannot fail is decoration. If the escaper were a
"""A guard that cannot fail is decoration. If the escaper were a
no-op, this pair would be the assertion that noticed."""
assert sh("scribe_urlenc", stdin="a b") != "a b"
raw = sh('scribe_json_out PreToolUse "$(cat)"', stdin='he said "hi"')
@@ -376,7 +374,7 @@ def test_a_tool_result_does_not_read_as_a_prompt():
def test_the_turn_guards_can_fail():
"""Rule 167. If the analyzer counted everything, or nothing, these are the
"""If the analyzer counted everything, or nothing, these are the
assertions that would notice."""
assert _turn([_prompt(), _close("t1", 41), _text("r")])["closed"] == "1"
assert _turn([_prompt(), _text("r")])["closed"] == "0"