First half of brain#21: the applicability envelope — half_life (volatile|
seasonal|durable), applies (local|homelab|universal), and claim_provenance
(asserted|inferred). The strength half (confidence, contested) follows once
the ingest-time contradiction detector exists to populate it.
writeHallNote already passed unknown frontmatter keys through verbatim, so
preservation was free — a test now pins it so a refactor of the merge loop
cannot silently drop the fields. What was missing is validation, and it
fails closed:
- a value outside the enum is rejected. An unvalidated value is worse than
an absent one, because a lint skips what it cannot parse and the note then
reads as classified while nothing acts on it;
- an envelope field without claim_provenance is rejected. A value a model
inferred must never be indistinguishable from one Mathias asserted —
otherwise a single classifier pass launders machine guesses into apparent
human judgement across 1113 notes, which is precisely how a weak claim
hardens into accepted fact;
- provenance without any envelope field is rejected too: it says who set
fields that are not there.
Validation runs before MkdirAll, so a rejected write leaves nothing behind —
not even an empty hall directory.
The field is `applies`, not `scope`, as brain#19 proposed: `scope:` has been
in use since brain#5 for glossary terms (homelab-core, project:<repo>), a
disjoint value space on the same key. The shipped schema keeps the name.
Notes with none of these fields are byte-identical to before. Bulk backfill
across the corpus stays out of scope — the same failure mode as the
extraction pass that produced 28 wrong wikilinks out of 67.
Schema documented in brain/CLAUDE.md (via brain_edit_root, which produced
the #97 audit record for the rewrite).
Refs brain#21, brain#19 (superseded), brain#5, hyperguild#97.
opts.Content can already carry its own "---\n...\n---" block (e.g. from
an upstream extraction/promotion step). writeHallNote used to prepend a
second block unconditionally, making a standard frontmatter parser blind
to everything the first block declared (title, tags, ...).
splitFrontmatter now pulls the existing block's fields out first;
wing/hall/created_at are always fresh-injected, and type/domain/
source_type prefer the note's own existing value over the opts
fallback. Remaining existing fields are carried through verbatim into
the single merged block.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Roq1ajWKR5f1hG5Df9wC6A
The unsourced-check fix (source_type: internal) only covered
claudewatcher's writes. 41 residual findings on the audit's next run
were manually-authored hall=facts entries written via brain_write/
capture -- also first-party observations, just a different write path,
with no way to mark them internal short of hand-editing every entry.
writeHallNote now defaults source_type to internal whenever hall ==
"facts" and the caller didn't set it explicitly. An explicit
source_type: external (now threaded through the /write HTTP endpoint)
opts a genuinely citation-needing entry back out.
brain-gardener#5: the audit's unsourced check was flagging 59 raw
claudewatcher session dumps as high-severity hallucination risk,
because it can't distinguish a first-party observation (a session
transcript) from an external claim needing citation.
Adds WriteNoteOptions.SourceType, emitted as source_type: <value> in
the wing/hall frontmatter route. claudeSink (the claudewatcher sink)
now always sets source_type: internal on the notes it writes.
Allows callers to provide pre-structured RawPage data directly, bypassing the
LLM extraction step. The pipeline still handles slug computation, frontmatter,
link canonicalization, source back-references, and dedup — only the extraction
is skipped. Useful when a more capable model or manual curation produces the
structured data.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wires pipeline.Run into the HTTP layer so callers can ingest raw text
or files/directories without touching the filesystem directly. Rewrites
main.go to parse LLM and watcher env vars and build pipeline.Config.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Empty or whitespace-only queries would silently pass through to search,
returning meaningless results. Also removed the Domain field from
queryRequest — it was accepted but silently ignored since search.Query
has no domain parameter, which would confuse callers.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements POST /query (BM25 search via internal/search) and POST /write
(raw file persistence to brain/raw/) as an api.Handler struct. Filename
is auto-generated when absent.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>