feat(mcp): record and alert on a governance-file rewrite (#97)
brain_edit_root was gated but unobserved: on success it emitted a slog.Warn and nothing else. That proves a rewrite was allowed, not that one happened — and brain-sync commits it within 15 minutes authored `brain-sync`, so git history cannot tell a CLAUDE.md rewrite apart from routine note traffic. CLAUDE.md is what the next agent loads as instructions. A successful brain_edit_root now emits an ntfy notification and an AuditEntry through the same sink a capture already uses. notes.Result gains PrevContentHash so both ends of the change are identifiable. Path and hashes only in the notification body. It leaves the cluster for a phone via a third-party topic, and a hash identifies the rewrite while the bytes would leak whatever was written into an instruction file. Reserved at Internal, not Confidential: Confidential makes DegradingSink refuse to record while loki is down rather than buffer, which would drop exactly the record that matters most. Observation is best-effort and never fails the call. The bytes are already on disk and will be committed, so returning an error would report a non-rewrite to the caller while the rewrite stands. A failure to observe logs at ERROR — that is the case where the trail itself broke. Ordinary brain_edit on knowledge//wiki/ is deliberately NOT audited. Git history already carries the same before/after bytes with a diff; the asymmetry is that governance files change what later agents obey. Routine note traffic in the same trail would also bury the rare governance record, which is how an alert stops being read. Red-first: the audit-contents and notification tests failed on an empty sink before the observer existed. The three negative tests (refused call, failed edit, ordinary note edit) passed from the start, pinning behaviour that must not change. Refs #97, #96, brain#4.
This commit is contained in:
@@ -49,6 +49,8 @@ type rpcError struct {
|
||||
// Server handles MCP JSON-RPC over HTTP for the ingestion service.
|
||||
type Server struct {
|
||||
brainDir string
|
||||
govAudit capture.AuditSink
|
||||
govNotify govNotifier
|
||||
pipeline pipeline.Config
|
||||
llm pipeline.CompleteFunc
|
||||
answerLLM pipeline.CompleteFunc // nil = brain_answer and brain_classify unavailable
|
||||
|
||||
Reference in New Issue
Block a user