brain_pending lists notes in brain/raw/ awaiting human review; brain_promote
moves one into the structured wiki. There is no third option. Confirmed by
reading ingestion/internal/api/promote.go + ingestion/internal/mcp/handlers.go
directly (not guessing): only ListPending/PromoteNote exist, no RejectNote, no reject.go, no reject tool descriptor.
Live consequence:brain_pending currently returns 4 items, oldest dated
2026-04-23 — three months old. They're not low-value by content (financial
sentiment analysis / hash-encoding scrape notes, an invoice-extraction design
note) — they're stuck because there's no way to say "reviewed, not worth
promoting" and have that recorded. The only way to remove one today is an
un-audited rm on the raw file directly, bypassing the tool surface entirely.
This is the same shape of gap as brain-gardener#104 (a queue that only grows
because nothing forces or records a negative decision) — found while auditing
that exact failure mode in a sibling system.
Proposal
Add RejectNote(brainDir, filename, reason string) (string, error) in ingestion/internal/api/promote.go (or a new reject.go alongside it),
matching PromoteNote's existing conventions:
Same filename validation (basename-only, no traversal — reuse the exact
check PromoteNote already does).
Move (not delete) brain/raw/<filename> → brain/raw/rejected/<filename>,
write-then-remove like PromoteNote's write-then-delete pattern, so a
filesystem failure never loses the note.
Stamp rejected_at (+ reject_reason if given) into frontmatter before the
move, same style as promoted_at.
No index rebuild / auto-tunnel needed (rejected notes never entered the wiki).
Expose as brain_reject in handlers.go's tool list, same schema shape as brain_promote (filename required, reason optional) — human-invoked only,
matching the existing promote/pending human-in-the-loop model. Never called
automatically; this stays a human decision, same principle as issue-status-derived-surfaced-not-closed in the brain (issue closure is a
human judgment call, never a mechanical fact) — a reject is exactly that same
shape of judgment call, just one level earlier in the pipeline.
brain_reject MCP tool wired in, descriptor + handler + test (mirror brain_promote's existing test in handlers_test.go).
Live-run against the 4 real stale pending items once shipped — the actual
backlog that surfaced this gap should get cleared as part of verifying it.
Related
brain-gardener#104/#121/#122 (same failure shape: unreviewed queue, no
negative-decision path, discovered during a broader "how do we keep brain
weeds from taking root" review, 2026-07-15).
Also noticed while reading ListPending: it explicitly skips tunnel-candidates-*.md files in raw/ — a second, distinct candidate queue
(auto-detected cross-wing link suggestions?) that wasn't investigated this
session. Worth checking whether it has the same rot risk — filed as a
discovered-but-deferred note, not scoped here.
## Context
`brain_pending` lists notes in `brain/raw/` awaiting human review; `brain_promote`
moves one into the structured wiki. There is no third option. Confirmed by
reading `ingestion/internal/api/promote.go` + `ingestion/internal/mcp/handlers.go`
directly (not guessing): only `ListPending`/`PromoteNote` exist, no
`RejectNote`, no `reject.go`, no reject tool descriptor.
**Live consequence:** `brain_pending` currently returns 4 items, oldest dated
2026-04-23 — three months old. They're not low-value by content (financial
sentiment analysis / hash-encoding scrape notes, an invoice-extraction design
note) — they're stuck because there's no way to say "reviewed, not worth
promoting" and have that recorded. The only way to remove one today is an
un-audited `rm` on the raw file directly, bypassing the tool surface entirely.
This is the same shape of gap as brain-gardener#104 (a queue that only grows
because nothing forces or records a negative decision) — found while auditing
that exact failure mode in a sibling system.
## Proposal
Add `RejectNote(brainDir, filename, reason string) (string, error)` in
`ingestion/internal/api/promote.go` (or a new `reject.go` alongside it),
matching `PromoteNote`'s existing conventions:
- Same filename validation (basename-only, no traversal — reuse the exact
check `PromoteNote` already does).
- Move (not delete) `brain/raw/<filename>` → `brain/raw/rejected/<filename>`,
write-then-remove like `PromoteNote`'s write-then-delete pattern, so a
filesystem failure never loses the note.
- Stamp `rejected_at` (+ `reject_reason` if given) into frontmatter before the
move, same style as `promoted_at`.
- No index rebuild / auto-tunnel needed (rejected notes never entered the wiki).
Expose as `brain_reject` in `handlers.go`'s tool list, same schema shape as
`brain_promote` (`filename` required, `reason` optional) — human-invoked only,
matching the existing promote/pending human-in-the-loop model. Never called
automatically; this stays a human decision, same principle as
`issue-status-derived-surfaced-not-closed` in the brain (issue closure is a
human judgment call, never a mechanical fact) — a reject is exactly that same
shape of judgment call, just one level earlier in the pipeline.
## Acceptance
- [ ] `RejectNote` + test (moves file, stamps frontmatter, write-then-remove
safety, filename validation matches `PromoteNote`'s).
- [ ] `brain_reject` MCP tool wired in, descriptor + handler + test (mirror
`brain_promote`'s existing test in `handlers_test.go`).
- [ ] Live-run against the 4 real stale pending items once shipped — the actual
backlog that surfaced this gap should get cleared as part of verifying it.
## Related
- brain-gardener#104/#121/#122 (same failure shape: unreviewed queue, no
negative-decision path, discovered during a broader "how do we keep brain
weeds from taking root" review, 2026-07-15).
- Also noticed while reading `ListPending`: it explicitly skips
`tunnel-candidates-*.md` files in `raw/` — a second, distinct candidate queue
(auto-detected cross-wing link suggestions?) that wasn't investigated this
session. Worth checking whether it has the same rot risk — filed as a
discovered-but-deferred note, not scoped here.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Context
brain_pendinglists notes inbrain/raw/awaiting human review;brain_promotemoves one into the structured wiki. There is no third option. Confirmed by
reading
ingestion/internal/api/promote.go+ingestion/internal/mcp/handlers.godirectly (not guessing): only
ListPending/PromoteNoteexist, noRejectNote, noreject.go, no reject tool descriptor.Live consequence:
brain_pendingcurrently returns 4 items, oldest dated2026-04-23 — three months old. They're not low-value by content (financial
sentiment analysis / hash-encoding scrape notes, an invoice-extraction design
note) — they're stuck because there's no way to say "reviewed, not worth
promoting" and have that recorded. The only way to remove one today is an
un-audited
rmon the raw file directly, bypassing the tool surface entirely.This is the same shape of gap as brain-gardener#104 (a queue that only grows
because nothing forces or records a negative decision) — found while auditing
that exact failure mode in a sibling system.
Proposal
Add
RejectNote(brainDir, filename, reason string) (string, error)iningestion/internal/api/promote.go(or a newreject.goalongside it),matching
PromoteNote's existing conventions:check
PromoteNotealready does).brain/raw/<filename>→brain/raw/rejected/<filename>,write-then-remove like
PromoteNote's write-then-delete pattern, so afilesystem failure never loses the note.
rejected_at(+reject_reasonif given) into frontmatter before themove, same style as
promoted_at.Expose as
brain_rejectinhandlers.go's tool list, same schema shape asbrain_promote(filenamerequired,reasonoptional) — human-invoked only,matching the existing promote/pending human-in-the-loop model. Never called
automatically; this stays a human decision, same principle as
issue-status-derived-surfaced-not-closedin the brain (issue closure is ahuman judgment call, never a mechanical fact) — a reject is exactly that same
shape of judgment call, just one level earlier in the pipeline.
Acceptance
RejectNote+ test (moves file, stamps frontmatter, write-then-removesafety, filename validation matches
PromoteNote's).brain_rejectMCP tool wired in, descriptor + handler + test (mirrorbrain_promote's existing test inhandlers_test.go).backlog that surfaced this gap should get cleared as part of verifying it.
Related
negative-decision path, discovered during a broader "how do we keep brain
weeds from taking root" review, 2026-07-15).
ListPending: it explicitly skipstunnel-candidates-*.mdfiles inraw/— a second, distinct candidate queue(auto-detected cross-wing link suggestions?) that wasn't investigated this
session. Worth checking whether it has the same rot risk — filed as a
discovered-but-deferred note, not scoped here.