refactor(evolve): collapse three scanners behind one runScanner core + collectFn seam#326
Merged
Merged
Conversation
…+ collectFn seam Replace the three hand-copied scanner pipelines (friction / preferences / dreaming) with one deep `runScanner` core fed by per-scanner `Taxonomy` adapters. The collect → batch → score → bucket scaffolding now lives once; each scanner contributes only its payload projection, default LLM scorer, and (unchanged) bucketing rule. - New `collect.ts`: extracted OrchestratorTurn + collectOrchestratorTurns + dbTurnIdToLine to a neutral module (breaks the run-scanner↔scan-friction cycle). - New `run-scanner.ts`: runScanner<P,T,Ctx> + Taxonomy + RunScannerOptions + SCAN_BATCH_SIZE, plus de-duplicated truncate/clamp/severityFor/severityRank. Adds a second injectable seam, collectFn (defaults to collectOrchestratorTurns), so the whole pipeline is testable without a DB. The core only touches the turn_id join key on T (ScoredRow constraint); all domain variation stays in bucket. - scan-*.ts: each is now a Taxonomy + a thin `scanX = (opts) => runScanner(...)` wrapper. Public names/signatures unchanged. Dreaming's richer bucketing (payload-in-first-pointer, separate note cap, recall→severity bump, promote/ reinforce verb) preserved whole; DreamEvidence threaded as the opaque ctx. - New run-scanner.test.ts: end-to-end pipeline coverage that the collectFn seam unlocks (cross-session swap, default+override batching, cross-batch merge, log-and-continue, per-category caps, dreaming reinforce-merge + payload round-trip). Pure internal refactor: no Signal shape change, no propose.ts/rank.ts change. Behavior verified byte-identical across all three buckets, prompts, and collect. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SeavKpZNAxkrokvKhQF8Ai
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Collapses the three hand-copied evolve scanner pipelines (
scan-friction,scan-preferences,scan-dreaming) into one deeprunScannercore fed by per-scannerTaxonomyadapters, and adds a second injectable seam (collectFn) so the full collect → batch → score → bucket pipeline is testable without a DB — closing the real test gap.Why
The three scanners shared the same four-stage shape with ~35–40 lines of identical scaffolding hand-copied into each file. The model seam (
scoreFn) already existed, but turn-collection hit Postgres directly, so no test drove the full pipeline — only the isolated bucket functions were covered.Changes
collect.ts(new) — extractedOrchestratorTurn,collectOrchestratorTurns,dbTurnIdToLineto a neutral module (breaks therun-scanner↔scan-frictioncycle).run-scanner.ts(new) —runScanner<P, T, Ctx>+ theTaxonomyinterface +RunScannerOptions+SCAN_BATCH_SIZE, plus de-duplicatedtruncate/clamp/severityFor/severityRank. The core only ever touches theturn_idjoin key onT(via aScoredRowconstraint); every domain distinction stays inside eachbucket.scan-*.ts— each is now aTaxonomy+ a thinscanX = (opts) => runScanner(taxonomy, opts, ctx)wrapper. Public function names/signatures unchanged. Dreaming's richer bucketing (payload-in-first-pointer, separate note-pointer cap, recall→severity bump, promote/reinforce verb) preserved whole;DreamEvidencethreaded through as the opaquectx.run-scanner.test.ts(new) — end-to-end pipeline tests the seam unlocks: cross-session swap, default-30 + override batching, cross-batch merge into one bucket, log-and-continue, per-category caps, dreaming reinforce-merge + payload round-trip + evidence threading.Scope
Pure internal refactor — no
Signalshape change, nopropose.ts/rank.tschange, public exports unchanged.Verification
tscclean; 140 tests pass (128 preserved verbatim + 12 new); daemonevolve-dreamingconsumer green; prettier clean.OrchestratorTurntest import, two thin-coverage batching gaps, one redundant assertion) are fixed in this branch.🤖 Generated with Claude Code
https://claude.ai/code/session_01SeavKpZNAxkrokvKhQF8Ai