Calibration Quality Gate — Falsifiability Filter + Category Classification
Historical context. This is the source spec absorbed from PR #1191 into two waves of implementation:Preserved here per the hotfix plan’s PR #1191 close protocol so the production context (96K-page brain, 6.8% falsifiability rate, category breakdown) doesn’t get lost in the CHANGELOG → release-notes condensation.
- v0.37.2.0 hotfix (this release): widens the
takes_resolution_consistencyCHECK constraint to acceptquality='unresolvable'as a 4th valid state. Unblocks the production grading script. Addsunresolvable_count+unresolvable_ratetoTakesScorecardas sibling fields (preserves v0.36.1.0 historical comparison semantics). Migration renumbered v74→v79→v80 during successive master merges — v0.37.0.0’s autonomous-remediation wave claimed v68-v78, then v0.37.1.0 (brainstorm/lsd) claimed v79.- Follow-up minor (forthcoming): falsifiability + category extraction at
propose_takes, SQL-side grade gate, per-category calibration scorecards, pg_trgm-based proposal dedup. Wave-blocking on cat15 F1 re-validation against the v0.36.1.0 fixtures.
Problem
v0.36.1.0 shipspropose_takes, grade_takes, and calibration_profile as a
connected pipeline: extract claims → grade them against outcomes → build a
calibration profile showing systematic biases.
In production on a 96K-page brain with 36K takes across 6,239 holders, the
grade_takes phase produces noisy results:
- 6.8% falsifiability rate: Of 500 candidate takes (weight ≥ 0.7), only 34 passed an LLM falsifiability filter. The other 93% were philosophical beliefs, present-state observations, advice, logistics, or vague vibes.
- 50% unresolvable: Even after filtering, 17/34 predictions couldn’t be graded because evidence was insufficient or the claim was too ambiguous.
- Duplicates: Same claim from the same page extracted multiple times with slightly different wording.
propose_takes extracts everything that looks like a belief or
assertion. That’s correct for the takes table (epistemological layer), but
grade_takes needs a much narrower subset: falsifiable predictions about
future outcomes where we can check what actually happened.
Example classifications from production testing
Genuine predictions (grade-worthy):- “X will reach $1M ARR very soon” → company_outcome
- “X is going to leave Y” → people_move
- “AI will make authentic authorship more important” → technology
- “X was convinced Y would win the Z market” → market_call
- “Desire is mimetic” → philosophical belief
- “X should charge 10x more” → advice
- “Return from Toronto on Monday” → logistics
- “Something is going to happen there” → vague/unfalsifiable
- “X is growing very quickly” → present-state observation
Solution
1. Falsifiability score at extraction time
Add afalsifiability column to the takes table (real, 0.0–1.0, nullable,
default null). propose_takes sets this during extraction using the same LLM
call that already produces the take — one additional field in the JSON schema.
2. Grade gate in grade_takes
Before attempting grading, filter:
- LLM cost for grading drops proportionally
- Evidence retrieval load drops (each grade attempt triggers hybrid search)
- Calibration profiles are built on real predictions, not noise
3. Deduplication at extraction
propose_takes should check for near-duplicate claims before inserting:
pg_trgm extension (already available on most Postgres installations).
Falls back gracefully: if similarity() isn’t available, skip the dedup check.
4. Category-aware calibration profiles
Thecalibration_profile phase can now group resolved takes by
falsifiability_category to produce per-domain scorecards:
Schema Changes
Evidence Retrieval (v0.36.1.0 → v0.37 enhancement)
The currentgrade_takes evidence retriever returns a stub placeholder. In
production testing, we wired real evidence retrieval via modusbrain query (hybrid
search). The pattern that works:
- Extract the core claim from the take (first 150 chars)
- Run
engine.query(claim)to get relevant pages - Filter to pages updated AFTER the take’s
since_date(evidence must be newer) - Pass top-5 chunks as the evidence block to the judge
evidenceRetriever injection point.
Production Results
After implementing the falsifiability filter (as a pre-processing step outside the cycle):
Key improvement: the false positive rate dropped from 62% noise to 0% noise
in the gradeable set. The remaining 50% unresolvable rate is genuine — those
predictions are about outcomes that haven’t happened yet or where the brain
lacks evidence. That’s correct behavior, not noise.
Files to Change
src/core/cycle/propose-takes.ts— Add falsifiability + category to extraction prompt and output schemasrc/core/cycle/grade-takes.ts— Add falsifiability gate before grading; wire real evidence retrievalsrc/core/cycle/calibration-profile.ts— Group scorecards by categorysrc/core/engine.ts— Addsimilarity()helper for dedup (graceful fallback)- New migration — Add columns + index
Testing
- Unit test: falsifiability classifier on 20 known-good and 20 known-noise takes
- Unit test: dedup correctly merges near-identical claims
- Unit test: grade gate filters below threshold
- Integration test: full cycle with falsifiability → grade → profile pipeline
- Regression test: existing takes without falsifiability score are not broken (null falsifiability = ungated, backward compatible)
Backward Compatibility
falsifiabilitydefaults to null. Existing takes are unaffected.grade_takeswith null falsifiability: configurable behavior. Default: grade all (backward compat). Operator can setcycle.grade_takes.require_falsifiability: trueto gate.- Category column is purely additive.
- Dedup is opt-in:
cycle.propose_takes.dedup.enabled: true.