Brain-quality sweep, batch 1: brain_edit/brain_update correctness + close-session docs #124

Merged
mathias merged 4 commits from sweep/brain-quality-batch1 into main 2026-09-22 21:27:11 +00:00
4 Commits
Author SHA1 Message Date
mathiasandClaude Sonnet 5 e2cb315b31 fix(api): validate the merged envelope on supersede, not the caller's fragment
CI / Lint / Test / Vet (pull_request) Successful in 32s
CI / Mirror to GitHub (pull_request) Skipped
Review caught a real bypass in the #111 fix before merge: the previous
commit validated only the frontmatter fence the caller supplied, not the
merged result that actually reaches disk. A caller could blank
claim_provenance alone (absent per claim.present's "empty is unset" rule)
while half_life/applies survived untouched from the prior note — each side
individually looked fine, the merged envelope on disk was the
unprovenanced one the whole check exists to catch. That's the exact
laundering #111 named as the worse of its two gaps.

Fixed by computing the envelope from fm (post-merge, pre-write) instead of
contentFM (the caller's raw fragment). Flip-tested: reverting to
fragment-only validation turns the new adversarial test red; the merged
validation turns it green.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 23:24:50 +02:00
mathiasandClaude Sonnet 5 31ebc8afc4 docs(close-session): teach the skill the claim envelope, drop the stale tag-list hint
CI / Lint / Test / Vet (pull_request) Successful in 33s
CI / Mirror to GitHub (pull_request) Skipped
hyperguild#112: two documentation gaps found checking the skill against
current state.

1. The skill never mentioned the claim envelope (half_life/applies/
   claim_provenance) capture's insight-write now validates — the natural
   place to say a close-session insight is usually exactly the durable,
   universal, human-decided kind it describes.
2. The "tagged internal today" repo list was a frozen snapshot next to a
   fail-safe gate — the expensive direction to mislead in. Replaced with
   the rule (untagged -> confidential) plus a pointer at classification.yaml
   as sole authority, since hyperguild#91 already moved the real count from
   10 to ~39 repos and it will keep moving.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 23:20:42 +02:00
mathiasandClaude Sonnet 5 c50c64f93b fix(api): validate the claim envelope on supersede, same as on create
hyperguild#111: writeHallNote validates half_life/applies/claim_provenance
before a note is born; UpdateNote's supersede path didn't, so the fields
that need provenance most (a whole-note regenerate is more likely to carry
model-authored frontmatter than a fresh create) went unchecked.

Fixed by extracting any frontmatter fence the caller leads Content with,
validating it with the same claim.ValidateEnvelope, and merging its fields
into the restamped frontmatter instead of leaving it as a second, inert
`---`-fenced block in the body — that merge was a real gap independent of
validation: a caller's supersede-time frontmatter was previously silently
dropped, never taking effect at all.

Reject leaves the note byte-identical (fail-closed, nothing written).
Fence-less content (the common case today) is unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 23:20:12 +02:00
mathiasandClaude Sonnet 5 1e57f49595 fix(mcp): name raw argument bytes when brain_edit refuses an empty old_str
hyperguild#115: a session reported "old_str is empty" with old_str that
was never empty on their end. Server-side round-trip re-verified clean
(fresh brain_edit call, real content, succeeds) — the code path itself
isn't broken, but its error left no way to tell "client sent empty" apart
from "something between the client and here lost the field" without a
second investigation. The refusal now names the raw argument byte count.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 23:18:43 +02:00