recon_gen.apps.l2_flow_tracing.datasets
QuickSight DataSet builders for the L2 Flow Tracing app.
The Chains / L2 Exceptions tabs join L2-declared values (static, from
the L2 instance) to runtime activity (from the prefixed
<prefix>_current_transactions matview). The L2 declarations are
inlined into the SQL as a CTE of literal rows — no per-rail dataset
proliferation, no per-instance database table.
The Rails tab is a transactions explorer (M.3.10c rewrite — the M.3.5 declared-rails table moves to a future Docs tab). It uses two new datasets that participate in the metadata cascade:
l2ft-postings-ds: one row per leg, parameterized onpKey+pValuesso the metadata cascade filters it via QS<<$param>>substitution into a JSONPath.l2ft-meta-values-ds: distinct metadata values for the chosen key, parameterized onpKeyso the Value dropdown narrows when the Key dropdown changes.
Substep landmarks:
M.3.4 — skeleton (no datasets)
M.3.5 — Rails dataset (later DROPPED in M.3.10c — moves to Docs tab)
M.3.6 — Chains dataset
M.3.7 — L2 Exceptions datasets (six small KPI-backers)
M.3.8 — Auto metadata-driven filter dropdown sources (later DROPPED in M.3.10c — replaced by the cascade)
M.3.10c — Rails tab redesign on dataset parameters
Functions
|
Return every Dataset the L2 Flow Tracing app needs. |
|
One row per parent transfer firing of a declared chain parent (M.3.10d, completion_status extended in M.3.10i for XOR groups). |
|
One row per declared Chain-row child (Z.A: a multi-children row contributes N rows) — the L2's parent→child topology joined to runtime parent firing counts + matched-child counts. |
|
L2.1 — Required Chain entries where parent fired but no matched child fired in the window. |
|
L2.4 — Aggregating-rail bundles_activity targets that no posting matched in the window. |
|
L2.6 — LimitSchedule (parent_role, rail_name) cells with zero outbound debit flow in the window. |
|
L2.5 — Rail.metadata_keys declared in L2 that no posting carries a non-null value for in the window. |
|
L2.3 — Rails declared in L2 with zero postings in the window. |
|
L2.2 — Posted Transactions whose |
|
Long-form |
|
One row per leg from |
|
One row per shared Transfer that matches a declared TransferTemplate (M.3.10f, completion_status reshaped M.3.10j). |
|
One row per Sankey edge segment for a TransferTemplate firing (M.3.10f, chain-edge UNION added in M.3.10i). |
|
UNION ALL across the 6 L2 hygiene checks into one row-per- violation dataset (M.3.10l). |
Bounded enum of |
|
Bounded enum of chain-instances |
|
|
Sorted list of distinct Chain row parent names. |
|
Sorted list of distinct metadata keys declared across every rail in the L2 instance. |
|
Sorted list of declared Rail names. |
|
Sorted list of declared TransferTemplate names. |
|
Matviews + base tables the L2 Flow Tracing dashboard reads, paired with the date column for App Info's |
|
WHERE-fragment that filters by the metadata key/value cascade, portable across Postgres + Oracle + SQLite. |
Bounded enum of transaction |
|
Bounded enum of transfer-template |
- recon_gen.apps.l2_flow_tracing.datasets.build_all_l2_flow_tracing_datasets(cfg, l2_instance)[source]
Return every Dataset the L2 Flow Tracing app needs.
Mirrors build_all_l1_dashboard_datasets: derives an L2-aware
cfg(so dataset IDs carry the L2 instance prefix as their middle segment per M.2d.3) when the caller hasn’t pre-stamped it. Idempotent — re-deriving an already-L2-aware cfg is a no-op.M.3.6 ships Chains; M.3.7 adds the 6 L2 Exceptions sections; M.3.10c adds the postings explorer + meta-values cascade source for the Rails tab (replacing M.3.5’s declared-rails table — moves to a future Docs tab — and M.3.8’s 28 per-key metadata dropdowns — replaced by the cascade); M.3.10d swaps the chains aggregate dataset for a per-parent-firing explorer (chain-instances); M.3.10f adds the Transfer Templates sheet with tt-instances (per shared Transfer) + tt-legs (per leg, backing the multi-leg flow Sankey); M.3.10l replaces the 6 separate L2 exception datasets with one unified UNION-ALL dataset (mirrors L1’s exceptions pattern — single KPI + bar chart + detail table).
- Return type:
list[DataSet]- Parameters:
cfg (Config)
l2_instance (L2Instance)
- recon_gen.apps.l2_flow_tracing.datasets.build_chain_instances_dataset(cfg, l2_instance)[source]
One row per parent transfer firing of a declared chain parent (M.3.10d, completion_status extended in M.3.10i for XOR groups). Backs the Chains sheet’s per-instance explorer.
Columns:
parent_chain_name— the L2-declared parent rail / template name. Drives the Chain dropdown’s selectable values.parent_transfer_id— DISTINCT transfer_id of the parent firing. Multiple legs of one transfer collapse to one row via GROUP BY.completion_status— computed inline; one of:'Completed'— every Required child fired AND every XOR group has exactly one member fired.'Incomplete'— at least one Required child missing OR any XOR group has 0 fired (orphan) OR > 1 fired (violation).
The pre-X.1.j third branch
'No Required Children'is gone — validator rule C5 rejects all-optional / no-XOR chains at L2 load, so the SQL never produces that case.parent_metadatais read in the WHERE only — kept off the contract so users don’t see raw JSON.pKey/pValuessubstitute into a JSONPathIN (...)predicate same as the postings dataset; the__ALL__sentinel short-circuits to “no metadata filter”.
SQL portability: correlated subqueries (no
ARRAY_AGG); no JSONB;MAXaggregates over varchar status / metadata which isn’t perfect but the parent transfer’s legs share these values in practice. The chains table is bounded by L2 declarations (typically tens of entries) so the cost stays predictable.- Return type:
- Parameters:
cfg (Config)
l2_instance (L2Instance)
- recon_gen.apps.l2_flow_tracing.datasets.build_chains_dataset(cfg, l2_instance)[source]
One row per declared Chain-row child (Z.A: a multi-children row contributes N rows) — the L2’s parent→child topology joined to runtime parent firing counts + matched-child counts.
A row IS one Sankey edge in the Chains visual. Counts come from
<prefix>_current_transactionsmatched on the parent’s name (which can be a Rail’srail_nameOR a TransferTemplate’stemplate_name— every leg row carries both, with template_name taking precedence when a rail is part of a template). Child matches requiretransfer_parent_idto point at one of the parent’s transfer_ids — that’s the runtime “did this child fire in response to this parent” relation.Orphan rate = (parent_firings without a matched child) / parent_firings. A required Chain entry with non-zero orphan rate is the seed for M.3.7’s L2.1 ‘Chain orphans’ exception.
Note on portability: uses correlated subqueries instead of ARRAY_AGG (PG-only) to keep the SQL portable. The chains table is small (typically tens of entries), so the cost is bounded.
- Return type:
- Parameters:
cfg (Config)
l2_instance (L2Instance)
- recon_gen.apps.l2_flow_tracing.datasets.build_exc_chain_orphans_dataset(cfg, l2_instance)[source]
L2.1 — Required Chain entries where parent fired but no matched child fired in the window.
Reuses the chains dataset’s CTE shape (declared edges + edge runtime) and filters to
required = 'Required' AND orphan_count > 0. XOR-group multi/none violations are deferred to a follow-on substep — a precise XOR check needs per-Transfer-id grouping that the simpler aggregate doesn’t capture.AJ.4 (Gap I): a
fan_in(N:1) chain is singleton-children, so it’s labeledRequiredand would take the naiveparent_firing_count − child_firing_countpath — but N parents converging on far fewer shared child Transfers makes that difference large and positive on healthy batching, so every fan_in chain read as a pile of orphans. For fan_in edges the orphan count is instead the number of parent firings whosetransfer_idis NOT a parent of any child batch Transfer (the genuine “cycle closed but never assigned to a batch”), derived from the AB.4.3_transfer_parentsmatview. This is production-correct: fan_in is a real structural property of the L2, not a demo artifact.- Return type:
- Parameters:
cfg (Config)
l2_instance (L2Instance)
- recon_gen.apps.l2_flow_tracing.datasets.build_exc_dead_bundles_activity_dataset(cfg, l2_instance)[source]
L2.4 — Aggregating-rail bundles_activity targets that no posting matched in the window.
Per Z.B (2026-05-15): bundles_activity refs are Identifier rail names; the SQL checks
t.rail_name = db.bundle_target. Each row is one (aggregating_rail, target) pair the L2 declared but the runtime never realized.- Return type:
- Parameters:
cfg (Config)
l2_instance (L2Instance)
- recon_gen.apps.l2_flow_tracing.datasets.build_exc_dead_limit_schedules_dataset(cfg, l2_instance)[source]
L2.6 — LimitSchedule (parent_role, rail_name) cells with zero outbound debit flow in the window.
Means the cap is effectively dead — either nobody routes that role/type combination, or the L2 declared a cap nobody enforces against. NOT EXISTS over the prefixed transactions matview keeps the query bounded by the (small) limit-schedule count.
- Return type:
- Parameters:
cfg (Config)
l2_instance (L2Instance)
- recon_gen.apps.l2_flow_tracing.datasets.build_exc_dead_metadata_dataset(cfg, l2_instance)[source]
L2.5 — Rail.metadata_keys declared in L2 that no posting carries a non-null value for in the window.
Each (rail, metadata_key) pair gets its own SQL fragment in the UNION ALL. Static JSON paths sidestep PostgreSQL’s reluctance to accept dynamic JSONPath arguments to
JSON_VALUE— keeps the SQL portable per the project’s no-JSONB constraint.- Return type:
- Parameters:
cfg (Config)
l2_instance (L2Instance)
- recon_gen.apps.l2_flow_tracing.datasets.build_exc_dead_rails_dataset(cfg, l2_instance)[source]
L2.3 — Rails declared in L2 with zero postings in the window.
Same shape as the Rails dataset but pre-filtered to
COALESCE(r.total_postings, 0) = 0. The KPI shows the count; the detail table lists the dead rails so the integrator can decide whether to retire the declaration or fix the ETL.- Return type:
- Parameters:
cfg (Config)
l2_instance (L2Instance)
- recon_gen.apps.l2_flow_tracing.datasets.build_exc_unmatched_rail_name_dataset(cfg, l2_instance)[source]
L2.2 — Posted Transactions whose
rail_namedoesn’t match any declaredRail.rail_name.The runtime version of M.2d.1’s deferred validator check (‘every Transfer MUST match a Rail’). LEFT JOIN to a CTE of declared types + filter to NULL surfaces the unmatched rows. Output is per-transfer-type with a count of postings carrying that type — the table reveals what’s leaking past the L2’s rails.
- Return type:
- Parameters:
cfg (Config)
l2_instance (L2Instance)
- recon_gen.apps.l2_flow_tracing.datasets.build_meta_values_dataset(cfg, l2_instance)[source]
Long-form
(metadata_key, metadata_value)for the cascade.Built as a UNION ALL across declared metadata keys, projecting one row per (transaction, key) combination where that key has a non-null value. The Value dropdown’s
LinkedValuessources from themetadata_valuecolumn; QS’sCascadingControlConfigurationfilters rows by the Key dropdown’s selection matched against themetadata_keycolumn — picking ‘customer_id’ narrows the dataset to that key’s rows, then DISTINCT metadata_value populates the dropdown.No dataset parameters needed — the cascade is purely column-match on the analysis side. The earlier single-column + parameter-substituted shape didn’t work because QS’s cascade is a column-match filter, not a parameter-driven dataset re-query (M.3.10c finding).
For an L2 instance with no declared metadata keys, the SELECT is replaced with WHERE FALSE so the dataset emits valid SQL that returns no rows.
- Return type:
- Parameters:
cfg (Config)
l2_instance (L2Instance)
- recon_gen.apps.l2_flow_tracing.datasets.build_postings_dataset(cfg, l2_instance)[source]
One row per leg from
<prefix>_current_transactions, parameterized so the Rails sheet’s filters push down into SQL via QS<<$param>>substitution.Two parameter families, both server-side:
Metadata cascade —
pKey(single) +pValues(single).pKey = '__ALL__'short-circuits the metadata WHERE to “no filter” (freshly-loaded dashboard renders every leg);pValues = '__placeholder__'matches no real value, so picking a Key without a Value goes empty (UX hint to pick both). Stays in the inner query’s WHERE.Category pushdown (Y.2.c + AA.A.3) —
pL2ftRail/pL2ftStatus/pL2ftBundle(all SINGLE_VALUED post-AA.A.3; was MULTI_VALUED pre-flip). Defaults to the L2FT_ALL_SENTINEL so a freshly-loaded dashboard matches every row via the sentinel disjunct in_match_all_in_clause. Bridged from the Rails sheet’s SINGLE_SELECT dropdowns; clearing a dropdown reverts to the sentinel default. Pushed into the OUTER WHERE becausestatusandbundle_statusare CASE-aliases (not visible to a WHERE in the same SELECT) so the projection wraps in a subquery.
Phase BM — date narrowing pushes down via
<<$pL2ftDateStart>>/<<$pL2ftDateEnd>>(replaces the pre-BM per-sheetTimeRangeFilteron QS + no-narrow on App2). One SQL form across both renderers.- Return type:
- Parameters:
cfg (Config)
l2_instance (L2Instance)
- recon_gen.apps.l2_flow_tracing.datasets.build_tt_instances_dataset(cfg, l2_instance)[source]
One row per shared Transfer that matches a declared TransferTemplate (M.3.10f, completion_status reshaped M.3.10j).
A “shared Transfer” is one
transfer_idfrom<prefix>_current_transactionswhose legs all carry the sametemplate_namematching a declared template. Per SPEC: every firing of aleg_railsrail with the sametransfer_keyMetadata values posts to the same shared Transfer, so the transfer_id distinct-count = number of TransferTemplate instances.completion_statusis one of:‘Imbalanced’ —
ABS(actual_net - expected_net) >= 0.01(L1 Conservation break).‘Orphaned’ — balanced, but a Required chain child didn’t fire OR an XOR group has 0 or > 1 fired members (L2 chain break).
‘Complete’ — balanced AND every Required child fired AND every XOR group has exactly one fired member.
Mirrors the chain-instances completion_status semantics so the analyst sees consistent language across both sheets.
Parameterized on pKey + pValues for the metadata cascade.
- Return type:
- Parameters:
cfg (Config)
l2_instance (L2Instance)
- recon_gen.apps.l2_flow_tracing.datasets.build_tt_legs_dataset(cfg, l2_instance)[source]
One row per Sankey edge segment for a TransferTemplate firing (M.3.10f, chain-edge UNION added in M.3.10i).
The query UNIONs three row-sources:
Template legs (
edge_kind='template_leg') — actual transactions wheretemplate_namematches a declared TransferTemplate.flow_source/flow_targetderive fromamount_directionso the Sankey reads asdebit account → template_name → credit account.Matched chain children (
edge_kind='chain_matched') — actual transactions whosetransfer_parent_idpoints at a template firing’s transfer_id AND whose rail_name matches a declared chain child.flow_source = template_name,flow_target = child_rail_name.Orphan chain children (
edge_kind='chain_orphan') — SYNTHETIC rows for declared (template, child) chain edges that the runtime didn’t fire. One row per (parent firing, declared child) where no matching child transaction exists.flow_source = template_name,flow_target = child_rail_name || ' (orphan)'so the analyst sees the missing edge visualized as a thin dashed-style ribbon to a distinct node.
Together this gives a complete Sankey per parent firing — every declared chain edge is visible whether it fired or not, with the template legs forming the central trunk.
Width = ABS(amount_money). Synthetic orphan rows carry the parent firing’s amount as a representative ribbon thickness so they’re visually present but recognizable.
Joining against the declared-templates CTE filters out any rogue
template_namevalue in current_transactions that isn’t in the L2 declaration (mirrors tt-instances).Parameterized on pKey + pValues for the metadata cascade.
- Return type:
- Parameters:
cfg (Config)
l2_instance (L2Instance)
- recon_gen.apps.l2_flow_tracing.datasets.build_unified_l2_exceptions_dataset(cfg, l2_instance)[source]
UNION ALL across the 6 L2 hygiene checks into one row-per- violation dataset (M.3.10l).
Mirrors L1’s l1_exceptions pattern: one row = one violation; check_type is the discriminator; count is the per-violation row count used for sort + bar stacking. Each branch inlines its own CTEs as a subquery so the outer SELECT can do consistent typing across branches without colliding CTE names.
Each branch is functionally equivalent to one of the 6 retired build_exc_* queries, just projected to the unified shape via CASTs + literal check_type labels.
- Return type:
- Parameters:
cfg (Config)
l2_instance (L2Instance)
- recon_gen.apps.l2_flow_tracing.datasets.bundle_status_values()[source]
Bounded enum of
bundle_statuscalc-field values. Static dropdown source — see_BUNDLE_STATUS_VALUESfor rationale.- Return type:
list[str]
- recon_gen.apps.l2_flow_tracing.datasets.chain_completion_status_values()[source]
Bounded enum of chain-instances
completion_statusvalues. Static dropdown source on the Chains sheet (X.1.g).- Return type:
list[str]
- recon_gen.apps.l2_flow_tracing.datasets.declared_chain_parents(l2_instance)[source]
Sorted list of distinct Chain row parent names. Drives the Chain dropdown’s selectable values on the Chains sheet (M.3.10d).
- Return type:
list[str]- Parameters:
l2_instance (L2Instance)
- recon_gen.apps.l2_flow_tracing.datasets.declared_metadata_keys(l2_instance)[source]
Sorted list of distinct metadata keys declared across every rail in the L2 instance. Drives both the dropdown-source dataset list and the analysis-level parameter list — single source of truth for “what metadata keys does this L2 expose?”.
- Return type:
list[str]- Parameters:
l2_instance (L2Instance)
- recon_gen.apps.l2_flow_tracing.datasets.declared_rail_names(l2_instance)[source]
Sorted list of declared Rail names. Drives the Rail dropdown’s selectable values on the L2FT Rails sheet (X.1.b).
Pre-X.1.b the Rail dropdown carried no selectable_values and relied on QS’s auto-distinct fetch (
tenK-sample-values-V2endpoint), which 404s on cold per-CI-run dashboards. Static enumeration from the L2 sidesteps the lazy fetch entirely — rail names are bounded + known at deploy time.- Return type:
list[str]- Parameters:
l2_instance (L2Instance)
- recon_gen.apps.l2_flow_tracing.datasets.declared_template_names(l2_instance)[source]
Sorted list of declared TransferTemplate names. Drives the Template dropdown’s selectable values on the Transfer Templates sheet (M.3.10f).
- Return type:
list[str]- Parameters:
l2_instance (L2Instance)
- recon_gen.apps.l2_flow_tracing.datasets.l2ft_matview_specs(cfg)[source]
Matviews + base tables the L2 Flow Tracing dashboard reads, paired with the date column for App Info’s
latest_dateKPI.Includes the base tables (transactions / daily_balances) so the operator can spot stale matviews against fresh ETL loads at a glance. Mirrors
l1_matview_specs/inv_matview_specs.- Return type:
list[tuple[str,str|None]]- Parameters:
cfg (Config)
- recon_gen.apps.l2_flow_tracing.datasets.metadata_filter_clause(l2_instance, metadata_col, dialect)[source]
WHERE-fragment that filters by the metadata key/value cascade, portable across Postgres + Oracle + SQLite.
The natural form
JSON_VALUE(metadata, '$.' || <<$pKey>>) IN (...)works on Postgres but fails on Oracle: Oracle’sJSON_VALUErequires the path argument to be a string literal at parse time (ORA-40597 — JSON path expression syntax error). The runtime concatenation'$.' || pKeyis rejected even when the planner could constant-fold it.Workaround: emit one branch per declared metadata key with the JSON path as a literal, gated by
<<$pKey>>matching that key. The L2 instance enumerates the keys at generate time, so the fan-out is bounded and static. Sentinel__ALL__short-circuits the cascade so a freshly-loaded dashboard renders all rows.metadata_colis the column the JSON_VALUE reads from (typicallymetadataorparent_metadata).dialectroutes throughjson_valueso SQLite getsjson_extract(the JSON1 equivalent) instead of the SQL/JSON-standard form.- Return type:
str- Parameters:
l2_instance (L2Instance)
metadata_col (str)
dialect (Dialect)