Add brain_reject: brain/raw/ pending notes can only be promoted, never discarded #81
Closed
opened 2026-07-15 09:45:18 +00:00 by mathias
·
3 comments
No Branch/Tag Specified
main
fix/pipeline-writes-158
fix/edit-path-io-192
fix/create-regular-188
fix/guard-special-179
fix/linkcheck-keeps-write-178
fix/write-dir-redirect-173
fix/tunnel-detect-174
fix/pipeline-root-157
w3/154-mcp-auth-wiring
fix/tunnel-traversal
test/in-brain-targets-171
fix/brain-scan-root-157
fix/graph-wiki-root-157
fix/vectorstore-root-157
fix/notes-root-157
fix/search-root-157
fix/brainwide-root-157
fix/raw-symlinks-153
feat/symlink-tripwire-157
fix/rest-auth-149
fix/123-brain-edit-envelope-check
feat/106-page-aware-extraction
fix/105-source-drift-on-main
fix/104-surface-merge-conflicts-on-main
agent/b1-compile-on-ingest
agent/104-surface-merge-conflicts
agent/105-source-drift
agent/107-loud-unconfigured-alerting
feat/brain-read-edit
fix/capture-i1-refusal-names-target
renovate/configure
feat/consolidate-single-harness
icebox/cmd-routing
feat/brain-pending-promote
fix/close-session-classification-refresh
fix/capture-summarywriter
v0.24.0
v0.23.1
v0.23.0
v0.22.0
v0.21.1
v0.21.0
v0.20.1
v0.20.0
v0.19.1
v0.19.0
v0.18.4
v0.18.3
v0.18.2
v0.18.1
v0.18.0
v0.17.0
v0.16.1
v0.16.0
v0.15.0
v0.14.0
v0.13.2
v0.13.1
v0.13.0
v0.12.1
v0.12.0
v0.11.2
icebox/cmd-routing-2026-07-01
v0.11.1
v0.11.0
v0.10.0
v0.9.0
v0.8.0
v0.7.0
v0.6.0
v0.5.0
v0.4.1
v0.4.0
v0.3.0
v0.2.0
v0.1.0
Labels
Clear labels
agent/ready
blocked/client
blocked/supplier
blocked/upstream
risk/high
risk/low
risk/medium
size/l
size/m
size/s
size/xs
status:dispatch
status:dispatched
status:review
status:reviewing
type/chore
type/decision
type/docs
type/feat
type/fix
type/ops
type/research
type/test
Meets the agent-ready contract; authorizes unattended execution
Waiting on the client
Waiting on a supplier
Waiting on an upstream project or vendor
Irreversible, security-relevant, or control plane
Reversible, local, test-covered
Touches shared state, deploys or data
Too big - split it
Under two days
Under half a day
Under an hour
Maintenance, no behaviour change
Human decision required; never agent/ready
Documentation only
New capability
Corrects broken behaviour
Infrastructure, deploy, runtime
Investigate; output is a finding, not code
Test coverage only
Milestone
No items
No Milestone
Projects
Clear projects
No projects
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: mathias/hyperguild#81
Reference in New Issue
Block a user
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.
🤖 dispatch: routing: executor=claude (default — no executor: label) — starting headless run
🤖 dispatch: executor claude failed: runner error: agent run ended in error (see stderr)
Re-reviewed and merged. Merge commit
674858fe5eec9d220260a6eeaa764fba02a272ff(squash of PR #133, head69731d9).Watcher bug fix verified independently (not trusted from the commit message):
"rejected"is in the skip-list at the exactd.IsDir()branch inprocessDir(ingestion/internal/watcher/watcher.go)."rejected"from the skip-list ->TestProcessDir_SkipsSubdirswent red withfile processed file=declined-file.mdin the log, exactly the bug described. Restored -> green again.graphsync.BackfillFromBrainDir(roots := []string{"wiki", "knowledge"}) andsearch.resolveRoots(all returned paths underwiki//knowledge/) - neither touchesraw/, confirming the PR's claim.ingestion/module for everyraw/raw/reference to find a fourth consumer: foundinternal/brain/tunnel.go'slogFuzzyCandidates(writes intoraw/root, never reads/walks it - harmless) andinternal/notes/path.go'sdeniedPrefixes(a denylist that explicitly refusesraw/in every scope - protective, not a consumer). No fourth walker ofraw/rejected/exists.go build ./... && go vet ./... && go test ./... -race -count=1, all packages pass.69731d9, matching PR head exactly.Gap found, not blocking: the issue's third acceptance item ("live-run against the real stale pending items once shipped") was never done - the PR author explicitly deferred it to post-deploy. Filed as #134 to track so it isn't lost to merge auto-close.