[PROPOSAL] governance-mcp: read-only substrate access for the jurist (PENDING-82)
The three-party model asks Claude.app to rule on items it cannot read. Steward
confirmed 2026-07-28 that local MCP servers are exposed to the app's *chat*
surface — and always have been, predating Cowork by about a year. My earlier
framing ("chat, not only Cowork") had the relationship backwards: it is "chat,
always; Cowork, only while its loop still runs locally," and local Cowork is the
mode being phased out as default. The jurist chat is therefore the sturdy target.
Five read-only tools. The one a pasted cache can never provide is
governance_item(id): the verbatim body of any item or ruling, across PENDING.md,
PENDING-archive.md and REVIEWED.md. Four refusals are designed in, each with a
control proving the refusal is detectable — no writes (AST-audited), no path
arguments (keys from a fixed enum, so there is no traversal to defend), no second
parser (item_spans is imported, not reimplemented), and not an agent (tools
return data; an agent would return testimony about the substrate instead).
[FIX] to the shared definition while here: item_spans() is now fence-aware. A
'## ' header inside a fenced block is neither an item nor a boundary. Zero such
headers exist today — 17 open items before and after — but governance drafts are
written as fenced markdown carrying '## REVIEWED-N' headers, which is the
steward's own practice, so the next draft would have created a phantom item and
truncated the item containing it. PENDING-82's own fenced JSON block confirms the
fix within the hour.
Not installed. The mcpServers key edits the steward's desktop-app config; the
snippet is in PENDING-82 and the server is inert until someone loads it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WuMjg3ipEVa3n8CoSzoyvc
This commit is contained in:
co-authored by
Claude Opus 5
parent
d12feb5ba9
commit
3df09228c0
@@ -19,6 +19,10 @@ type: feedback
|
||||
- 2026-07-28T11:25 — ⚑ **The union verifier found 3 deficits, and the diagnosis matters more than the count.** All three are *header* lines, absent by intent: the stale `Repo: bmf` and `Branch: fix/replay-durability-contracts` pointers (that branch merged as `c9746ae`, HEAD is `main` — staleness already named in PENDING-78) plus the `Protocol:` line, reflowed. No governance content was lost. **But the header was rewritten inside `8abfe88` while the commit message mentions only the split** — a real edit to a governance file, unlogged. Third instance today of *the domain a check does not cover*: the morning's losslessness proof was over item blocks, so a deliberate 3-line header change sat outside its unit of account and was invisible to a proof that reported "lossless." Logged into `7f6157a`'s message rather than left silent. Reusable: **a losslessness proof is only as wide as its unit of account — name the unit, then ask what in the file is not made of it.**
|
||||
- 2026-07-28T11:25 — Same class, caught while in there: the header asserted *"the next item is PENDING-80"* while 79, 80 and 81 all exist. Replaced the stated number with the rule that computes it (next = one above the highest `## PENDING-<n>` in either file). A stated number is a drift source the drift-check cannot see, because it lives inside the file it describes.
|
||||
- 2026-07-28T11:35 — Fumbled this ledger three edits in a row: appended returns to the tail of **Authorization moves**, then created a **duplicate `## Open horizons`** heading, then a mid-file duplicate of `## Sub-agent dialogues`/`## Bypasses`. Rewrote the file whole rather than patching the patches. Small, but the shape is worth naming: **I was appending by anchor without holding the document's structure in view** — the same locality error as editing a section without reading the file. Cheap correction, no content lost; recorded rather than quietly tidied.
|
||||
- 2026-07-28T12:10 — ⚑⚑ **The day's question answered by walking into it three times, the third time inside the fix for the second.** Building the MCP server's read-only guarantee: v1 checked for write primitives with `[w for w in ('"w"', "os.remove", "shutil.", …) if w in src]` — which **found all nine, in its own token list.** Rewrote it over the AST. Re-ran: the *git* half of the same check, left as `'"commit"' not in src`, now failed because the source it reads contains `"commit"` and `"push"` **as the literals of the check itself.** Two instances of one shape, the second surviving my repair of the first because I fixed the half that failed rather than the *class*. Fixed both over AST (`write_calls`, `git_subcommands` — argv lists, not characters). **This is the answer to the literal question, and it is not "audit each instrument":** a text search for forbidden words can never clear a file that must name those words, so the fix is to stop measuring in the medium the instrument is written in. Ask of any check: *is its evidence the same kind of thing as its own source?* Kin to [[feedback-checkable-claim-surfaces-bugs]] — the demand for a checkable claim exposed the defect twice in ten minutes.
|
||||
- 2026-07-28T12:10 — ⚑ **A latent defect in the shared definition, with a trigger already in use.** `item_spans()` (the single definition of "an item") treated **any** `## ` line as a header, including inside fenced code blocks. Zero such headers exist in the substrate today — so no behaviour changed, 17 open items before and after — but governance drafts are written as fenced markdown carrying `## REVIEWED-N` headers, which is *the steward's own documented practice* ([[feedback-governance-drafting-copy-paste-clean]]). The next such draft would have produced a phantom item **and** truncated the real item containing it. Now fence-aware, with a paired control (fenced → ignored; same text unfenced → found). Confirmed load-bearing within the hour: PENDING-82's own body carries a fenced JSON block and spans correctly (L414–463). Reusable: **when checking whether a rule is sound, ask not "does the substrate violate it today" but "what practice already in use would violate it tomorrow."**
|
||||
- 2026-07-28T12:10 — ⚑ **A false pointer in yesterday's resumption point, in my own hand.** It said the two §Your Role edits were "drafted verbatim in the transcript **and in PENDING-81**." They are not in PENDING-81 — they existed only in a transcript the restart discarded. Same wrap that mis-stated the archive: **two false claims about where work lived, from one wrap.** Both are the same error as an unstaged file — believing something is recorded because I produced it. Re-drafted into PENDING-82, in the file. Doctrine already said this (*a draft that lives in a transcript is not a record*); the wrap protocol is where it failed to bite.
|
||||
- 2026-07-28T12:10 — My selftest asserted an item body contains **zero** `## ` headers; it contains exactly one, its own. Failed against correct code. Third instance of 09:35's shape (a control written before reading the output) — and note it was cheap to catch *because the control existed at all*. Rewrote to assert exactly-one-header plus a real boundary check, which PENDING-82's arrival immediately made non-vacuous.
|
||||
|
||||
## Open horizons
|
||||
|
||||
@@ -45,6 +49,7 @@ type: feedback
|
||||
- 2026-07-28T10:40 — ⚑ **The `.app`/`CLAUDE.md` question has a structural answer, not a tooling one.** Their readers differ in filesystem access, so one document can *compute* its state and the other can only *cache* it — confirmed by substrate (the live preferences exist nowhere on disk; only March-era sandbox snapshots). **Duplication between them is structurally required; only its staleness is optional.** The corollary I nearly missed: a pointer is worthless to a reader who cannot open files, which is *why* the preferences accumulated state in the first place. Reusable: before proposing a single-source-of-truth, check whether every reader can reach the source.
|
||||
- 2026-07-28T10:40 — ⚑ **The party structure itself has drifted, and both documents are constitutional.** `CLAUDE.md` names three parties; the `.app` preferences name Cowork as a fourth under `COWORK.md` — a real document, dated Mar 22, orphaned in an agent-mode sandbox — while calling the model three-party. The MemPalace weld shape, one layer up: doctrine welded to a retired instrument, where the instrument is *a party*. No generated block can fix it; it needs a ruling.
|
||||
- 2026-07-28T11:25 — **Archive break repaired on steward authorization** (*"yes, absolutely"*): `7f6157a`, pushed `8abfe88..7f6157a` to `github/main`. `PENDING-archive.md` now tracked; the record the remote carries is whole again. Verified **before** committing, not after: baseline `PENDING.md.bak-2026-07-28-pre-split` (1848 lines) ⊆ (`PENDING.md` ∪ `PENDING-archive.md`) at **line** granularity — no regex, no parser notion of "item" — with a same-run positive control (sentinel absent from the union → reported missing: true) plus a second control confirming the check is blind by design to the 65 post-split appends. Two [FIX]-class changes, both stated in the commit message rather than left to the diff: the archive add, and the header's self-contradicted counter. `~/CLAUDE.md` and `REVIEWED.md` untouched — Constraint #1 holds.
|
||||
- 2026-07-28T12:10 — **PENDING-82 placed** (`~/dotfiles/PENDING.md` L414): the read-only MCP server, `[PROPOSAL]`. Built and self-tested (27 controls + a live stdio round-trip); **not installed** — the `mcpServers` key edits the steward's app config, so the snippet is handed over rather than applied. Appended as a new item rather than edited into PENDING-81, per that file's own append-only rule. Carries the re-drafted §Your Role replacement text and the second, stronger ground for Cowork's retirement (remote Cowork, the incoming default, runs no local MCP at all — so it could not have been the jurist's eyes even in principle). **No REVIEWED-81/82 drafted:** composing a ruling before the ruling is the say–do seam this ledger opened on at 08:12. Offered, not written.
|
||||
|
||||
## Sub-agent dialogues
|
||||
|
||||
|
||||
Reference in New Issue
Block a user