Commit Graph
12 Commits
Author SHA1 Message Date
mathiasandClaude Sonnet 5.5 27b1579b80 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>
2026-10-20 20:56:03 +02:00
mathiasandClaude Sonnet 5.5 6f5fb481ad fix(wing): client filter handles multi-word/\s/\b patterns, homoglyphs, marks, fillers; checked in bounded chunks (review of #211, pass 2)
Views of each chunk: as is; NFKD + marks/format/fillers dropped + Cyrillic/Greek
lookalikes mapped + dashes normalised, with whitespace collapsed (acme corp,
\s) or removed (acme-\nclient); and a soft hyphen read as a dash. Chunks of 1 MiB
with a 4 KiB overlap bound the folding's allocation (it was ~14x the input: 919
MiB for a 64 MiB note). Documented limits: leetspeak, punctuation splits,
encodings and homoglyphs outside a small table. Mutation-checked.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 20:45:00 +02:00
mathiasandClaude Sonnet 5.5 6b6d430383 fix(wing): the client filter survives cheap evasions; lookalike and reserved folder names are refused (review of #211)
- client pattern is matched against the text and against an NFKC-folded,
  format-character-free, dash-normalised, whitespace-free form
  (non-breaking hyphen, zero-width characters, soft hyphen, line splits,
  fullwidth letters). x/text/unicode/norm was already in the module graph as an
  indirect dependency; this makes it direct, no new download.
- folder/frontmatter wing names must be ASCII (strings.ToLower folds the Kelvin
  sign to 'k') and must not be sources/concepts/entities/wiki; such names are
  never proposed as a wing, and a reserved default is a startup error
Mutation-checked.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 20:29:09 +02:00
mathiasandClaude Sonnet 5.5 8988c291ca feat(server): INGEST_WING_ROUTING wires the Router; remove the classifier path (#208)
Removes the LLM classifier, the substring 'BM25' scorer, the agreement gate,
the backtest and the old migrate/wingmigrate tooling: measured at 2 of 55
pages placed (#208), and the migration was built for the per-wing facts layout
this branch replaces. wing now holds the decision types, the Router and
KnownWings. INGEST_WING_ROUTING=1 builds the Router (default wing
INGEST_DEFAULT_WING=web-clip, client pattern from INGEST_CLIENT_BLOCK or
CLAUDE_INGEST_CLIENT_BLOCK); a bad default or pattern is fatal at startup.
Watcher tests rewritten for the Router, including a deliberate subfolder into
any existing wing, typo'd folders parked, forged-key and edit-while-placing
cases.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 20:18:39 +02:00
mathiasandClaude Sonnet 5.5 8342de5c49 feat(wing): Router — rules-only placement: deliberate subfolder, else default wing (#208)
No model. A subfolder/frontmatter wing must be a plain name of an existing wing
and then places; conflicting signals and client-marker text park; everything
else goes to the default wing. Wings are read from disk per decision.
Mutation-checked.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 20:05:29 +02:00
mathiasandClaude Sonnet 5.5 006a891423 fix(watcher): never overwrite a parked note; a proposal cannot be forged or pre-empted; CRLF notes; wing dirs must be plain (#205)
CI / Lint / Test / Vet (pull_request) Failing after 1m2s
CI / Mirror to GitHub (pull_request) Skipped
From the independent review of #207:
- parked notes are created O_EXCL with -2,-3 suffixes (same-named notes from
  different subfolders were overwritten and their originals deleted)
- proposed_wing is emitted only if a plain wing name; a note's own proposal_*
  keys are dropped so they cannot outrank the resolver's (first-wins reads)
- CRLF notes: wing: key honoured, one valid frontmatter block on parking
- KnownWings skips wiki dirs that are not canonical lower-case names
- adds the wingmigrate dry-run/apply command
Each fix has a test that failed first and a mutant that fails it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 06:16:48 +02:00
mathiasandClaude Sonnet 5.5 20d874a23d feat(server): INGEST_WING_RESOLVE=1 wires the wing resolver into the watcher (#205 S3)
Off by default (retired layout unchanged). On: eligibility from
classification.yaml, local ingest LLM as classifier, BM25 as cross-check,
INGEST_WING_THRESHOLD (default 0.9, rejected outside [0,1]). Build failure
is fatal when explicitly enabled.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 06:09:17 +02:00
mathiasandClaude Sonnet 5.5 62de8ac2e6 feat(wing): score a wing by its best page, not summed hits; add wingbacktest command (#205 S1)
A first backtest showed BM25 summed over a wing's top hits favours big wings
(0/4 agreement). Best-page scoring is volume-robust; pinned by a test that
failed before the change.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 06:04:45 +02:00
mathiasandClaude Sonnet 5.5 365130459f feat(wing): backtest metric (coverage/precision at a threshold) and shared TopWing (#205 S1)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 05:58:55 +02:00
mathiasandClaude Sonnet 5.5 c06509afe4 feat(wing): BM25 scorer as the independent cross-check (#205 S1)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 05:58:14 +02:00
mathiasandClaude Sonnet 5.5 85d8ecb5c7 feat(wing): LLM classifier with tolerant, fail-closed reply parsing (#205 S1)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 05:57:51 +02:00
mathiasandClaude Sonnet 5.5 5bbf47064d feat(wing): resolver that places a raw note in a wing without human typing (#205 S1)
Explicit subfolder/frontmatter wins; otherwise a classifier and an
independent scorer must agree above a threshold on a known, eligible wing.
Anything else is an Exception for a human. Never creates a wing, never
assigns an ineligible (client/untagged) wing. Mutation-checked: dropping the
eligibility, known-wing, scorer, threshold or conflict check fails a test.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 05:56:55 +02:00