fix(pipeline,api,wing): never archive a note as processed when nothing was ingested; titles do not merge sources; ties prefer the page on disk (review of #211, pass 3)
CI / Lint / Test / Vet (pull_request) Successful in 1m6s
CI / Mirror to GitHub (pull_request) Skipped

- in a wing, an ingest that writes no source page is an error: the note goes to
  raw/failed with the warnings in log.md instead of leaving the inbox as
  'processed' (model returned [], only invalid types, only clashing entities)
- the wing's source path is deterministic, so Resolve no longer redirects a
  source onto another note's page because the model gave both the same title
  ('Home'); that overwrote the hash and the text
- a batch tie between concept and entity prefers the page already on disk, so
  its update is not lost; the wing's own source page keeps its slug even when a
  foreign page shares it
- brain_pending lists an unreadable (over-cap) note with the reason instead of
  dropping it; the client filter's 4 KiB match-span limit is documented
Each has a test that fails without it (mutation-checked).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-20 20:56:03 +02:00
co-authored by Claude Sonnet 5.5
parent 49286ba8b8
commit 27b1579b80
8 changed files with 214 additions and 10 deletions
+10
View File
@@ -97,6 +97,16 @@ func listNotes(root *os.Root, queue string) ([]PendingNote, error) {
}
raw, info, readErr := readRawNote(root, e.Name())
if readErr != nil {
// Not skipped: a note that cannot be read (over the cap, say) must
// not silently vanish from the queue it is waiting in.
fi, ierr := root.Lstat(e.Name())
if ierr != nil {
continue
}
out = append(out, PendingNote{
Filename: e.Name(), CreatedAt: fi.ModTime().UTC().Format(time.RFC3339), SizeBytes: fi.Size(),
Excerpt: "(cannot be read: " + readErr.Error() + ")", Queue: queue,
})
continue
}
fm, body := parseFrontmatter(string(raw))
@@ -190,3 +190,21 @@ func TestReadCapped_StopsAtTheCap(t *testing.T) {
assert.Contains(t, err.Error(), "exceeds")
assert.LessOrEqual(t, e.n, int64(1000+1+32*1024), "pulled far more than the cap: %d", e.n)
}
// An .md over the read cap used to vanish from the listing (readRawNote failed
// and the entry was skipped). It must stay visible, with the reason.
func TestListPending_AnUnreadableNoteIsListedWithTheReason(t *testing.T) {
dir := t.TempDir()
require.NoError(t, os.MkdirAll(filepath.Join(dir, "raw"), 0o755))
require.NoError(t, os.WriteFile(filepath.Join(dir, "raw", "huge.md"), []byte(strings.Repeat("x", 200)), 0o644))
prev := maxRawNoteBytes
maxRawNoteBytes = 100
t.Cleanup(func() { maxRawNoteBytes = prev })
got, err := ListPending(dir)
require.NoError(t, err)
require.Len(t, got, 1)
assert.Equal(t, "huge.md", got[0].Filename)
assert.Contains(t, got[0].Excerpt, "cannot be read")
assert.Contains(t, got[0].Excerpt, "exceeds")
}