CE-14 (Story-2072) put the pinned context_bundle.v1 on the broker's
wire, for the Mira command/converse rail. CE-21 does the same for the other
matter-evidence consumer — Vega, the frame builder — but through a
different door: the framer_trigger worker that fires on
matter.created and assembles Vega's stateless FramerInput.
The bundle rides strictly additively; the raw documents
field is retained exactly as today (RFD docs/notes/rfd-context-engine.md
§10 consumers table, §14.3 row CE-21, §5.4).
documents stays
framer_trigger fires on matter.created — before any
card, chunk, or graph node can exist. A bundle fetched at that instant is
empty by construction. So the raw documents field is retained
indefinitely as the first-frame grounding, and the bundle is additive
enrichment for later frames. Any future retirement of
FramerInput.documents is gated on a parity eval that includes the
first-frame case (§10; FramerInput's versioning stays extend-only per pramaan-docs
ADR-153). CE-21 changes nothing about the first frame — it only lets a warm matter's
later reframes see the assembled bundle.
| Surface | Change |
|---|---|
src/workers/framer_trigger/processor.py — attach_context_bundle() (new) |
A cache READ, never a build. Reads the pinned
context.bundles row at Vega's axes — brief × L, the
widest recipe at the biggest tier (the framer edits the whole brief) — and, on a
hit, stamps framer_input["context_bundle"] = bundle.model_dump(...).
Runs in its OWN session with its own try: a miss or error is
enrichment, never a run-breaker, and never poisons the already-committed claim
transaction. Mirrors CE-14's prefetch_context_bundle fail-open
doctrine. |
src/workers/framer_trigger/processor.py — process_matter_created() |
One call inserted after the claim + run row COMMIT, before the payload
leaves: await attach_context_bundle(deps, firm_id=…, matter_id=…,
framer_input=claimed.framer_input). The raw documents
assembly in _build_framer_input is untouched. |
FRAMER_BUNDLE_PROFILE = "brief", FRAMER_BUNDLE_BUDGET = "L" |
Vega's pinned-bundle axes, named as constants (RFD §14.3 CE-21
get_bundle(brief, L)). |
documentsprocess_matter_created
└─ _claim_and_mint_run # atomic lease claim + agent_run row (COMMITS)
└─ attach_context_bundle # ← CE-21: own session, cache read, fail-open
└─ framer_input["context_bundle"] = bundle.model_dump() # only on a HIT
└─ build_vega_invoke_payload # input carries documents (always) + bundle (if warm)
└─ invoke_agent(payload)
The bundle read is deliberately its own session, separate from the claim transaction:
the claim + run row are committed before the enrichment runs, so a
bundle-store error can never roll back a minted run. This is the same fail-open
ordering the CE-14 broker uses when it stamps context_bundle beside the
Memory Pack.
The acceptance criterion is that the first frame is byte-for-byte what it was before
CE-21. Three unit tests
(tests/unit/test_framer_trigger_processor.py) lock it:
read_cached_bundle returns None → no
context_bundle key is added → Vega's input is exactly
{title, matter_type, documents, previous_frame, locked_fields}, as
today. (The pre-existing test_claim_win_commits_run_row_before_vega_invoke
still asserts that exact shape — it stays green.)profile='brief', budget='L') attaches
context_bundle beside documents — nothing else
in the input changes.context_bundle — the run is
never broken by enrichment.
The pramaan-agents FramerInput mirror is unchanged this story: the
functions side builds a plain dict and adds one optional key, forward-compatible with
the Vega schema gaining context_bundle when its own story lands (additive
only). No migration — the bundle is read from the existing
context.bundles store (CE-13). Building on a cache miss
is not done here — that is the debounced rebuild worker's job (§9); the trigger only
reads, keeping the invoke path DR-1-bounded. Frame provenance
(CE-23/CE-24) sits after CE-21 and is out of scope. Retiring
documents is gated on the first-frame parity eval (§10) and is
explicitly not attempted.
uv run ruff check
uv run pytest tests/unit/test_framer_trigger_processor.py \
tests/unit/test_framer_trigger_handler.py \
tests/unit/agent_contracts/test_golden_parity.py # frame parity green
make openapi # app/admin snapshots unchanged (FramerInput is not an HTTP DTO)
Recall: CE-21 attaches the pinned context_bundle.v1
(brief × L) onto Vega's FramerInput in a fail-open, own-session read after
the claim commits — strictly additive beside the raw documents that stays
as the permanent first-frame grounding, so an empty first frame produces today's
behaviour unchanged.