[HARDENING] governance-mcp: the jurist can now read the constitution it design-gates (PENDING-86 a)

Steward-authorized 2026-08-05, on the jurist's own request while unable to close
PENDING-99's Q2 — a question that turns on the chamber constitution's vocabulary
(§II.3's "inline anchor marker", §V's marker exclusion), which governance_read
did not expose. Third recorded instance on PENDING-86: the constitution, the
skill files, contamination-problem.md.

Adds two keys to the existing enum: `chamber-spec`, `graduation-spec`. No new
tool, no path argument, no traversal surface — the domain stays enumerable and
every refusal control still passes.

⚠ THE NON-OBVIOUS PART. Reachability of the KEY is not reachability of the
CLAUSE. This file's operative sections begin around line 354; the ~330 lines
above them are SUPERSEDED version headers kept as the amendment trail. A jurist
reading with the default limit=400 would land squarely in obsoleted text and
could rule on superseded clauses — the new access CAUSING the misruling it
exists to prevent. So the trap is disclosed on the key's own description, at the
point of use, and two controls pin it:

  - the §V inline-anchor clause and the §II.3 marker constraint are both
    reachable in ONE paged call (offset=350, limit=2000) — the actual Q2 text
  - NEGATIVE CONTROL: a first-page read does land in the "(obsoleted)" region,
    proving the trap is real rather than hypothetical

Selftest 29 → 35 controls, 0 fail. Live stdio round-trip confirms the §V clause
arrives verbatim through governance_read.

⚠ Requires a Claude.app restart: the running server process carries the old
code and will not show the new keys until respawned.

Option (d) — keyword search across PENDING/PENDING-archive/REVIEWED — is NOT in
this change and remains open on PENDING-86. It is a new tool surface, not two
enum entries, and the jurist asked for (a)+(d) together.

Refs PENDING-86, PENDING-99 Q2, PENDING-82.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AB3Kryoy6b1pm2Nz1DYdLh
This commit is contained in:
David F Glidden
2026-08-05 21:17:33 +02:00
co-authored by Claude Opus 5
parent 40a6a2aaf8
commit 5cd5faf66e
+35
View File
@@ -69,6 +69,20 @@ FILES = {
"claude-md": (os.path.join(D, "CLAUDE.md"), "the executor's governing document"), "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"), "memory-index": (os.path.join(wd.MEM, "MEMORY.md"), "wake-loaded memory index"),
"app-brief": (wd.BRIEF_PATH, "last generated .app Standing Context block"), "app-brief": (wd.BRIEF_PATH, "last generated .app Standing Context block"),
# 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
# rules on. Read-only, keyed, no path argument — the enum design is unchanged.
"chamber-spec": (os.path.join(HOME, "_Dev", "chamber-library", "docs",
"chamber-library-specification.md"),
"the chamber library constitution — ⚠ the OPERATIVE version header is "
"at the top, but roughly the next 330 lines are SUPERSEDED version "
"headers kept as the amendment trail; the operative sections start at "
"'## 0. What this document is'. Page past them (offset≈350) or you will "
"be reading obsoleted text"),
"graduation-spec": (os.path.join(HOME, "_Dev", "chamber-library", "_curation",
"graduation-spec.yaml"),
"the constitution's machine-readable convention-data + gate-list"),
} }
REPOS = wd.REPOS REPOS = wd.REPOS
@@ -351,6 +365,27 @@ def selftest():
t_read({"file": "../../.ssh/id_rsa"}).startswith("ERROR: unknown file key")) t_read({"file": "../../.ssh/id_rsa"}).startswith("ERROR: unknown file key"))
chk("governance_read ACCEPTS a listed key [positive control for the refusal above]", chk("governance_read ACCEPTS a listed key [positive control for the refusal above]",
t_read({"file": "pending"}).startswith("[pending —")) t_read({"file": "pending"}).startswith("[pending —"))
print("\nPENDING-86 (a) — the documents the jurist design-gates are now reachable:")
chk("chamber-spec key reads",
t_read({"file": "chamber-spec"}).startswith("[chamber-spec —"))
chk("graduation-spec key reads",
t_read({"file": "graduation-spec"}).startswith("[graduation-spec —"))
# Reachability of the KEY is not reachability of the CLAUSE. These two controls
# test the actual thing the item exists for: the §II.3 / §V text a Q2-shaped
# ruling turns on. A key that opens onto 330 lines of superseded headers would
# pass the two checks above and still leave the gap wide open.
_spec_deep = t_read({"file": "chamber-spec", "offset": 350, "limit": 2000})
chk("§V's inline-anchor clause is reachable in one paged call [the Q2 clause]",
"is content-for-the-reader but is **not a prose word**" in _spec_deep)
chk("§II.3's marker constraint is reachable in the same call [the Q2 clause]",
"must not corrupt the" in _spec_deep and "prose-word-identity check under §V" in _spec_deep)
chk("the superseded-header trap is disclosed on the key itself [it would cause the "
"misruling this access exists to prevent]",
"SUPERSEDED version" in FILES["chamber-spec"][1])
chk("a first-page read lands in the SUPERSEDED region [negative control: proves the "
"trap is real, not hypothetical]",
"(obsoleted)" in t_read({"file": "chamber-spec", "limit": 300}))
chk("repo_activity refuses an unlisted repo", chk("repo_activity refuses an unlisted repo",
t_repo({"repo": "/etc"}).startswith("ERROR: unknown repo")) t_repo({"repo": "/etc"}).startswith("ERROR: unknown repo"))
chk("repo_activity ACCEPTS a listed repo [positive control]", chk("repo_activity ACCEPTS a listed repo [positive control]",