Skip to content

v13.14.4

Choose a tag to compare

@github-actions github-actions released this 12 Jun 14:31
· 272 commits to main since this release

Forty-eight commits since v13.14.3. Three load-bearing themes plus
infrastructure cleanup.

1. Phase BX close — editor polish + role-reframe row. The L2
editor's open cells from the 2026-05 cold-read land:

  • BX.3 Rail list table view (?view=table session toggle, grouped
    by source role with single-leg rails grouping by their leg_role
    too; toggle anchors strip embed=1 so a click from a home-section
    embed lands on the chromed standalone page, not the bare fragment).
  • BX.4 Read-card sections mirror the edit form's grouping (Identity
    / Classification / Topology / etc.) instead of one flat <dl>.
  • BX.9 Theme editor reorder — essentials (accent / secondary / logo)
    at top with a live-preview card and 300ms-debounced auto-save on
    blur; everything else collapses into <details>Advanced</details>,
    default-closed at DEFAULT_PRESET, default-open when an L2 has
    non-default values.
  • BX.14 Domain-flavor banking phrasing for every L2 validator error
    • [?] glossary triggers in the rejected-save banner, sourced from
      8 new GLOSSARY entries keyed by error code family.
  • BX.15 [?] chip tooltips next to Coverage + Trainer toggles on
    the diagram sidebar.
  • BX.16 Inline chain shape-preview below the children chip-list on
    chain edit pages (reuses the BX.8 mini-diagram wasm-graphviz
    renderer; fires on load so the initial paint pre-populates from
    the form's current parent value).
  • BX.17 Polish cluster — duration picker quick-select chips
    (Instant / 1h / EOD / Next-day + free-text), reference-panel
    default-open behavior on empty list pages only, completion-DSL
    autocomplete.

The persona audit confirmed BXa absorbed all live references; two
dead-code refs cleaned up (_VALID_KINDS retightened to
frozenset[EntityKind]; unreachable ("theme", "persona") ternary
removed).

2. Untyped-enum sweep — typed primitives for the five most-touched
enum surfaces.
Per a fresh audit
(docs/audits/untyped_enum_audit_2026_06_11.md), L2 primitives carried
five enums as bare str despite stable schema CHECKs (or no CHECK at
all). Sweep landed in staged commits for bisect-ability:

  • AmountDirection = Literal["Debit", "Credit"] (closed); ~12 spine
    writers + etl.write_transaction annotated.
  • POSTED_STATUS: Final = "Posted" (half-open per operator lock —
    "Posted" is canonical materialized state; other values stay
    integrator-extensible; no CHECK added).
  • Scope threaded through 4 spine generators that were dropping to
    bare str (chain_completion / failed_transaction / supersession /
    inv_fanout).
  • SupersedeReason annotated on _txn_row{,_tuple} and supersession
    writers (str | NoneSupersedeReason | None).
  • OriginORIGIN_INTERNAL_INITIATED, ORIGIN_EXTERNAL_FORCE_POSTED,
    ORIGIN_EXTERNAL_AGGREGATED Final constants (half-open like
    status; the survey also surfaced + fixed a typo bug in
    inv_fanout.py:222,241 that used "ExternalInitiated" — not in
    the canonical set per fuzzer + ETL walkthrough; corrected to
    ORIGIN_EXTERNAL_AGGREGATED. SPEC docs synced).

A new no-raw-enum-equality AST lint sits at the test boundary so
raw-string comparisons against the canonical values can't regress
(extended through the constructor-input sweep — 187 raw kind=/scope=/
origin= etc. kwargs across 25 test files now carry typed constants).

3. BX.new.list-cascade-reload HTMX-inheritance silent swap bug.
The biggest single fix this cycle. The standalone list page's
cascade-reload wrapper carried hx-select="#list-page-body", which
HTMX 1.9 inherits to all descendants by default. Every Delete / Edit /
save / toggle button inside the wrapper picked up the inherited
hx-select. When the Delete button fired its hx-get to /delete-confirm,
HTMX applied the inherited select against the countdown HTML response
— there is no #list-page-body in that response, so the swap silently
emptied the target. afterSwap fires successfully; the wrapper just
gets cleared with nothing in. ~3h of MutationObserver tracing to find.

Fix: hx-disinherit="*" on the cascade-reload wrapper. Documented as a
generic quirks-log entry in docs/reference/quicksight-quirks.md
this is exactly the silent-success failure mode the docs file exists to
catch.

Test infrastructure + audits. Five smaller landings:

  • CB.5 deletion + runner collapse — the two deprecated agreement
    test files (test_inv_dashboard_agreement.py + test_audit_dashboard_agreement.py)
    carrying "CB.5 follow-up deletes the file entirely" comments since
    2026-05 finally get deleted (1,835 lines), with a 13-file orphan
    reference sweep. The runner's qs_browser layer's two-invocation
    split (the agree_file Oracle-DDL-race carve-out) collapses to one.
  • BK.6 / #35LimitBreach and InboundCapBreach plants on cust1
    • cust2 (was cust1-only) so the L1 inverse-picker test has ≥2
      distinct account_id values. Calendar-drift footgun caught the next
      day: days_ago=8 sat at the boundary of a 7-day window and broke
      at the day rollover; clamped to 5 and filed as backlog for a
      bounded days_into_window type (see date_range_model_audit.md §10).
  • QS typeahead_filter — non-empty-query branch landed on
    QsEmbedDriver per the code comment that described the pattern;
    unblocks 4 dispatch-level skips. The picker tests themselves were
    already calling filter_options / pick_filter, so the actual
    skip reclaim is narrower than the triage claimed.
  • Three audit docs landed — untyped enum surface, no-raw-str-args
    lint blast radius (1,364 hits across the corpus; recommended
    Option A scoped to 39 enum-shaped names + treat as its own future
    phase), QS Browser skip triage (117 skips → 40 recoverable across
    8 buckets).
  • PG snapshotter pgcrypto FileLock refactor — already shipped in
    v13.14.2, now fully exercised; one corner-case stale-tab race
    caught + fixed.

Operator-facing UX rotation worth flagging. Delete behavior went
through three iterations this cycle in response to live dogfood:
top-of-page confirm banner → in-place button swap with countdown +
Cancel + reason tooltip → terser "In use" button text + dropped
Cancel + smaller tooltip → text-on-button only ("Delete" / "In Use"),
no countdown tooltip. The cumulative result is a Delete UX that
indicates state via button text + position rather than a separate
hover/popup affordance. Browser e2e tests cover all three render
paths.

Two clusters land together:

Phase CT — Oracle 19c plant flow fix (user-reported #197 /
CS.10.followup #328).
Two date-literal footguns where bare ISO-8601
strings were used as TIMESTAMP comparands. Oracle 19c rejects with
ORA-01843 ("not a valid month") — its NLS_TIMESTAMP_FORMAT default
doesn't auto-coerce that shape. PG + DuckDB happened to accept it via
implicit coercion, so the bug was Oracle-only and silently swallowed
inside the trainer Apply flow (the DELETE error'd out before reaching
the matview, leaving the plant a no-op).

Fix: use date_literal(iso, dialect) from common/sql/dialect.py
DATE 'YYYY-MM-DD' (portable across PG / DuckDB / Oracle).

Affected callsites:

  • common/l2/plant_registry.py::_invoke_balance_cadence_gap_plant
    — the user-reported trigger
  • common/l2/deploy_pipeline.py::_build_generator_sql (X.4.h trainer
    cutoff / scrub-head feature) — sibling bug, same shape

Verification: Oracle 19c trainer plant matrix went from 7/16 passed
to 8/16 passed (the formerly Oracle-only skip on
balance_cadence_gap is gone). Other 8 remain universally skipped
under BV.3.3.c.bug4-followup (chain-coherence dashboard rendering,
not Oracle-specific). Two new unit gates pin the typed DATE 'YYYY-MM-DD' shape across all three dialects so the regression
can't reappear.

Two CI-stabilization companions landed alongside CT.0:

  • CT.1App2Driver.pick_filter now peeks cur vs the resolved
    target BEFORE running the action; when equal, skip the 30s
    _wait_for_refetch (setValue is a no-op, no change fires, no
    refetch comes). Defensive against any picker test that hits the
    already-at-target case.
  • CT.2 — Narrow pytest.skip on the [app2] parametrize of
    test_inv_drilldown.py::test_account_network_table_walk_rerenders_table
    pending backlog #331 (App2 Anchor parameter pick fires change but
    no /visuals/*/data refetch on CI; [qs] variant continues to
    gate the same K.4.8 invariant on the production renderer).

Phase CS — 14 backlog items (CR follow-throughs + operator-visible
polish + Oracle/Studio bug bash). Highlights:

  • CS.1 — Removed dead SQLite plumbing surfaced by no-sqlite-prose
    lint (CB.8 cleanup). DB connection-leak gate renamed to
    RECON_GEN_DB_CONN_LEAK_GATE.
  • CS.2 — Re-lit test_inv_drilldown after CR.6.a; now uses
    drill_from_first_row_via_menu to match the production
    DATA_POINT_MENU trigger + row-content assertion (catches the
    K.4.8f-3 no-op shape that a count-based assertion would miss).
  • CS.4 — Sankey node-cap operator visibility: subtitles name the
    cap on Investigation + L2FT Sankeys; caps aligned at 50.
    Dynamic banner deferred to CS.4.followup #326.
  • CS.6 — Reordered L1 + L2 dashboards: Exceptions sheet promoted
    to position 2 (right after Getting Started) so operator's daily
    triage flow lives above lower-volume secondary surfaces. SheetIds
    unchanged so deep-link URLs still resolve.
  • CS.7 — L1 Exceptions copy: prose said 10 invariants; matview
    has 12 (post-CL.6 + AB-era chain-coherence). New
    L1_EXCEPTIONS_BRANCH_NAMES typed tuple is now the single source
    of truth; unit gate parses the matview SQL and asserts the
    bidirectional match so the count can't drift again.
  • CS.8 — Themed 503 page for PoolReleasedDuringRefresh (the
    CO.x DuckDB writer-lock fix's transient-state surface). Operator
    sees a calm "Data refresh in progress" with auto-reload meta
    refresh instead of an untemplated 500.
  • CS.9RECON_GEN_STUDIO_ETL_HOOK_TIMEOUT_SECS env knob with
    positive_int validator. Operators with slow upstream ETLs can
    raise the timeout cap without forking; subprocess termination
    returns rc=124 (GNU timeout convention) on timeout.
  • CS.11 — Pinned the Distinct Senders KPI binding shape; root-
    cause for the App2-side "renders as None" regression needs live
    env reproduction → deferred to CS.11.followup #327.
  • CS.12anomaly/money_trail app2 tests now derive the
    expected sender role from the L2 instance instead of hardcoding
    CustomerSubledger (which doesn't exist on sasquatch_pr).
  • CS.13 — Session Start probes for base schema before refresh;
    emits actionable remedy instead of silent no-op when missing.

Three CS items filed as backlog follow-ups (#326, #327, #328) for
the parts that couldn't be completed in-session.