Commit d542399
authored
* feat(examples/migration_v5): MAKO reference extractor + tests (PR #156 first cut)
Add ``examples/migration_v5/reference_extractor.py`` —
the hand-authored reference for the notebook's Beat 3
cells and the ``bqaa-revalidate-extractors`` CLI. The
module exposes:
* ``EXTRACTORS`` — ``{"TOOL_COMPLETED":
extract_mako_decision_event}``. Only ``TOOL_COMPLETED``
events carry MAKO tool outputs; other event types fall
through to the AI fallback.
* ``RESOLVED_GRAPH`` — resolved from
``ontology.yaml`` + ``binding.yaml``. The harness
validates extractor output against it before
fingerprinting.
* ``SPEC`` — ``None``. MAKO extractors don't consume
``spec``; ``None`` matches the harness's keyword
default.
``extract_mako_decision_event`` dispatches on
``content.tool`` and produces the per-tool slice of the
MAKO graph:
* ``capture_context`` → ``ContextSnapshot`` node.
``snapshot_payload`` (dict) is JSON-serialized to a
string — MAKO declares ``snapshotPayload`` as
``xsd:string``; a raw dict would fail validation.
* ``propose_decision_point`` → ``DecisionPoint`` node.
* ``evaluate_candidate`` → ``Candidate`` node +
``evaluatesCandidate`` edge.
* ``commit_outcome`` → ``SelectionOutcome`` node +
``selectedCandidate`` edge. Spans with a ``rationale``
field are ``partially_handled`` (rationale is
trace-only, not declared on the MAKO entity).
* ``complete_execution`` → ``DecisionExecution`` node +
four hub edges (``executedAtDecisionPoint``,
``atContextSnapshot``, ``hasSelectionOutcome``,
``partOfSession``) + the envelope-side ``AgentSession``
node. The synthesis is what Beat 4.4's hub-shape
traversal needs — the agent's tools never return a
session payload, so the extractor has to manufacture
the ``AgentSession`` node + the ``partOfSession`` edge
from the plugin's envelope ``session_id``.
Node-ID encoding follows the binding's per-entity PK
columns from PR #155 (``decision_execution_id``,
``candidate_id``, etc.) so the materializer's
``parse_key_segment`` → FK column lookup resolves edges
cleanly.
Tests:
``tests/test_migration_v5_reference_extractor.py`` —
14 tests covering per-tool extraction, the module-level
surface contract, empty/missing-field fast paths, and
end-to-end validation. The decisive test
``test_full_decision_flow_validator_clean`` runs the
canonical 5-event flow through the extractor + the merge
helper and asserts the resulting ``ExtractedGraph``
passes ``validate_extracted_graph`` against the MAKO
``RESOLVED_GRAPH`` (6 nodes, 6 edges, ``report.ok``).
Next: notebook cells 3.3-3.5 + 3.7 + 4.4 wired up to use
this module.
* fix(examples/migration_v5): session-scope PK values + edge IDs + span/trace provenance
Three round-2 fixes for PR #157:
* **P1 — cross-session PK / edge_id collision.** The
agent's tools generate IDs via content-derived sha1
prefixes (``ctx-<10hex>`` / ``dp-<10hex>`` / etc.); two
sessions whose tools receive the same arguments produce
*identical* raw IDs (e.g. two ``capture_context`` calls
with the same ``audience_size`` + ``budget_remaining_usd``).
Before this fix:
- Materialized node tables carried duplicate PK values
across sessions. BigQuery doesn't enforce uniqueness
but ``CREATE PROPERTY GRAPH`` declares ``KEY (...)``
and the graph traversal assumes uniqueness; the demo
silently collapsed two sessions' decision flows.
- Edge IDs (``evaluatesCandidate:{dp_id}:{cand_id}``,
etc.) had the same collision because they used the
raw tool IDs.
Fix: new ``_scoped_id(session_id, raw_id)`` helper
produces ``"{session_id}:{raw_id}"`` and is applied to
every entity PK *value* + every edge_id. ``AgentSession``
is unchanged because its identity is already
``session_id``. Node_id key segments now encode the
scoped PK value, so the materializer's
``parse_key_segment`` → FK column lookup populates edge
tables with the matching scoped values.
* **P2 — DecisionExecution provenance properties.**
``DecisionExecution.spanId`` and
``DecisionExecution.traceId`` are MAKO-declared and
bound in ``binding.yaml`` (columns ``span_id`` /
``trace_id``), but the extractor wasn't emitting them.
These are the provenance link back to the plugin trace.
``_extract_complete_execution`` now takes ``trace_id``
alongside ``span_id`` from the event envelope and emits
both as properties when present. Sparse sources
(offline replay, synthetic fixtures) that omit the
envelope fields are handled gracefully — the extractor
omits the properties rather than emitting empty strings.
* **Tests** — three new tests, all passing alongside the
existing 14:
- ``test_two_sessions_have_unique_node_pk_values_and_edge_ids``:
runs two distinct decision flows that share raw tool
IDs, asserts every node's PK value is unique and
every ``edge_id`` is unique.
- ``test_complete_execution_carries_envelope_span_and_trace``:
asserts the plugin envelope's ``span_id`` /
``trace_id`` flow onto the ``DecisionExecution``
properties.
- ``test_complete_execution_omits_span_trace_when_missing``:
a missing envelope ``trace_id`` produces a node
without a ``trace_id`` property rather than an empty-
string one.
All 17 tests pass:
``PYTHONPATH=src python -m pytest
tests/test_migration_v5_reference_extractor.py``
1 parent 80f6c6f commit d542399
2 files changed
Lines changed: 1030 additions & 0 deletions
0 commit comments