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>
This commit is contained in:
2026-10-20 06:09:17 +02:00
co-authored by Claude Sonnet 5.5
parent 2c6220782d
commit 20d874a23d
7 changed files with 196 additions and 27 deletions
+32
View File
@@ -0,0 +1,32 @@
package wing
import (
"fmt"
"github.com/mathiasbq/hyperguild/ingestion/internal/classification"
)
// NewConfiguredResolver builds the production Resolver for a brain: Known from
// the wiki, Eligible from classification.yaml (a wing is auto-assignable only
// when it classifies below Confidential, so an untagged wing never is), the
// LLM classifier over complete, and BM25 over the known wings as the scorer.
//
// Known is read once. A wing created later is unknown to this Resolver until
// the process restarts, which fails safe: notes aimed at it are parked.
func NewConfiguredResolver(brainDir string, complete CompleteFunc, threshold float64) (*Resolver, error) {
known, err := KnownWings(brainDir)
if err != nil {
return nil, err
}
cls, err := classification.Load(brainDir)
if err != nil {
return nil, fmt.Errorf("load classification: %w", err)
}
return &Resolver{
Known: known,
Eligible: func(w string) bool { return cls.Wing(w) < classification.Confidential },
Classifier: NewLLMClassifier(complete),
Scorer: NewBM25Scorer(brainDir, known, ""),
Threshold: threshold,
}, nil
}