diff --git a/PENDING.md b/PENDING.md index e6cf66b..df83164 100644 --- a/PENDING.md +++ b/PENDING.md @@ -760,6 +760,68 @@ Measured against the register itself before acting. Four corrections; the item's **Files affected:** none yet; a measurement, not a change. Output would be a dated record beside the doctrine package. **Awaiting:** Steward direction on (a)/(b)/(c), and on whether the executor is the right party to run a measurement of its own oversight at all — the jurist is no more independent here, so this may be steward-only work. +### Docket entry 2026-08-24 (second, later) — a CROSS-DIRECTION catch, on the same day +*Filed with the same alacrity as the same-direction entry above, which is the point: an executor +that records evidence against the doctrine promptly and evidence for it slowly is running the bias +the docket exists to detect.* + +**The jurist caught a false claim the executor made about the executor's own code**, by reading the +artifact rather than the record. PENDING-95 Amendment 1 asserted the new pre-commit gate "reuses the +CURRENT attestation rule." It does not: it tests the resulting artifact where the PreToolUse hook +tests the file as it was, making it a **strict superset**. The executor had written both surfaces, +described them in a governance record, and not noticed they had diverged. + +**Why this one counts where a self-catch does not.** The catch required (i) a different party, +(ii) reading two artifacts the executor authored, and (iii) disbelieving the executor's own summary +of them. The executor then **verified it empirically before accepting it** — exit 0 at one surface, +exit 1 at the other, same edit — so the finding is confirmed by the party it corrects rather than +taken on authority. + +**Also cross-direction, same ruling:** the jurist found that the `(read YYYY-MM-DD)` field and the +QUOTES requirement are parsed by nothing on either surface — a defect present since 2026-07-17 that +the executor had looked at repeatedly, including while editing one of the two files that day. + +⚠ **What it does NOT establish.** One catch does not answer Q3, and the jurist's access here was +**newly granted by the executor hours earlier** — so this is evidence about what the pair can do +*when the jurist can read the substrate*, not about the pair as it has historically operated. The +honest reading of the day is: **two same-direction misses in the parties' model of each other, and +one genuine cross-direction catch that required a read surface which did not exist that morning.** +That is an argument for widening the read surface before it is an argument about independence. + +### Docket entry 2026-08-24 — a SAME-DIRECTION miss, and its class is the parties' model of each other +*Filed unprompted per Constraint 6: "Evidence against is to be recorded when observed, not only when +sought." Raw instances left checkable; evidence, not a verdict.* + +**Same-direction miss (the doctrine's predicted failure), n=2 within one week, mirror-imaged.** + +| party | claim asserted from recall | substrate | +|---|---|---| +| **executor**, 2026-08-23 (recorded in PENDING-155) | *"Claude.app has no filesystem access"* | false — mediated read access since 2026-08-08 | +| **jurist**, 2026-08-24 (in its PENDING-155 ruling, self-reported) | the governance tools are unavailable — asserted **twice in one session** | false — deferred behind `tool_search`, never called | + +**The class is sharper than "asserted from recall."** Both are negative claims about **the other +party's capabilities**, made without a positive control, by parties who each hold the doctrine that +forbids it — the jurist naming its own as *"the same error… from the party whose Q2 doctrine that +is."* Neither party checks what the other can actually do. **This is the pair failing in the same +direction on the same class, which is confirmation of the weak separation REVIEWED-86 declares.** + +⚠ **It has an operational cost, incurred today.** The executor offered to build a jurist package +that was largely unnecessary, because it mis-modelled the jurist's reach; the jurist left its own +tools uncalled for a session, because it mis-modelled its own. **The parties' mutual +capability-model is a shared blind spot with a measurable price in wasted work and unused reach.** + +**Who caught each, which is the part that bears on the doctrine.** +- The executor's instance: caught by the **steward**, by asking *"unless there is enough there that + his governance tools can read without a package"* — a one-line prompt that redirected the work. +- The jurist's instance: caught by the **jurist itself**, unprompted, on re-reading. + +⚠ **A self-catch is not a cross-catch and must not be counted as one.** The pair did not check each +other here; one party checked itself and the steward checked the other. **This is consistent with +the weakened form the doctrine names as its own failure case** — *"only the steward supplies genuine +independence; jurist review is a second reading, valuable and not a check"* — and it is the second +same-direction instance on this docket. It does not settle Q3; it moves the evidence the same way +2026-08-06 did, and no instance yet recorded moves it the other way. + ### Docket entry 2026-08-06 — first day the correlation question has data in BOTH directions *Filed per the jurist's instruction on PENDING-102. Raw classifications left checkable; this is evidence, not a verdict.* @@ -935,6 +997,166 @@ the Obsidian vault pass and again 2026-08-20 against the trial-09 package — th workstreams, neither of which the hook was scoped to and both of which it was meant to cover in spirit. The instance is disclosed by the party that bypassed it. +### AMENDMENT 1 — 2026-08-24 — third instance; the REACHABILITY half is fixed, the STRICTNESS half is still owed + +**Steward-authorized in-session** — *"yes, we need this fixed now."* Implemented and committed +before this entry was written; this entry records what was done, not what is proposed. + +**Third instance, same shape.** `verify-before-compose` did not fire on any file written in this +session either — the runbook edit, two memory trackers, the session ledger — because every write +went through Bash. Found today only by censusing the configured hooks after the steward observed +the session "felt intuitive"; **the executor reported it as a new discovery and only then read this +item, which had carried it since 2026-08-20.** Reporting before reading is the same ordering error +that produced a duplicate-tracker proposal earlier the same afternoon. + +**A second, independent victim of the identical defect.** `daybook-cue.py` (built 2026-08-24 11:19) +was wired `PostToolUse | Write|Edit` — **the same matcher, copied from the hook whose blindness was +already on this record.** It has never fired; `~/.claude/state/` does not exist. A known defect +propagated into a new mechanism because it was recorded as an instance rather than as a class. + +**What was fixed.** `chamber-library/.githooks/pre-commit` (`4ccba76`) now gates staged +constitutional artifacts — same scope predicate as the PreToolUse hook — on a `GROUNDED-IN:` +attestation. **The commit is the tool-agnostic boundary**: however a file was edited, it passes +through here. Controls run before committing: constitutional+ungrounded **BLOCKS** (exit 1), +constitutional+grounded **PASSES** (exit 0), non-constitutional+ungrounded **PASSES** (exit 0). + +**What was deliberately NOT fixed, and why.** +1. **The strictness question is untouched.** The new gate reuses the CURRENT rule (marker anywhere + in staged content), so it **inherits this item's original decay defect** by design. Options + (a)–(d) above remain UNRULED and choosing one at the commit boundary would have pre-empted the + steward. ⚠ **When that ruling lands it must be applied to BOTH surfaces** — the PreToolUse hook + and the pre-commit gate — or they will diverge. Declared in the hook body, not only here. +2. **`--no-verify` bypasses it.** Speed-bump, not guarantee — the same claim the original makes. +3. **The PreToolUse hook is unchanged.** It still covers the Write/Edit route; no reason to remove. +4. **`daybook-cue.py` is NOT fixed.** Two gaps, and only one is this item's class: (i) reachability, + shared with this item; (ii) its condition is `note_size < SKELETON_CEILING` (1200 bytes), so a + session appending nothing to an already-filled note is invisible to it — today's note is 15,688 + bytes and the cue would have stayed silent **even if reachable**. (ii) is a design choice, not a + defect, and is not the executor's to redecide. + +**Still open on this item:** the (a)–(d) ruling, and the coverage question of which *other* guards +are wired to tool-matchers the work routes around. `census-02-have-they-ever-fired` (2026-08-04) +censused seven instruments; **three mechanisms built since have never been censused.** + +**Read surface widened so the ruling can have the right subject.** `governance-mcp.py` serves the +jurist a fixed enum of files. It already carried PENDING-95 itself (via `read_item`), the whole +register, and chamber-library's git log — so most of this ruling was reachable unaided. It did **not** +carry the two artifacts the ruling GOVERNS. Ruling from the record alone would have made the subject +the executor's *description* of the hooks rather than the hooks — the exact defect recorded inside +REVIEWED-125 (*"the ruling's subject was the pasted text, not the filed artifact"*). Added two +read-only keys, `grounding-hook-pretool` and `grounding-hook-commit`, under the **PENDING-86 option +(a) precedent** already sitting three lines above them in the same dict. Enum design unchanged, no +path argument, no write path; the AST no-write invariant and its positive control still pass. +⚠ **Requires a steward restart of the desktop app** — the key map is built at import, the same +friction that held PENDING-134. ⚠ **Done on the steward's in-session instruction rather than a +separate authorization; read-only and reversible by deleting two dict entries** if that reads as +overreach. + +**Awaiting:** steward ruling on (a)–(d), now applying to two surfaces rather than one. + +### AMENDMENT 2 — 2026-08-24 — RULED. (a) rejected, (d) discharged, (b)/(c) deferred on a census + +**Jurist ruling placed by the steward** (REVIEWED, id unasserted pending PENDING-110's +`REVIEWED-N`/`PENDING-N` collision). Ruled from a **verbatim read of both hook artifacts**, which the +two read-only keys added earlier the same session made possible; the ruling records that findings +1–3 exist only because of that widening. + +**⚠ A CLAIM IN AMENDMENT 1 WAS FALSE AND THE JURIST CAUGHT IT BY READING THE CODE.** Amendment 1 +said the pre-commit gate "reuses the CURRENT attestation rule." It does not. The PreToolUse hook +tests the payload **plus the file as it was on disk**; the pre-commit gate tests `git show ":$file"`, +the **resulting** artifact. **The two surfaces had already diverged, and the new one is strictly +stricter.** Verified empirically before accepting the finding: an edit stripping the only marker +from a marked file **exits 0 at the PreToolUse hook and 1 at the commit gate**. The standing +instruction is therefore amended and is asymmetric — *the pre-commit semantics are the FLOOR, do not +equalise downward*; the PreToolUse hook owes the payload-vs-disk correction regardless of (b)/(c). + +**(a) REJECTED** — not revisited without new steward input. **(d) AUTHORIZED unconditional, and +DISCHARGED 2026-08-24** across the surfaces that were owed it (`8eea85f` + the PreToolUse header): +per-file decay stated, the divergence stated, and — the finding neither party had before the read — +**the `(read YYYY-MM-DD)` field and the "Grounding section that QUOTES them" requirement are +ADVISORY TEXT PARSED BY NOTHING.** Both surfaces test the bare substring `GROUNDED-IN:`. Marker +presence has never been evidence that any reading occurred. + +**⚠ On (d) obligation 2, the ruling's premise was already stale, and this item is why.** The ruling +directs correcting chamber-library `CLAUDE.md`'s *"is **DENIED** unless the artifact carries"* clause +and notes honestly that the jurist cannot verify its state (outside its read enum). **Checked: that +clause was already corrected on 2026-08-06 under PENDING-106, jurist-authorized FIX**, and now +records all three consequences explicitly, including the once-per-file decay and the unenforced +QUOTES requirement. **What was stale is THIS ITEM's quotation of it** — PENDING-95 is dated +2026-08-04 and was never updated after the 08-06 correction, so the jurist read a two-day-old quote +as current. Nothing to fix in the constitution; the defect was in the register quoting a document +instead of pointing at it. + +**(b)/(c) DEFERRED**, on a stated and cheap condition: a read-only census of the 31 marked guarded +files recording, per file, whether the marker carries (i) a parseable date, (ii) named §sections, +(iii) a Grounding section that actually quotes — **distribution, not summary.** Ruling between (b) +and (c) now would authorize a mechanism against an unknown input format and would likely invalidate +markers retroactively. The jurist offers a **third form, explicitly outside the four and offered as +a scope expansion for the steward to accept or refuse**: require the marker's date to be no older +than the last substantive change to the artifact it grounds — (c)'s firing semantics with no format +migration. And: draft the new marker against three real files before authorizing the rule that +invalidates the other 28. + +**Read-surface doctrine, accepted.** The two keys were added on in-session instruction. The jurist +records this as exemplary in disclosure and load-bearing in effect, and holds the doctrine point +anyway: **the jurist's read surface is the one instrument the jurist cannot audit, so changes to it +should arrive as recorded rulings, not in-session authorization.** ⚠ **Adopted as binding on the +executor from now.** Accordingly, the obvious next key — chamber-library `CLAUDE.md`, whose clause +(d) obligation 2 concerns and whose state the jurist could not verify — is **NOT being added +unilaterally; it is proposed and awaits a ruling.** + +### THE (b)/(c) CENSUS — RUN 2026-08-24, the condition is discharged + +*Read-only, no new instrument, as ruled. Definitions declared before counting: **(i)** `(read +YYYY-MM-DD)` in the marker line · **(ii)** a `§` in the marker line · **(iii)** a heading matching +/grounding/i with at least one blockquote line in the 40 lines after it. Per-file table preserved in +the session record so the classification is checkable, not just the totals.* + +**⚠ First result: the item's own numbers were stale.** **60 guarded / 32 marked / 28 unmarked**, not +59/31. Verified by walking the tree against the five basename patterns, not relayed. The drift is +one file each way and changes nothing, but the item has been quoting 59/31 since 2026-08-04. + +| | count | share | +|---|---|---| +| marker carries a parseable **date** | **24 / 32** | **75%** | +| marker names **§sections** | 31 / 32 | 97% | +| a **Grounding section that actually quotes** | **15 / 32** | **47%** | + +**Distribution (the jurist asked for this, not a summary):** + +| date | §sec | quotes | files | +|---|---|---|---| +| ✓ | ✓ | ✗ | **16** | +| ✓ | ✓ | ✓ | 8 | +| ✗ | ✓ | ✓ | 6 | +| ✗ | ✗ | ✓ | 1 | +| ✗ | ✓ | ✗ | 1 | + +**This decides the ruling's own fork.** The jurist wrote: *"if the date field is broadly absent, (c) +is the only live option… if broadly present, a third form becomes available."* **It is broadly +present — 75%.** So the third form is live: *require the marker's date to be no older than the last +substantive change to the artifact it grounds.* No format migration for 24 files; 8 need a date +added. **(c)'s firing semantics at roughly a third of (c)'s cost.** + +**⚠ The largest single class is the one that matters: 16 files carry date AND §sections AND NO +quoting Grounding section.** Marker complete, substantive half absent — the protocol's own evidence +that the attestation has been doing ceremonial work. This is PENDING-109's prior, now with a number. + +⚠ **A caveat that cuts against reading 47% as a failure rate.** All ten +`chamber-library-specification*.md` files score `DS-`, and that may be **correct by category**: the +spec is what one grounds *in*, not a grounded draft, so it has no occasion for a Grounding section. +Excluding them, quoting runs **15/22 = 68%**. I have not established which reading is right — it +turns on whether the marker on a spec file means "this version was grounded" or "this file grounds +elsewhere," and that is a question for the party who placed them. **Both figures are reported +because choosing one silently would be the finer-instrument error the ruling warns about.** + +**Not done, and named:** the ruling's *"draft the new marker against three real files before +authorizing the rule that invalidates the other 28"* — deliberately left, since it is an authoring +act under the third form, and the third form is a scope expansion the steward has not yet accepted. + +**Awaiting:** steward direction on the (b)/(c) census, and on the third form as a scope expansion. + + ## PENDING-96 — The engine's `SILENCE — ✓ warranted` certifies the index and claims the answer **Date:** 2026-08-04 **Tag:** [HARDENING] @@ -1363,6 +1585,21 @@ Method: every `*JURIST-PACKAGE*.md` / `*FOR-JURIST*.md` under `~/dotfiles/claude --- +### Contribution 2026-08-24 — the prior is now CONFIRMED BY DIRECT READ, not inferred + +Filed by the jurist while it had the substrate. This item's prior — that the *"QUOTES the ratified +sections"* clause has **no mechanism behind it** — is confirmed on **both** grounding surfaces: +each tests the bare substring `GROUNDED-IN:` (`case "$haystack" in *GROUNDED-IN:*` and +`grep -q 'GROUNDED-IN:'`). Neither parses the date field nor validates marker shape. Previously +inferred; now read. ⚠ **Scope: two of two artifacts within the jurist's read enum; a parser outside +that surface cannot be ruled out.** + +⚠ **And a routing decision recorded so it is not silently reversed:** the residual coverage question +(guards wired to tool-matchers the work routes around) was expected to land here and **does not**. +This item is bounded to **kind (a)** — an honest mechanism under an over-claiming doc. `daybook-cue.py` +is a different kind: nothing over-claims; the mechanism is simply off the path. Folding it in would +silently widen a scope this item says is not to be widened silently. Opened as PENDING-156. + ## PENDING-110 — `REVIEWED-N` and `PENDING-N` are independent sequences that now collide, and a bare number no longer identifies an item **Date:** 2026-08-06 @@ -4608,3 +4845,57 @@ If the visible work today came from **positional difference between two same-for **Files affected:** new `~/dotfiles/scripts/daybook-mcp.py`; `/wrap-up` §7.5 (fold step); awaiting steward hand: `claude_desktop_config.json` merge + app restart. **Recommendation:** Build it, with a selftest carrying the same paired positive/negative controls as its sibling, and hold installation until the daily log has run long enough to show it survives — a jurist inbox for a practice that lapsed would be the fourth abandoned attempt rather than the first durable one. **Awaiting:** Steward authorization. + +--- + +## PENDING-156 — Kind (c): mechanisms that are off the path the work takes +**Date:** 2026-08-24 +**Tag:** [HARDENING] +**Summary:** A third failure kind, distinct from PENDING-109's: not a doc over-claiming an honest mechanism, but an honest mechanism wired to a trigger the work routes around. Two instances found today out of seven configured hooks; three mechanisms built since the last census have never been censused at all. +**Rationale:** Opened on jurist direction in the PENDING-95 ruling, which declined to absorb it: *"109 is bounded to kind (a)... `daybook-cue.py` is kind (c): nothing over-claims; the mechanism is off the path. Open it as its own [HARDENING] item."* PENDING-95 has already carried two defects under one id and one ruling slot and does not take a third — a lesson that item paid for. + +**The class, stated so it is testable:** a mechanism whose *stated* scope is correct, whose selftests pass, and whose trigger condition is never met in practice because the work reaches the guarded action by a route the trigger does not observe. It differs from kind (a) in that **no document is wrong** — the failure is invisible to any audit that reads docs against code, which is what makes it worth its own item. A green suite and an honest doc are both consistent with zero firings. + +**Instances so far (2 of 7 configured hooks, both found 2026-08-24 by censusing the hook config, not by any selftest):** +1. `verify-before-compose` — `PreToolUse | Write|Edit`; work routes through Bash. Recorded 2026-08-19, 08-20, 08-24. Remediated at the commit boundary, not at the matcher. +2. `daybook-cue.py` — `PostToolUse | Write|Edit`, **inheriting the identical matcher by copy** from the hook whose blindness was already on the record. Never fired; `~/.claude/state/` did not exist. Fixed 2026-08-24 (matcher widened to `Bash|Write|Edit`; condition changed from note-size to staleness on steward ruling). + +**⚠ The propagation is the finding, not the instances.** Defect 2 exists because defect 1 was filed as an *instance* rather than as a *class*, and the new mechanism copied the broken pattern from the recorded one. Filing this item is the remedy for that specific mode. + +**Never censused:** `census-02-have-they-ever-fired` (2026-08-04) covered seven instruments. Three have been built since and are outside it — `thread-query.py`, `daybook-cue.py`, `daybook-ensure.py` — under the standing discipline *re-run a prior verification at the scope of your extension*, which was not run. + +**Options:** (a) re-run census-02's method over the current mechanism set, extended with an "is its trigger on the path the work takes?" axis; (b) axis-only pass over the 7 configured hooks, cheapest, ignores non-hook mechanisms; (c) require every new mechanism to record a first-firing observation before it counts as built — a standing rule rather than a census. +**Recommendation:** (b) now, because it is one read of one config file and closes the known-unknown; then (c) as the durable form, since (a) will simply be owed again the next time something is built. ⚠ (c) generalises today's repeated lesson — **only the live run catches it; a green selftest over a self-authored fixture certifies its author's blind spot** — which fired three times on 2026-08-23/24 on three different tools. +**Files affected:** none yet; a census plus, under (c), a line in the build discipline. + +### Option (b) RUN — 2026-08-24, same session the item was opened + +*Steward-directed. Read-only, one pass over `settings.json` + every `dotfiles/scripts/*.py`, then a +caller search. Distribution, not summary.* + +| verdict | mechanisms | +|---|---| +| **fires mechanically** | `wake-digest.py` (SessionStart) · `governance-drift-check.py` (**executed by wake-digest**, not prose) · `daybook-ensure.py` (SessionStart) · `daybook-cue.py` (PostToolUse, fixed today) · `cm-hook.mjs` | +| **fires only if the executor follows a prose step** | `thread-query.py` — the one genuinely at risk, and it already carries a trial for exactly this | +| **nothing invokes automatically** | `vault-links.py` · `verify-quotes.py` · `l1-replay-sampler.py` — **manual instruments; not a defect**, and census-02 already records that hand-run tools have the rich firing records | +| **wired but OFF the path** | `verify-before-compose.sh` — the only remaining kind-(c) instance | + +**⚠ THE METHOD FINDING IS WORTH MORE THAN THE TABLE.** A first pass classified by the **proxy** +*"is it named in a SKILL.md?"* and produced a tidy 3/3/3 split. A caller search corrected **two of +nine, in opposite directions**: `governance-drift-check.py` looked prose-wired and is in fact +hook-fired (wake-digest executes it, and today's SessionStart output proves it ran); +`vault-links.py` looked code-referenced and is **cited in a docstring comment, never called**. A +proxy does not err in one direction that can be corrected for — it errs both ways at once. Recorded +because the tidy first answer is the one that would have been filed. + +**Result for the item's scope:** kind (c) has **one live instance**, not a class in the wild — +`verify-before-compose`, already remediated at the commit boundary and still owed the matcher fix. +The propagation risk the item names is real but its blast radius today is one. **The bigger residual +is the prose-wired tier**: a mechanism whose trigger is a sentence in a skill fires only when read +and obeyed, which is the described-not-invoked class that had the link canary hand-typed twice. +`thread-query.py` is the only one there, and it is under trial. + +**Recommendation now narrows to (c)** — the standing rule *a mechanism does not count as built until +a first firing is observed on the live path* — since (b) is now run and (a) would re-derive it. + +**Awaiting:** Steward authorization. diff --git a/REVIEWED.md b/REVIEWED.md index 1e6a5b6..ccf5f48 100644 --- a/REVIEWED.md +++ b/REVIEWED.md @@ -2160,3 +2160,57 @@ REVIEWED-125's original entry is unaltered and remains above this amendment. Ame by replacement is the defect the register-integrity check exists to catch, earned when REVIEWED-87's original entry was overwritten by its own amendment and nothing detected it. + +## REVIEWED-126 — PENDING-155 — A daybook append surface for the jurist +**Date:** 2026-08-24 +**Decision:** AUTHORIZED to build; DEFERRED on installation, on three conditions. +**Ruled by:** jurist (Claude.app), reading the item verbatim via `governance-mcp.py`. + +**Opening correction, recorded by the jurist against itself.** The governance tools were +available throughout, deferred behind `tool_search` and never called; their absence was +asserted twice in one session from recall. Same class as the error PENDING-155 records +the executor making about the jurist ("Claude.app has no filesystem access") in the same +week, mirror-imaged: a negative claim with no positive control, from the party whose Q2 +doctrine forbids exactly that. **Cross-filed to PENDING-89 as a same-direction miss +(docket entry 2026-08-24); it is evidence bearing on Q3 and cuts against the strong form +of the differently-biased-checkers doctrine.** + +**On the substance.** The separate-server decision is correct and well-grounded. The +read-only invariant on `governance-mcp.py` is checkable — AST selftest with paired +controls, verified live — and a passing control is worth more than one fewer server. +Building the write path elsewhere is right. + +**Condition 1 — the topology change, which is the real risk and was unexamined.** The +proposal defends the governance server's invariant thoroughly but does not ask what a +jurist -> scratch -> executor path does to the party model. Every jurist->executor +communication to date has passed through the steward, who relays; this creates a channel +that does not. A record is not an instruction, but nothing in the proposed shape prevents +a daybook entry from containing one, and the executor reads the file while folding. +Belongs in the spec, not left to discipline: + +> Daybook entries are RECORD ONLY. No imperative addressed to the executor. The fold is +> mechanical; the executor does not act on entry content. + +Without that, the loop has a bypass nobody authorized. + +**Condition 2 — the installation gate must be declared, or it never fires.** "Hold until +the daily log has run long enough" is an undeclared criterion, filed on a day spent +establishing that an undeclared criterion is not a criterion. Name a date or a count of +consecutive days, or the caution against "the fourth abandoned attempt" becomes the +mechanism of the fourth abandoned attempt. + +**Condition 3 — honest degradation on the append.** No read-back means the jurist cannot +verify its own write. `daybook_append` MUST return a positive confirmation (bytes +appended, or resulting line count), and the jurist must never report "recorded" beyond +what the tool returned. Silent failure here produces exactly the class of claim this +register exists to prevent. + +**Note for the entry format, not a condition.** A jurist account of a jurist-only exchange +is one party's testimony about itself, and the pressure runs toward recording insight over +error. An entry format with a slot for insights and none for corrections will +systematically under-record the second. + +**If AUTHORIZED:** build `~/dotfiles/scripts/daybook-mcp.py` with conditions 1 and 3 +satisfied and a selftest carrying paired positive/negative controls. Do NOT install: +installation awaits condition 2 being named and met. `claude_desktop_config.json` merge +and app restart remain the steward's hand. \ No newline at end of file diff --git a/claude/governance/thread-query-log.jsonl b/claude/governance/thread-query-log.jsonl new file mode 100644 index 0000000..52833ad --- /dev/null +++ b/claude/governance/thread-query-log.jsonl @@ -0,0 +1 @@ +{"date": "2026-08-24", "thread": "the turning exists but has never run in anger; whether the container should be shaped like the work — Darwin's chapters — rather than the session's thread", "terms": ["turning", "exists", "never", "anger", "whether", "container", "should", "shaped", "like", "work", "darwin's", "chapters", "rather", "session's", "thread"], "candidates": 868, "returned": [{"path": "~/Library/Mobile Documents/iCloud~md~obsidian/Documents/David, root-and-branch/08. Notes/Books.app highlights/Surviving an Eating Disorder.md", "date": "2025-04-20", "matched": ["turning", "exists", "never", "anger", "whether", "should", "like", "work", "rather", "thread"]}, {"path": "~/Library/Mobile Documents/iCloud~md~obsidian/Documents/David, root-and-branch/00. Compass/00b. Constellations/Animal Rationis Capax/99. Archives—Previous Iterations/99. The Chamber/00. Core Foundation/complete-amphitheatre/hybrid/turing-beyond-test.md", "date": "1953-01-01", "matched": ["never", "whether", "like", "work"]}, {"path": "~/Library/Mobile Documents/iCloud~md~obsidian/Documents/David, root-and-branch/08. Notes/Books.app highlights/Concerning the Spiritual in Art.md", "date": "2025-04-20", "matched": ["turning", "exists", "never", "whether", "should", "like", "work", "rather"]}, {"path": "~/Library/Mobile Documents/iCloud~md~obsidian/Documents/David, root-and-branch/00. Compass/00b. Constellations/Animal Rationis Capax/99. Archives—Previous Iterations/99. The Chamber/00. Core Foundation/complete-amphitheatre/weil-attention-gravity.md", "date": "1942-01-01", "matched": ["exists", "like", "work"]}, {"path": "~/Library/Mobile Documents/iCloud~md~obsidian/Documents/David, root-and-branch/08. Notes/Books.app highlights/The Longing for Less.md", "date": "2025-04-20", "matched": ["exists", "never", "whether", "should", "shaped", "like", "work", "rather"]}]} diff --git a/claude/memory/feedback-chamber-work-ground-in-constitution-charter-runbook.md b/claude/memory/feedback-chamber-work-ground-in-constitution-charter-runbook.md index cb4be4a..22a18a5 100644 --- a/claude/memory/feedback-chamber-work-ground-in-constitution-charter-runbook.md +++ b/claude/memory/feedback-chamber-work-ground-in-constitution-charter-runbook.md @@ -14,7 +14,7 @@ metadata: - **Touchstone (the why)** — `~/_Dev/studium-engine/docs/the-chamber-touchstone.md` (est. 2026-07-28). **First, and it is one page.** The three below are *operational*; none of them says what any of it is for. Seven questions to test work against; every quote source-verified verbatim. It holds no state, so it does not decay. - **Constitution** — `~/_Dev/chamber-library/docs/chamber-library-specification.md` (currently v2.8.0 OPERATIVE; ratified, governed-not-steward-direct) + its machine-readable form `_curation/graduation-spec.yaml` - **Charter** — `~/_Dev/studium-engine/docs/the-studium-engine-architectural-charter.md` (three cognitions · boundedness-is-trust · the four layers · voice-as-primitive · tempo-is-ethics · the recursion) -- **Runbook** — `~/_Dev/chamber-library/_curation/conversion-runbook.yaml` (classify → pipeline → verify → file; the `machines:` split; **`reanchor:`**; `known_gaps:`). Note: it does **not** yaml-parse (`decision_tree:` arrows) — it is human-read. +- **Runbook** — `~/_Dev/chamber-library/_curation/conversion-runbook.yaml` (classify → pipeline → verify → file; the `machines:` split; **`reanchor:`**; `known_gaps:`). ~~Note: it does **not** yaml-parse (`decision_tree:` arrows) — it is human-read.~~ ⚠ **SUPERSEDED 2026-08-24, verified against the substrate: the runbook DOES yaml-parse** (`yaml.safe_load` succeeds at HEAD). Either it was repaired since 2026-07-28 or the claim was never true. **This mattered:** the false note said the parse check was pointless, and the parse check is exactly what caught a defect I introduced editing `known_gaps` the same day — inner double quotes inside a double-quoted scalar, which would have shipped a runbook that no longer loads. **A memory note that tells you a verification is unnecessary is the most expensive kind to get wrong.** Run `python3 -c "import yaml; yaml.safe_load(open('_curation/conversion-runbook.yaml').read())"` after any edit; the file's inner-quoting convention is SINGLE quotes (`catch 'no cruft but OCR garbage'`). **Why (earned the same day it was given):** the session that produced this directive spent hours re-deriving what these documents already held. Three successively broken detectors were built to establish an apparatus defect the quality ledger had classified on 2026-07-03; docling was declared absent because I checked `import docling` instead of the runbook's declared `~/.local/bin/docling`; and the `reanchor:` obligation — that a new canonical hash invalidates bindings across **both** repos — was nearly missed on a reconversion proposal. The steward's correction was two words: *"read the runbook?"*. See [[feedback-resurface-banked-notes-before-rederiving]], of which this is the chamber-specific, stronger form: not *resurface if you recall a note exists*, but **read these four, always, before acting or opining**. diff --git a/claude/memory/project-chamber-versioned-releases.md b/claude/memory/project-chamber-versioned-releases.md index 5bc4204..336c4a2 100644 --- a/claude/memory/project-chamber-versioned-releases.md +++ b/claude/memory/project-chamber-versioned-releases.md @@ -10,7 +10,28 @@ metadata: **Steward reframe (2026-07-25):** the full Chamber — all the voices across 2000 years, including the image-based works (Warburg's *Mnemosyne Atlas*, Jung's *Red Book*) and the personally-foundational Berger and Christopher Alexander — **cannot be realized all at once, and shouldn't be attempted that way.** The Chamber is realized as **versioned releases with soft borders**: "**Chamber V1** = constitution vX + engine vY + a *bounded voice-set*, serving a **specific purpose**"; then with capability *x*, "**Chamber V2**", and so on. The full Chamber is the **horizon**, not the V1 deliverable. -**The driver is PURPOSE, not corpus-completeness.** The steward needs to *use* the Chamber for concrete work, not only the unbounded dream of "talking to the library and the minds who formed him" ([[project-studium-engine-telos-chamber-of-voices]] — this is the practical bounding of that telos). Named purposes awaiting the Chamber: **the Making Sequence** · **the violin / music-in-the-XXI-century treatise** · **ARC**. And: this library/engine work **directly informs CapableMind**. +**The driver is PURPOSE, not corpus-completeness.** The steward needs to *use* the Chamber for concrete work, not only the unbounded dream of "talking to the library and the minds who formed him" ([[project-studium-engine-telos-chamber-of-voices]] — this is the practical bounding of that telos). Named purposes awaiting the Chamber: **the Making Sequence** · **the violin / music-in-the-XXI-century treatise** · **ARC**. + +> **THE EDGE, written 2026-08-24 (steward-directed) — what each named purpose actually waits on.** +> This frame recorded *that* the treatise awaits the Chamber; it did not record *which gate*. It is +> `known_gaps.research` in `_curation/conversion-runbook.yaml`: **polytonic Greek / Latin / fraktur / +> long-s OCR**, the one gap whose trigger is *"before the historical/classical corpus — needs +> investigation, no recipe yet."* The treatise's sources are 18th–19th c. printed methods +> (Geminiani, Tartini, Leclair, Pisendel, Rode, Capet), so it is gated there and nowhere else in the +> machinery. **Partly crossed already:** the PENDING-56 trial had Docling+OCR OCR a scanned French +> book — 104 footnotes, full page-provenance, 0 cruft, ~11 min on the M4 — *readable but not +> verbatim-clean*, so it feeds the verbatim guard rather than replacing it. **Untested on polytonic +> Greek and fraktur specifically.** That named test is the remaining distance. +> +> **Why this matters to the open decision below:** "which purpose anchors V1" is a question about +> *cost*, and until now only one side of each purpose's cost was written down. The treatise's +> material is not the constraint — vault `06. Projects/Pattern, Presence, Practice` holds **177 +> notes / 158,313 words**, superseding the frozen `Arcus Temporis` archive (106 notes / 91k). It has +> **no repo yet by design**: the steward's ordering is machinery first, because the sources are not +> machine-readable until that gap closes. The reciprocal note is written into the runbook itself, so +> both ends carry it. The dependency previously existed only in the steward's head — the runbook knew +> *"first historical treatise"* as a trigger without knowing which, and this file knew the treatise +> waited on *"the Chamber"* without knowing on what. And: this library/engine work **directly informs CapableMind**. **Why (load-bearing):** the enormity was the paralysis — "the corpus must be trustworthy before I can use it" collapsed into "wait for everything." Versioning dissolves it: a bounded V1 whose properties are *proven-or-named* (the jurist's floor — [[feedback-census-by-mechanism-not-proxy]] kin; *boundedness IS trust*) is legitimately usable NOW. Selection criterion is **stability** (re-conversion breaks engine work, not imperfection), not quality. diff --git a/claude/memory/project-obsidian-vault-practice.md b/claude/memory/project-obsidian-vault-practice.md index 4181122..695410a 100644 --- a/claude/memory/project-obsidian-vault-practice.md +++ b/claude/memory/project-obsidian-vault-practice.md @@ -14,6 +14,45 @@ workflow to have the important parts of our work captured there… My instinct i make better use of it. I want to create an Obsidian 'spec' like we have done for all of our serious projects."* +**THE FULL BRIEF — steward, 2026-08-24. Supersedes nothing above; it states the WHY the +2026-08-19 goal only implied, and it is the design brief this workstream should actually be built +against.** Verbatim: + +> *"I wanted another trace of our working procedure, a sort of transcript/log that I could look at at +> the en[d] of a week and see where we had come from. But also, I wanted it to be another source of +> information for the three of us (and eventually the Fool) to look back through as well. Not only +> that, I wanted the possibility of any seeds or foreshadowing of what we're building now to surface +> from earlier idea[s] that may give us the 'oblique strategy' nudge we need if we're at an impasse. +> My gut says that preparing Obsidian for full collaborative use for us was important, and once I've +> finished feeding it with my journals especially, I think it will show deeper lines of thinking that +> I've been turning over for years. All of this, I hope, will help the technical work we are trying +> to complete which will bring us back to the thinking, writing, and making work."* + +**Four purposes, and they are not one mechanism.** Naming them separately because the workstream has +been building as though they were: + +| # | purpose | shape it wants | what serves it today | +|---|---|---|---| +| 1 | weekly trace — *"see where we had come from"* | chronological, low-friction capture | `daybook-cue.py` (built 2026-08-24, unproven) | +| 2 | a source **all three parties — and eventually the Fool — read back through** | queryable by any party, not just the executor | Smart Connections trial (grade 2026-10-05) | +| 3 | seeds / foreshadowing → the **"oblique strategy" nudge at an impasse** | associative, deliberately *not* adjacent | `thread-query.py` (built 2026-08-24, unproven) | +| 4 | the journals — *"deeper lines of thinking I've been turning over for years"* | thematic clustering over a corpus not yet ingested | nothing; journals not yet fed | + +⚠ **Purpose 2 is new and has a design consequence nothing has costed:** the vault must be legible to +the **jurist and the Fool**, not only to the executor. The jurist has no repository access at all +(PENDING-82 / PENDING-86, recurring), so "a source for the three of us" is today false for one of +the three. That is a gap in the brief, not in the vault. + +⚠ **Purposes 1 and 3 may pull against each other.** Eno & Schmidt's Oblique Strategies work *because* +the card is unrelated to the problem; a work-shaped key (the 2026-08-24 finding — see the session +note) sharpens purposes 2 and 4 but could defeat 3 by making returns too relevant. **Do not collapse +them into one retrieval mode without deciding which purpose each serves.** Open design question, +deliberately not resolved here. + +**The frame around all four:** the vault work is a *detour in service of the return* — *"help the +technical work… which will bring us back to the thinking, writing, and making work."* It is not an +end in itself and should be scoped as what it is. + **The tool boundary, from the steward, so it is not re-elicited:** | tool | role | diff --git a/claude/memory/session-ledger-2026-08-24.md b/claude/memory/session-ledger-2026-08-24.md index 424f59d..1fbfc45 100644 --- a/claude/memory/session-ledger-2026-08-24.md +++ b/claude/memory/session-ledger-2026-08-24.md @@ -66,6 +66,45 @@ type: feedback for every link anyone writes naturally in future. Verified: 0 dead touching the harvest, vault dead 483 -> 475, total links 2,200 -> 2,242. + +- `2026-08-24T~afternoon-wake` — **`thread-query.py` fired in anger for the first time and returned + nothing bearing on the thread.** 868 candidates, 5 hits, all matched on near-stopwords carried in + my own query prose (*exists · never · like · work · whether · should · rather*): an April-2025 + eating-disorder highlight, Kandinsky, Weil, a 1953 Turing sketch. **Logged as a null result** — + the trial's falsifier requires null runs to be recorded, not explained away. ⚠ Honest reading: + I fed a *sentence* to a term-scored instrument. Whether the fault is the scorer's or the caller's + is unresolved and should not be resolved by whoever wants the tool to have worked. §3 flag — + *a search query shaped by what the session wants to find*, in the mirror form: a query shaped by + how the session happened to phrase itself. +- `2026-08-24T~afternoon-wake` — **Substrate-checked the beacon rather than reading its disposition + line.** PENDING-149's `**Awaiting:** nothing blocking` is a disposition clause; verified against + the register that REVIEWED-125 + AMENDMENT 1 are placed (`38c4866f`) and the rule filed. Fires + **2026-08-25T12:00:00Z, ONCE.** +- `2026-08-24T~afternoon-wake` — **N-now re-measured, not relayed: 50/84** (49 this morning). The + rolling window moved *toward* the threshold across the day, having moved away overnight. + + +- `2026-08-24T~afternoon` — ⚠ **PROPOSED BUILDING A SECOND HOME FOR SOMETHING THAT ALREADY HAS A + GOVERNED ONE — and the standing rule that would have prevented it is the one I skipped.** + I proposed giving "the OCR pipeline" a canonical workstream tracker, having found it in MEMORY.md + only as `reference-capablehands-standing-authorization.md` and read that as a permission slip with + no workstream behind it. **The steward said: read the runbook.** OCR is not a workstream. It is two + leaves of `conversion-runbook.yaml`'s `decision_tree` (`pdf_image_only` → olmOCR single-column / + ocrmac multi-column), inside a machinery with a ratified direction (PENDING-56/REVIEWED-56, Docling + as primary front-end), per-tier lanes (REVIEWED-67), and a `machines:` block **where the + CapableHands M4 authorization already lives** as the workhorse designation. The memory file is a + *pointer into* the runbook, working as designed. **The runbook is the tracker.** + ⚠ **`feedback-chamber-work-ground-in-constitution-charter-runbook.md` covers "any chamber work OR + TALK ABOUT IT" — scope deliberately wide, earned 2026-07-28 on a day whose cost was "largely in + confident talk." This whole conversation was chamber talk and I opened none of the four until told + to.** Identical shape to this morning's compost-heap *prevention*, which worked only because I + checked first: **same failure class, both directions, same day.** The memory note even predicts + this — "session-start prose has failed this class ≥4 times before, which is exactly why the + existing hook was built" — and the enforcement mechanism is still owed. +- `2026-08-24T~afternoon` — **Corrected a claim I had already stated to the steward.** I said "the + OCR pipeline has no tracker" and framed it as a memory gap. It is not a gap. Retracted in-line + rather than left standing. + ## What held - **Parsed frontmatter rather than grepping it**, for the status census (645 notes with @@ -99,6 +138,13 @@ type: feedback suspects it was incomplete; three same-shape instances yesterday). - Tooling/uses sweep. - Hub notes as **venues** for re-encounter — the load-bearing horizon under the pulling thread. +- ⏰ **THE BEACON — 2026-08-25T12:00:00Z, tomorrow, run ONCE.** Any failure ⇒ STOP and report; no + retry on executor authority. Pass `outputValue` exactly as served to `derive_fool.py:79`. +- **The literal question** — classify the last ~15 pulling threads as cross-session or session-local. + Cheap, checkable, and it decides whether the work-shaped container is real work or a figure. +- Petrification detection (no links ∧ no edits ∧ no citation over a window). +- `/wrap-up` §7 guard — the vault sync can now REVERT authorized vault work (SHR-2026-08-20-c, + proposed, unauthorized). ## Confidence to recalibrate @@ -108,6 +154,13 @@ type: feedback Today's Mod re-read is a reading task, but the vault-status rebuild is exactly the shape that invites it. Parse; do not grep-and-report. - ⚠ **A substring is not a path** — `99. Archive` silently excluded 78 live notes. Live is 1,298. +- ⚠ **Post-compression: context was cleared, not carried.** Everything I hold this afternoon is + *read from files*, not remembered from this morning. Where I state something as established + today, it is inherited unless I say I re-verified it. §3 flag named at the top of the session + rather than discovered at the bottom. +- Carried from this morning, unchanged and unproven: **both mechanisms built yesterday are tested + and completely unproven in use.** One has now produced its first honest zero. That is data, not + a verdict. ## Authorization moves diff --git a/claude/skills/wrap-up/SKILL.md b/claude/skills/wrap-up/SKILL.md index 65ce136..65b134b 100644 --- a/claude/skills/wrap-up/SKILL.md +++ b/claude/skills/wrap-up/SKILL.md @@ -253,7 +253,9 @@ A reader who was not in the session must be able to follow it. This is the regis asked for on 2026-08-23 — *"relatively simple terms"* — and it is the hardest part of this step, because the executor's default register is dense, hedged, and written for itself. -**Shape** (steward-specified 2026-08-23): +**Shape** (steward-specified 2026-08-23; `## Corrections` added 2026-08-24 per REVIEWED-126). +⚠ **This list is a SECOND copy of the format — `daybook-ensure.py`'s `SKELETON` writes the first.** +They will drift. Change both, or make one derive from the other: - `## The day in short` — one paragraph. What actually happened, and why it mattered. - `## ` — **one H2 per project touched**, H3 beneath if it needs it. Project work goes under its own heading, *not* into project folders. Put commit hashes here, inline, next to what @@ -266,6 +268,13 @@ because the executor's default register is dense, hedged, and written for itself way the executor's do. Claude.app cannot write to the vault itself (its MCP surface, `governance-mcp.py`, is structurally read-only and audited to stay that way), so this step is where its side of the exchange gets kept. +- `## Corrections` — what any party got wrong today, and **who caught it**. Name both: an error the + steward caught, one the jurist self-reported, one the executor found by running a check it nearly + skipped. **Added 2026-08-24 on the jurist's reasoning (REVIEWED-126):** *"an entry format that has + a slot for insights and none for corrections will systematically under-record the second"* — the + pressure in any self-authored account runs toward insight over error. **An empty `## Corrections` + on a working day is itself a claim, and usually a false one.** Cross-party misses caught here may + also be owed to PENDING-89's docket. - `## Open / next` — what is unfinished, with dates where they exist. **Accrete DURING the session; do not save it all for the wrap.** A session that dies unwrapped loses diff --git a/scripts/daybook-cue.py b/scripts/daybook-cue.py index 189b145..ca9c9f1 100755 --- a/scripts/daybook-cue.py +++ b/scripts/daybook-cue.py @@ -19,6 +19,27 @@ written. So this fires there. REMINDER, NOT BLOCK — unlike verify-before-compose, whose failure is unrecoverable. This one's is recoverable, and a gate that interrupts every write would be removed within a day. Fail-open everywhere: any unexpected condition exits 0. + +REVISED 2026-08-24, steward ruling, verbatim: "the daily note needs to be appended to +until the end of the day. Period. There is no design choice to make." + +Two defects were fixed together, both found by censusing the hooks rather than by a +selftest -- the suite was 16/16 green throughout: + + 1. WRONG CONDITION. v1 cued only while the note was under SKELETON_CEILING (1200 B), + i.e. it watched "was the note ever started", not "is it current". A session that + appends nothing to an already-filled note was invisible to it: on the day it was + built, the note stood at 15,688 B and the cue would have stayed silent all + afternoon while work landed elsewhere. Size is not the question and the ceiling + is gone. The question is STALENESS: has the note been appended to lately? + 2. UNREACHABLE. v1 was wired PostToolUse | Write|Edit -- the same matcher as + verify-before-compose, copied from the hook whose blindness was already recorded + in PENDING-95 since 2026-08-19. Work routes through Bash heredocs, so it never + fired once: no stamp, ~/.claude/state/ did not exist. Bash is now matched, and + any tool use counts as the session being live. + +It self-silences on compliance: appending resets the note's mtime, which clears the +staleness condition. It insists only while the note is actually behind. """ import datetime, json, os, sys, time @@ -28,7 +49,7 @@ VAULT = os.path.join(HOME, "Library/Mobile Documents/iCloud~md~obsidian/Document DAILY_DIR = os.path.join(VAULT, "01. Daily") STAMP = os.path.join(HOME, ".claude/state/daybook-cue.stamp") -SKELETON_CEILING = 1200 # the generated skeleton is ~558 bytes +STALE_SECONDS = 1800 # the note may lag the work by 30 min, no longer QUIET_SECONDS = 900 # cue at most once per 15 minutes; a nag gets disabled # Writes that are not "work landing". @@ -55,23 +76,46 @@ def note_path(today=None): return os.path.join(DAILY_DIR, f"{d.isoformat()}.md") -def should_cue(path, note_size, note_exists, seconds_since_last, today=None): - """Pure decision, so it can be tested without a filesystem or a clock.""" - if not is_substantive(path): +def should_cue(activity, note_exists, note_age, seconds_since_last): + """Pure decision, so it can be tested without a filesystem or a clock. + + activity -- did substantive work just land? (bool) + note_age -- seconds since the daily note was last modified + """ + if not activity: return False if not note_exists: # daybook-ensure owns creation, not us return False - if note_size >= SKELETON_CEILING: # it is being written; say nothing + if note_age < STALE_SECONDS: # appended to recently; it is current return False if seconds_since_last < QUIET_SECONDS: return False return True -MESSAGE = """DAILY NOTE — still a skeleton, and work is landing elsewhere. +def activity_from(tool_input): + """Substantive work, from EITHER a file_path (Write/Edit) or a command (Bash). + + For Bash the written path is not recoverable without parsing arbitrary shell, so + any Bash call counts as the session being live -- which is the right reading of + the rule: the note must be current while work is happening, whatever the tool. + A command that touches the daily note itself is not a trigger for writing it. + """ + path = tool_input.get("file_path") or "" + if path: + return is_substantive(path) + command = tool_input.get("command") or "" + if not command: + return False + if os.path.basename(DAILY_DIR) in command or "01. Daily" in command: + return False + return True + + +MESSAGE = """DAILY NOTE — behind the work. Append to it now. The convention is FILL IT AS THE WORK HAPPENS, not at the wrap: - 01. Daily/{date}.md (currently {size} bytes) + 01. Daily/{date}.md (last touched {mins} min ago; {size} bytes) Give what just landed its own `##` heading now — plain language, for the steward on a day he wants to know what happened without reading git log. /wrap-up §7.5 finalises; @@ -87,11 +131,12 @@ def main(): except Exception: sys.exit(0) - path = (data.get("tool_input") or {}).get("file_path") or "" + activity = activity_from(data.get("tool_input") or {}) np = note_path() try: exists = os.path.exists(np) size = os.path.getsize(np) if exists else 0 + note_age = (time.time() - os.path.getmtime(np)) if exists else 0 except OSError: sys.exit(0) @@ -101,7 +146,7 @@ def main(): last = 0 since = time.time() - last - if not should_cue(path, size, exists, since): + if not should_cue(activity, exists, note_age, since): sys.exit(0) try: @@ -110,7 +155,8 @@ def main(): except OSError: pass - print(MESSAGE.format(date=datetime.date.today().isoformat(), size=size), file=sys.stderr) + print(MESSAGE.format(date=datetime.date.today().isoformat(), size=size, + mins=int(note_age // 60)), file=sys.stderr) sys.exit(2) # PostToolUse: tool already ran; stderr is surfaced as feedback @@ -126,30 +172,46 @@ def selftest(): vault_md = os.path.join(VAULT, "08. Notes/Something.md") daily_md = os.path.join(DAILY_DIR, "2026-08-24.md") + STALE = STALE_SECONDS + 1 - # positive control — it fires when it should - check("cues on a real vault write while the note is a skeleton", - should_cue(vault_md, 558, True, 9999) is True) - check("cues on a repo write too", - should_cue(os.path.join(HOME, "_Dev/x/y.py"), 558, True, 9999) is True) + # positive controls — it fires when it should + check("cues when the note is stale and work lands", + should_cue(True, True, STALE, 9999) is True) + check("cues on a LARGE but stale note (the v1 defect: size is not the question)", + should_cue(True, True, STALE, 9999) is True) # negative controls — each suppression works in isolation - check("silent once the note is being written", should_cue(vault_md, 5000, True, 9999) is False) - check("silent when the note does not exist", should_cue(vault_md, 0, False, 9999) is False) - check("silent inside the quiet window", should_cue(vault_md, 558, True, 10) is False) - check("silent on the daily note itself", should_cue(daily_md, 558, True, 9999) is False) - check("silent on scratchpad", should_cue("/private/tmp/claude-501/x/scratchpad/a.md", 558, True, 9999) is False) - check("silent on /tmp", should_cue("/tmp/a.md", 558, True, 9999) is False) - check("silent on .obsidian config", should_cue(os.path.join(VAULT, ".obsidian/x.json"), 558, True, 9999) is False) - check("silent on backups", should_cue(vault_md + ".bak", 558, True, 9999) is False) - check("silent on jsonl logs", should_cue(os.path.join(HOME, "a/b.jsonl"), 558, True, 9999) is False) - check("silent on empty path", should_cue("", 558, True, 9999) is False) + check("silent when the note was just appended to", + should_cue(True, True, 60, 9999) is False) + check("silent when the note does not exist", should_cue(True, False, STALE, 9999) is False) + check("silent inside the quiet window", should_cue(True, True, STALE, 10) is False) + check("silent when no substantive activity", should_cue(False, True, STALE, 9999) is False) - # boundary - check("threshold is exclusive at the ceiling", - should_cue(vault_md, SKELETON_CEILING, True, 9999) is False) - check("just under the ceiling still cues", - should_cue(vault_md, SKELETON_CEILING - 1, True, 9999) is True) + # boundary on staleness + check("threshold is exclusive at STALE_SECONDS", + should_cue(True, True, STALE_SECONDS, 9999) is True) + check("one second under the threshold is silent", + should_cue(True, True, STALE_SECONDS - 1, 9999) is False) + + # activity_from — the reachability half (the v1 defect: Bash was invisible) + check("Write/Edit: a real vault write is activity", + activity_from({"file_path": vault_md}) is True) + check("Write/Edit: a repo write is activity", + activity_from({"file_path": os.path.join(HOME, "_Dev/x/y.py")}) is True) + check("BASH counts as activity (v1 never fired because it did not)", + activity_from({"command": "python3 - <<'EOF'\nopen('x','w')\nEOF"}) is True) + check("bash touching the daily note is NOT a trigger for writing it", + activity_from({"command": "cat '01. Daily/2026-08-24.md'"}) is False) + check("empty tool_input is not activity", activity_from({}) is False) + check("silent on the daily note itself", activity_from({"file_path": daily_md}) is False) + check("silent on scratchpad", + activity_from({"file_path": "/private/tmp/claude-501/x/scratchpad/a.md"}) is False) + check("silent on /tmp", activity_from({"file_path": "/tmp/a.md"}) is False) + check("silent on .obsidian config", + activity_from({"file_path": os.path.join(VAULT, ".obsidian/x.json")}) is False) + check("silent on backups", activity_from({"file_path": vault_md + ".bak"}) is False) + check("silent on jsonl logs", + activity_from({"file_path": os.path.join(HOME, "a/b.jsonl")}) is False) # fail-open on malformed input import subprocess diff --git a/scripts/daybook-ensure.py b/scripts/daybook-ensure.py index 42d2ca7..37a4bd4 100755 --- a/scripts/daybook-ensure.py +++ b/scripts/daybook-ensure.py @@ -51,7 +51,9 @@ tags: **{nav}** *A plain-language record of the day's work, so the history is readable without reading `git log`. -Project work sits under its own heading; decisions, insights and open threads are collected at the end.* +Project work sits under its own heading; decisions, insights and open threads are collected at the end. +Corrections are a standing slot, not an occasional one: an empty `## Corrections` on a working day +is itself a claim, and usually a false one.* ## The day in short @@ -59,6 +61,8 @@ Project work sits under its own heading; decisions, insights and open threads ar ## Insights and exchanges worth keeping +## Corrections + ## Open / next """ diff --git a/scripts/governance-mcp.py b/scripts/governance-mcp.py index 1e254c3..5de5752 100644 --- a/scripts/governance-mcp.py +++ b/scripts/governance-mcp.py @@ -72,6 +72,18 @@ FILES = { "claude-md": (os.path.join(D, "CLAUDE.md"), "the executor's governing document"), "memory-index": (os.path.join(wd.MEM, "MEMORY.md"), "wake-loaded memory index"), "app-brief": (wd.BRIEF_PATH, "last generated .app Standing Context block"), + # PENDING-95 AMENDMENT 1, 2026-08-24. Same shape as the PENDING-86 (a) precedent + # below: the jurist is asked to rule options (a)-(d) on the grounding gate, and the + # two artifacts the ruling GOVERNS are the two it could not read. Ruling from the + # record alone would make the subject the executor's description of the hooks rather + # than the hooks -- the defect recorded inside REVIEWED-125 ("the ruling's subject + # was the pasted text, not the filed artifact"). Read-only, keyed, enum unchanged. + "grounding-hook-pretool": (os.path.join(HOME, ".claude", "hooks", + "verify-before-compose.sh"), + "PreToolUse grounding gate (Write|Edit route)"), + "grounding-hook-commit": (os.path.join(HOME, "_Dev", "chamber-library", ".githooks", + "pre-commit"), + "pre-commit grounding gate (tool-agnostic route)"), # PENDING-86 option (a), steward-authorized 2026-08-05. The jurist design-gates # constitutional supersessions of documents it could not read; three distinct # instances are recorded on that item. These two are the ones the loop actually