fix(pipeline,classification): read the brain through brainfs (#157, slice 6)
CI / Lint / Test / Vet (pull_request) Successful in 1m3s
CI / Mirror to GitHub (pull_request) Skipped

The ingest pipeline read wiki/sources/<slug>.md and schema.md (both go
into the LLM prompt), the pages it revises, and the pages it adds
source refs to, with os.ReadFile; BackfillRefs listed wiki/sources with
os.ReadDir. A symlinked file's content reached the prompt and the
written pages (1.6M leaks in the race), and a FIFO hung the ingest.
classification.Load (the I1 sovereignty levels) read
classification.yaml the same way and hung startup on a FIFO.

- pipeline: readSourcePage, loadSchema, reviseOrCreate, injectSourceRefs
  read through readBrainFile (brainfs ReadRegular); reviseOrCreate takes
  the brain dir and reads page.Path, a missing page is still created and
  a non-regular one is an error. BackfillRefs lists with ReadDirRegular
  and reads with ReadRegular.
- classification.Load: read through brainfs; a symlinked or special
  classification.yaml is a hard error (as a malformed one is); an absent
  file or brain dir still yields the default config.

Tests use in-brain targets and the [[slug|title]] link form the pipeline
actually follows (bare [[slug]] links are ignored by it, which made a
first draft of the backfill test vacuous).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-05 07:13:39 +02:00
co-authored by Claude Opus 5.5
parent b6ef11600b
commit 70df6db7a0
6 changed files with 238 additions and 21 deletions
+17 -8
View File
@@ -3,7 +3,10 @@ package pipeline
import (
"context"
"errors"
"fmt"
"github.com/mathiasbq/hyperguild/ingestion/internal/brainfs"
"io/fs"
"os"
"path/filepath"
"strings"
@@ -23,10 +26,16 @@ func BackfillRefs(ctx context.Context, brainDir string) (int, error) {
return 0, fmt.Errorf("load inventory: %w", err)
}
sourcesDir := filepath.Join(brainDir, "wiki", "sources")
entries, err := os.ReadDir(sourcesDir)
// Listed and read through the brain root, regular files only
// (hyperguild#157).
bfs, err := brainfs.Open(brainDir)
if err != nil {
if os.IsNotExist(err) {
return 0, fmt.Errorf("open brain: %w", err)
}
defer func() { _ = bfs.Close() }()
names, err := bfs.ReadDirRegular("wiki/sources")
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
return 0, nil
}
return 0, fmt.Errorf("read sources dir: %w", err)
@@ -37,20 +46,20 @@ func BackfillRefs(ctx context.Context, brainDir string) (int, error) {
// get both refs merged before a single write.
pending := make(map[string]wiki.Page)
for _, e := range entries {
for _, name := range names {
if ctx.Err() != nil {
return 0, ctx.Err()
}
if e.IsDir() || !strings.HasSuffix(e.Name(), ".md") {
if !strings.HasSuffix(name, ".md") {
continue
}
b, err := os.ReadFile(filepath.Join(sourcesDir, e.Name()))
b, err := bfs.ReadRegular("wiki/sources/" + name)
if err != nil {
continue
}
sourceContent := string(b)
sourceSlug := strings.TrimSuffix(e.Name(), ".md")
sourceSlug := strings.TrimSuffix(name, ".md")
sourceTitle := extractTitle(sourceContent)
if sourceTitle == "" {
sourceTitle = sourceSlug
@@ -70,7 +79,7 @@ func BackfillRefs(ctx context.Context, brainDir string) (int, error) {
// Start from already-accumulated version if we've seen this page.
page, seen := pending[relPath]
if !seen {
raw, err := os.ReadFile(filepath.Join(brainDir, filepath.FromSlash(relPath)))
raw, err := bfs.ReadRegular(relPath)
if err != nil {
continue
}