Knowledge-Policy Scoring Pipeline (Pre-Visibility)
End-to-end architecture of NornicDB's knowledge-policy scoring stage, from a read-path entry point through the compiled binding lookup, decay/promotion math, and suppression decision — right up to the hand-off to the Visibility Layer.
This document diagrams the code as it exists in the repository. Every stage in the flowchart maps to a specific file and function in pkg/knowledgepolicy/ and pkg/storage/badger_decay_filter.go. See the Legend below for line-level references.
For the user-facing policy DDL and semantics, see Knowledge-Layer Policies. For what happens after an entity is marked suppressed, see Visibility Suppression and Deindex.
Full Pipeline
Legend — Source References
Every box in the diagram corresponds to a real function in the codebase.
| Diagram box | Source |
|---|---|
filterNodeByDecay / filterEdgeByDecay | pkg/storage/badger_decay_filter.go:56, 107 |
revealAll gate & BeginQueryRevealScope | pkg/storage/badger_decay_filter.go:30, 37 |
Resolver.ResolveNode label-subset walk & conflict resolution | pkg/knowledgepolicy/resolver.go:23, 97, 130 |
BindingTable (wildNode/wildEdge, sorted-label key) | pkg/knowledgepolicy/compiled_binding.go:39 |
CompiledBinding (flattened bundle + binding + promotion) | pkg/knowledgepolicy/compiled_binding.go:21 |
Scorer.score (main scoring path) | pkg/knowledgepolicy/scorer.go:187 |
resolveAnchor (CREATED / VERSION / LAST_ACCESSED / CUSTOM) | pkg/knowledgepolicy/scorer.go:316 |
computeDecay (inverse-in-place via negative half-life) | pkg/knowledgepolicy/scorer.go:355 |
selectPromotionProfile (WHEN predicate eval) | pkg/knowledgepolicy/scorer.go:290 |
computeFinalScore (multiplier + floors + cap) | pkg/knowledgepolicy/scorer.go:402 |
SuppressionEligible = FinalScore < VisibilityThreshold && !HasNoDecayProperty | pkg/knowledgepolicy/scorer.go:271 |
Suppression reason classification (score_floor vs below_threshold) | pkg/knowledgepolicy/scorer.go:277–285 |
AccessMetaEntry (Fixed / Overflow / KalmanFilters) | pkg/knowledgepolicy/access_meta.go:11 |
AccessAccumulator (P-local sharded) | pkg/knowledgepolicy/access_accumulator.go:24 |
AccessFlusher.applyOnAccessMutations | pkg/knowledgepolicy/on_access_runtime.go:9 |
ProcessKalmanMutation (velocity + gain) | pkg/knowledgepolicy/kalman_accumulator.go:9 |
SuppressionRecheckFunc / EmbedInvalidateFunc feedback | pkg/knowledgepolicy/access_flusher.go:42, 39 |
Pipeline Stages (Pre-Visibility)
- Query entry. The read path in
BadgerEnginecallsfilterNodeByDecay/filterEdgeByDecay. Areveal()scope short-circuits scoring for that query only. - Scorer selection. The namespace extracted from the entity ID picks a per-tenant
Scorer. If none exists, the explicitVisibilitySuppressedflag is honored (and counted asexplicit_flagin metrics). - Resolver. Sorted-label subset walk against
BindingTable; ties are resolved by lowestDecayBinding.Order; wildcard fallback for unmatched labels or edge types. - CompiledBinding. Pre-flattened bundle + binding overrides + promotion policy + property rules + compiled WHEN predicates.
NoDecayshort-circuits to a neutral score of1.0withNoDecay=true; the read filter still honors an explicitVisibilitySuppressedflag for those entities. - Anchor + age.
resolveAnchorpicks the start time (CREATED / VERSION / LAST_ACCESSED / CUSTOM). Age isnow − anchor, clamped to zero. - Base decay. Exponential, linear, step, or none. A negative
HalfLifeNanosinverts the curve in place (1 − f) — this is how idle-time consolidation is implemented, without adding new function/type/flag values to the schema. - Promotion. WHEN predicates are evaluated over
AccessMetaEntry+ entity properties; the profile with the highestMultiplier(thenScoreFloor) wins. - Final score.
base × multiplier, clamped by[promoFloor, promoCap], then floored byDecayFloor. - Suppression decision.
FinalScore < VisibilityThreshold && !HasNoDecayPropertysetsSuppressionEligible=true; the scorer classifies the reason asscore_floororbelow_threshold, and the read filter drops the entity from the result set. - Access feedback (out of band). Surviving entities feed the
AccessAccumulator, drained byAccessFlusher, which appliesON ACCESSmutations (optionally through the Kalman filter), merges fixed-field deltas, re-evaluates property suppression, optionally callsEmbedInvalidatewhen property suppression state changes, persistsAccessMetaEntry, and finally triggersSuppressionRecheck.
Next Stage
Everything downstream of SuppressionEligible now has its own architecture page: Knowledge-Policy Visibility Layer and Deindex.
That follow-on document covers:
- query-time
reveal()bypass behavior - reveal-scope locking with
BeginQueryRevealScope - persisted suppression flags and deindex work items
- tombstone-based hiding of BM25 and vector-backed index entries
- retention sweep behavior once suppressed entities remain in storage long enough
See Also
Go consumers read metadata through ReadAccessMeta. Replace GetAccessMeta(id) with ReadAccessMeta(knowledgepolicy.AccessMetaReadRequest{EntityID: id}) and use the result's Entry. Replace ScanAccessMeta() with ReadAccessMeta(knowledgepolicy.AccessMetaReadRequest{Mode: knowledgepolicy.AccessMetaReadAll}) and use Entries. Both return the storage error separately. Point reads preserve work and tombstone entries; all reads enumerate entity access state in one captured transaction and skip internal work keys.
- Knowledge-Layer Policies — DDL, catalog objects, and end-to-end examples
- Decay Profiles — half-life, thresholds, floors, and the four decay functions
- Promotion Policies —
ON ACCESS,WHENpredicates, Kalman filtering - Knowledge Policy Tuning and Testing — end-to-end tuning workflow
- Knowledge Policy Metrics — the counters and histograms emitted at each stage above
- Knowledge-Policy Visibility Layer and Deindex — downstream read filtering, reveal scopes, deindex cleanup, and retention hand-off
- Visibility Suppression and Deindex — what happens after
SuppressionEligible=true