symfonic.agent.preflight_l1¶
preflight_l1 ¶
v7.7.5 PRE-FLIGHT L1 imperative synthesiser.
Jarvio v7.5.1-L2-prose-vs-L1-imperative architectural ask. When
FrameworkConfig.procedural_render_preflight_in_l1=True, the engine
synthesises an imperative PRE-FLIGHT block from the active procedural
skills and injects it into the L1 CACHED prompt prefix alongside
adopter inject_contributions(position='cached') blocks.
Closes the v7.5.1 over-promise:
- CHANGELOG v7.5.1 + Guide 10 framed
PRE-FLIGHT:markers as a "hard ordering constraint" -- but the marker landed only in L2MEMORY_CONTEXT(volatile, NOT cached) where Anthropic models treat the text as descriptive prose rather than imperative. - Jarvio's 3-probe wire evidence: rule fully rendered 3x, 0
tool_useblocks across both turns -- the model never attempted the action, so the v7.7.4precondition_gatehad nothing to intercept. - The reference pattern that DOES work for Anthropic models is the
Jarvio
_JARVIO_FAILURE_HANDLINGdirective atjarvio_domain.py:112-122(their monorepo): a short, named section header in L1 with capitalised modals.
This module's :func:build_preflight_contribution mirrors that
pattern:
- Header:
## ENFORCED PRE-FLIGHT RULES(named section, matches the L0/L1 directive style adopters' models already follow). - Bullets:
- YOU MUST call \`{pre_tool}\` BEFORE calling \`{action_tool}\` (skill: {label}).One bullet per(skill, bare-id precondition)pair so multi-preconditionlist[str]shapes (v7.6.2) render as N bullets.
Only bare-identifier preconditions participate -- free-text rules
cannot be safely rewritten into YOU MUST call X without inventing
tool names. The bare-identifier discriminator is shared with the
v7.7.4 gate (_extract_bare_identifier in
symfonic.core.nodes.precondition_gate).
Default off: when the knob is False, the synthesiser short-circuits at
the first check and returns None. Zero new code paths fire when
opt-out, preserving v7.7.4 byte-for-byte cache parity.
build_preflight_contribution ¶
build_preflight_contribution(
skills: list[Any], state: Any | None = None
) -> PluginContribution | None
Synthesise the L1 PRE-FLIGHT PluginContribution for this turn.
Returns None when the snapshot has no enforceable bare-
identifier preconditions OR when the knob is off (caller responsible
for the knob check; this function trusts its input). When non-None,
the contribution lands at position="cached" with
order=5 (BEFORE adopter plugins' default order=100) so the
section sits immediately after the bundled L1 body and adopter
blocks render below it.
header="" because the rendered body already carries the
## ENFORCED PRE-FLIGHT RULES header -- the contribution
renderer would otherwise auto-prepend ### symfonic.procedural
on the first emission per (origin, position).
origin="symfonic.procedural" makes the contribution
grep-able in prompt audits and labels it as framework-emitted (vs
adopter-plugin-emitted).
v7.18.0 (Option A): state is forwarded to
:func:_build_preflight_body for state-predicate filtering.
state=None preserves the pre-v7.18 byte-identical contribution
shape for tests + direct callers that haven't yet plumbed state
through.