symfonic.capabilities.memory.phases.drafts¶
drafts ¶
ProceduralExtractor protocol + ProceduralDraft dataclass.
Moved here from symfonic.core.learning.procedural_extractor, which now
imports it back. This is the seam phase 12 consumes -- what a deployment
hands it, and what it writes -- and the phase is on a roster the kernel
composes. The concrete model-backed adapter stays in the legacy package:
it is an integration, and nothing on a roster imports it.
v7.6 (adopter Ask 9) — opt-in surface for the LLM-backed Phase 12 extractor. Mirrors the v7.2 EntityExtractor pluggability shape:
- the regex-based extractor in
:mod:
symfonic.core.learning.phases_procedural_promotionis the default (configured at v6.1) and produces flat verb-object skills; - the LLM-backed extractor in
:mod:
symfonic.core.learning.procedural_extractor_llmis opt-in viaFrameworkConfig.phase_12_use_llm_extractor=Trueand produces structured :class:ProceduralDraftrecords that distinguish action / preflight / recovery / guardrail rules.
The protocol is intentionally minimal -- the dispatch site in
:func:promote_episodic_to_procedural only needs to call extract
and turn the returned drafts into :class:~symfonic.memory.models.entry.MemoryEntry
records via the shared :func:_build_skill_entry_from_draft helper.
Design reference:
.claude/docs/2026-05-28-adopter-llm-procedural-extractor-eval.md §8.
ProceduralKind
module-attribute
¶
Allowed values for ProceduralDraft.kind.
The four-way split tracks the failure modes adopter identified in their
deep-dive (symfonic-core-ask-llm-procedural-extractor.md):
action— repeated tool usage that should become a learned habit.preflight— pre-flight check that prevents downstream failure ("before X, call Y"). The most load-bearing kind for v7.6 since the regex extractor structurally cannot represent it.recovery— error-recovery rule ("after E, call R").guardrail— negative rule ("never X — use Y instead").
VALID_PROCEDURAL_KINDS
module-attribute
¶
VALID_PROCEDURAL_KINDS: frozenset[str] = frozenset({'action', 'preflight', 'recovery', 'guardrail'})
Runtime mirror of :data:ProceduralKind for response-shape validation.
The Literal type is gone at runtime so the LLM-response parser uses this
frozenset to clamp unknown kinds back to "action" (the most benign
default — a noisy action draft surfaces to human review, a fabricated
preflight rule could hide a real precondition gap).
ProceduralDraft
dataclass
¶
ProceduralDraft(kind: ProceduralKind, trigger: str, action: str, rationale: str, confidence: float = 0.0, precondition: str | list[str] | None = None, source_episode_ids: list[str] = list())
Structured draft produced by a :class:ProceduralExtractor.
Frozen so the dispatch site can pass instances around without
worrying about downstream mutation. Defaults match the LLM prompt's
nullable / optional fields so a minimal response can still
construct a valid draft (only kind, trigger, action,
and rationale are strictly required).
precondition shape (v7.6.3 -- adopter multi-precondition ask):
None-- no pre-flight gate.str-- single pre-flight rule (v7.6.0 byte-identical shape; preserved so consumers thatisinstance(p, str)still match the common case).list[str]-- ordered multi-pre-flight rule. Mirrors the v7.6.2 storage widening on :meth:~symfonic.memory.layers.procedural.layer.ProceduralLayer.seed_authored_skill, so LLM-extracted drafts and hand-authored skills share one storage shape. The v7.5.1 :class:~symfonic.memory.layers.procedural.router.CapabilityRouterrenders one indentedPRE-FLIGHT:line per entry in supplied order.
ProceduralExtractor ¶
Bases: Protocol
Pluggable Phase 12 extractor interface.
Implementations MUST be best-effort: a malformed response, a missing
field, or an exception during model invocation must yield []
rather than raise. The dispatch site logs the empty result via the
standard Phase 12 telemetry line so consumers can tell "extractor
found nothing" from "extractor crashed" via the cost_usd /
llm_calls fields.
draft_to_memory_entry ¶
Convert a :class:ProceduralDraft to a procedural MemoryEntry.
Moved here with the draft it reads. Phase 12's two extractors -- the regex
one and the model-backed one -- write through one store_skill contract,
and the phase is on a roster the kernel composes, so the conversion cannot
live in the legacy package the concrete LLM adapter stays in.
Used by the Phase 12 dispatch site in
:func:promote_episodic_to_procedural so the LLM and regex paths
share the same store_skill write contract. The draft's
structured fields land in the entry metadata under stable keys so
the v7.5 query_skills projection surfaces them:
precondition->properties['precondition'](v7.5)source->"phase_12_llm_extractor"(distinct from the regex extractor's"phase_12_promotion"so SRE telemetry can split adoption).kind/confidence/rationale/trigger/source_episode_idsare first-class properties for v7.7+ router enrichment.
The label field follows the v6.1 "Auto-learned: <name>"
convention so downstream label-prefix grouping (e.g. SemanticMerge
similarity, /procedures listing UI) keeps working unchanged.