symfonic.core.learning.promotion¶
promotion ¶
v8.0 conversation→brand promotion + GDPR provenance (design §11/§12).
The 4-tier write model (§11.a) keeps conversation/working state ephemeral
(transient durability) and promotes only extracted facts upward, out of
band, during sleep consolidation. This module provides the PURE building
blocks the SleepConsolidator adopts:
- :func:
is_promotion_eligible— the §11.c trigger (durable AND confidence ≥ floor; the consolidation-phase gate is the caller's responsibility since promotion only ever runs inside the consolidator). - :func:
build_provenance/ :func:merge_provenance— the §12.a provenance shape carried on promoted nodes (source_conversation_idsARRAY etc.). - :func:
apply_erasure— the §12.b reference-counted deletion cascade: remove a conversation id from the provenance; an empty array marks the node for deletion (defaultdelete-on-empty-provenancePII policy), a surviving corroborating conversation keeps the fact.
These are deliberately side-effect-free so they are unit-testable in isolation and so wiring them into the consolidation hot path is a separate, reviewable integration step (the live SleepConsolidator is not rewired here).
apply_erasure ¶
apply_erasure(
provenance: dict[str, Any] | None,
erased_conversation_id: str,
*,
pii_policy: str = PII_POLICY_DELETE,
) -> tuple[dict[str, Any] | None, bool]
Apply a conversation-erasure to a promoted node's provenance (§12.b).
Returns (new_provenance, delete_node):
- Remove erased_conversation_id from the source arrays.
- If the array is now EMPTY: under the default
delete-on-empty-provenance policy the node is a deletion
candidate (delete_node=True) — no orphaned PII survives as a
"brand asset". Under retain-anonymised the node survives with an
empty provenance (adopter accepts responsibility).
- If other conversations still corroborate the fact: it SURVIVES with
the remaining provenance (reference-counted, NOT cascading-destructive).
Erasure operates on the SOURCE level (where it came from), never the query level (where it lives) — the §7 R3 mitigation, now locked.
Source code in src/symfonic/core/learning/promotion.py
build_provenance ¶
build_provenance(
*,
source_conversation_id: str,
source_scope_path: str,
promoted_by: str,
extraction_confidence: float,
promoted_at: str | None = None,
) -> dict[str, Any]
Build the §12.a provenance dict for a newly promoted node.
source_conversation_ids / source_scope_paths are ARRAYS from the
start (a durable brand fact is frequently corroborated by multiple
conversations over time; :func:merge_provenance appends to them).
Source code in src/symfonic/core/learning/promotion.py
extraction_confidence ¶
Read the extraction confidence off an entry's metadata.
Returns 0.0 when absent so an entry with no confidence signal never auto-promotes (conservative — design §11.c).
Source code in src/symfonic/core/learning/promotion.py
is_promotion_eligible ¶
is_promotion_eligible(
entry: Any,
*,
confidence_floor: float = DEFAULT_PROMOTION_CONFIDENCE_FLOOR,
) -> bool
Return True when a conversation-tier fact may promote to brand (§11.c).
ALL must hold
- Durability gate — the source is
durable(NOT transient/expired). Raw conversational state stays transient and never promotes; only extracted facts writtendurableare eligible. - Confidence — extraction confidence ≥
confidence_floor.
The third condition (runs only in the out-of-band consolidation phase, §11.c.3) is structural: this predicate is ONLY called from the consolidator, so it is satisfied by construction. Org promotion (§11.c) is never automatic and is intentionally NOT covered here.
Source code in src/symfonic/core/learning/promotion.py
merge_provenance ¶
merge_provenance(
existing: dict[str, Any] | None,
*,
source_conversation_id: str,
source_scope_path: str,
promoted_by: str,
extraction_confidence: float,
) -> dict[str, Any]
Append a corroborating conversation to an existing provenance (§12.a).
When the consolidation/janitor pipeline merges a duplicate fact, the
reinforcing conversation's id is appended (de-duplicated). The highest
observed confidence and the latest promoted_at win.