fix(watcher): never walk into raw/rejected/ — brain_reject (closes #81)
CI / Lint / Test / Vet (push) Successful in 33s
CI / Mirror to GitHub (push) Successful in 4s

Adds brain_reject (RejectNote + MCP tool) and fixes the watcher to skip raw/rejected/, preventing declined notes from being silently re-ingested and republished.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Mathias <mthbqv@gmail.com>
This commit was merged in pull request #133.
This commit is contained in:
2026-09-23 06:06:35 +00:00
committed by mathias
co-authored by Claude Sonnet 5
parent 9a37881441
commit 674858fe5e
8 changed files with 228 additions and 6 deletions
+28
View File
@@ -96,6 +96,14 @@ func (s *Server) tools() []map[string]any {
"slug": str("optional target slug; defaults to filename minus date prefix"),
}),
},
{
"name": "brain_reject",
"description": "Reject a brain/raw/ note: stamps rejected_at (+ reject_reason if given) into frontmatter and moves it to brain/raw/rejected/ — never deleted outright, and never re-surfaced by brain_pending. The third option alongside promote/leave-pending; a human decision, never automatic. Returns {path}.",
"inputSchema": schema([]string{"filename"}, map[string]any{
"filename": str("basename in brain/raw/, e.g. 2026-04-23-fed-sentiment-scrape.md"),
"reason": str("optional; why this isn't worth promoting"),
}),
},
noteToolDescriptors()[0],
noteToolDescriptors()[1],
{
@@ -389,6 +397,26 @@ func (s *Server) brainPromote(ctx context.Context, args json.RawMessage) (json.R
return json.Marshal(map[string]string{"path": relPath})
}
type brainRejectArgs struct {
Filename string `json:"filename"`
Reason string `json:"reason,omitempty"`
}
// brainReject records a decision not to promote a raw/ note (api.RejectNote:
// frontmatter-stamp + move to raw/rejected/). No graph indexing — a rejected
// note never entered the wiki, so there's nothing to index.
func (s *Server) brainReject(_ context.Context, args json.RawMessage) (json.RawMessage, error) {
var a brainRejectArgs
if err := json.Unmarshal(args, &a); err != nil {
return nil, fmt.Errorf("parse args: %w", err)
}
relPath, err := api.RejectNote(s.brainDir, api.RejectOptions{Filename: a.Filename, Reason: a.Reason})
if err != nil {
return nil, err
}
return json.Marshal(map[string]string{"path": relPath})
}
// indexInGraph is a best-effort wrapper around graphsync.IndexDoc that
// logs failures but never propagates them — the underlying write/ingest
// has already succeeded and the graph is an augmentation, not a