Change: Blake (the registrar runtime) gains a fourth mode —
insight — that writes THE READ: the insight brief for one document, read with the
whole matter behind it. Agent half only. The broker seam (invoke, byte-verify, persist) is the
twin, pramaan-functions Bug-2320, and it merges after this.
pramaan-context-embed Lambda. That Lambda is a data
plane: it holds no OPENROUTER_API_KEY and its VPC has no egress to openrouter.ai —
so every staging brief failed brief_failed and the Insights tab said "Read pending"
forever, while CI stayed green (a unit test has no VPC). Ruling, Bug-2320:
agents think; lambdas move. A model homed in a Lambda is a P0 bug.| Before (Story-2169) | After (this story) | |
|---|---|---|
| The prompt, the model choice, the model call | context-embed Lambda (src/shared/context/insight_brief.py) |
Blake, on AgentCore — where OpenRouter is the model plane and the secret arrives via the pramaan-docs ADR-121 D2 bootstrap |
Span construction · V-CITE byte verification · ring law · drop-unverifiable-pins · the context.document_insight_briefs write |
the Lambda | the Lambda — unchanged. The guarantees stay with the data. |
That split is the whole design. Blake proposes LEAN pins — a page/block plus a
verbatim quote, a matter index, or a bare general_law label — and nothing else.
It cannot mint a SourceSpan, a span_id, a char offset or a
section_id. An unverifiable pin is only droppable by the broker if what
the model proposed was coordinates + bytes it can check: citations are data flow plus a
deterministic gate, never a citation agent (desk RFD §4).
| Piece | File | What it does |
|---|---|---|
| Typed I/O + the ring-law gate | agents/registrar/insight_models.py |
The feedstock (document card + anchored pages + the document's named sections + the broker's PRE-NUMBERED matter assertions) → flat gen DTO → the wire brief, rebuilt key-by-key. Every strip and drop is recorded on an InsightGate. |
| Prompt | agents/registrar/prompts/insight.py |
The retired seat's prompt carried over intact — same role, same JSON shape, same three-source ring law, same section-structured == SECTION == reading and 40,000-char anchored ceiling (truncation MARKED, never silent) — plus Blake's recursive injection fence and ambient output cell. prompt_version → document_insight_brief.v3. |
| Orchestration | agent.py::run_insight |
One bounded non-streaming attempt through Pydantic AI + PromptedOutput (Bug-1357-safe). NEVER raises: a producer error is a typed error result, not an exception that unwinds the enrichment chain. |
| Runtime seam | runtime.py mode "insight" → events.document_insight_event |
Parse-before-run.started (fail-closed), then model.usage → document.insight → run.completed; on a producer error, status='error' + run.failed. Never a silent truncation. |
| Pin kind | Carries | What the gate does |
|---|---|---|
this_document | page_number, block_index, a VERBATIM quote | An unquoted or page-less pin DROPS its item. The bytes are checked broker-side (V-CITE) — deliberately not re-checked here, so the two verifications can never disagree. |
matter | matter_ref (an index into the broker's numbered list) | An index outside the list DROPS its item. The numbering is the broker's — never renumbered or reordered in the answer. |
general_law | verify: true and nothing else |
Any record coordinate the model attaches is STRIPPED — the claim survives as the model's knowledge, labelled for the reader; the false pin dies. The law is never dressed as a fact in the record. |
{"schema": "agent_event_stream.v1", "type": "document.insight",
"run_id": "<meta.run_id>", // event-level, never inside the payload
"payload": {"document_id": "...", "status": "ok|needs_review|error",
"brief": { ...lean-pin draft... } | null,
"model_id": "...", "prompt_version": "document_insight_brief.v3"}}
Exactly those five keys: the broker's DocumentInsightPayload is
extra="forbid", so a sixth key would fail parse_event_line and store
nothing. Gate stats therefore ride the operator log (registrar.insight.gate), not the
wire. model_id + prompt_version are REQUIRED: the model call lives here
now, so this event is the brief's provenance stamp — the broker records it rather than
inferring it, and an unstamped brief is stored by nobody.
| Ambient seat, no cache writes (S-9) | Background, non-interactive enrichment: no prompt-cache creation is requested anywhere on this path, so no cache-write spend rides an ambient seat. Blake's existing ambient audit (tests/test_registrar_ambient_audit.py) greps the source for the marker and holds this. |
| Anchored-text ceiling | 40,000 chars (REGISTRAR_INSIGHT_MAX_ANCHORED_CHARS) — the card has already distilled the document. The truncation is marked in the feed, so the model knows where its sight ends. |
No new span names: the invoke rides agents.registrar.invoke and the model call
rides agents.registrar.context_extract, both already in the registry. Token/cost
metrics ride the existing model-call spans (_record_cost →
record_genai_token_usage_and_cost), which is now the ONLY place the brief's spend is
visible — the Lambda no longer pays for a model. meta.traceparent from the invoke is
the parent, so trace_id stays continuous Lambda → AgentCore.
| Test | Proves |
|---|---|
test_general_law_pin_never_carries_a_record_coordinate | the ring law holds even when the model dresses law as record |
test_blake_never_emits_a_span_id_section_id_or_char_offset | a model that smuggles a minted coordinate into its output still puts none on the wire |
test_matter_pin_index_must_exist_or_the_item_drops · test_this_document_pin_without_a_quote_drops_its_item · test_a_bad_extra_source_drops_only_itself_not_the_punch_card | the drop rules, item by item |
test_insight_mode_emits_the_document_insight_effect_and_completes | the mode is accepted and answers in the frozen event language with EXACTLY the five payload keys |
test_producer_failure_is_loud_never_a_silent_truncation · test_run_insight_never_raises_when_the_provider_is_absent | failure is loud and typed; the pass never unwinds its caller |
test_anchored_feed_is_section_structured_and_fenced · test_anchored_feed_marks_its_truncation_honestly | the document is fed as its NAMED PARTS, fenced as evidence, honestly bounded |
346 registrar tests + 179 governance tests green; ruff clean.
Registrar/agents deploys are MANUAL dispatch — registrar-deploy.yml
in pramaan-infra. Merging this PR does not put mode="insight" on the
staging runtime; the orchestrator runs that leg. Until it does, the functions twin (Bug-2320)
logs context_insight.brief_failed loudly — never a silent "Read pending".