v13.15.0
Three operator-dogfood bug-fix themes plus a substantive new feature
(Phase DA) collected into one release. Minor bump for the new App2
decoration feature (App2's renderer now paints drillable-column visuals
in parity with the QuickSight side).
1. Phase DA — App2/QS click-drill decoration parity + type-system gate.
Operator dogfood (2026-06-12, L1 Overdraft sheet) surfaced two coupled
defects in the click-drill decoration story:
- App2's
renderTableignoredconditional_formattingentirely, so
every drillable column looked identical to non-drillable columns —
operator couldn't tell which cells carried drills until they hovered. - Every L1 app site misused
CellAccentText(the LEFT-click cue) for
DATA_POINT_MENUdrills. Zero usage ofCellAccentMenuanywhere.
Even on the QS side the column showed plain accent text while the
actual drill was menu-only — analyst expectation vs reality drift.
The audit at docs/audits/da_0_clickability_audit.md found 15 mutations
across 12 Tables in 4 apps. Phase DA landed:
- Collapsed two-type CellFormat design to one
Drillable(on=Dim, color=str)marker. The visual cue (plain accent text vs accent
text + tint background) auto-derives from the drill triggers writing
fromon.columnat QS-emit time and at App2 plan-build time. Authors
declare "this column carries a drill"; the type system + renderer
pick the visual from the drill set:- any
DATA_POINT_MENUdrill writes from the column → accent text- accent-tint background
- only
DATA_POINT_CLICKdrill(s) → accent text only
DroppedCellAccentText+CellAccentMenu+ theCellFormatunion
common/clickability.py+tests/unit/test_clickability.py—
pre-stable posture, no compat shim.
- any
- App2 renderer plumbing.
_VisualPlangrows
column_decoration: Mapping[str, str];_table_column_metawalks
visual.conditional_formattingand resolves the per-column kind via
the sameDrillable.visual_kind(drills)code path the QS-side
Drillable.emituses — App2 ≡ QS by construction.
_data_shape.shape_tableforwards ascolumn.decoration;bootstrap. js::renderTablepaintscell-accent/cell-accent-menuCSS
classes on each<td>. - CSS + cell-click affordance.
widgets-theme.cssadds
.cell-accent { color: var(--color-accent); font-weight: 500 }and
.cell-accent-menu { color: var(--color-accent); background: color-mix(in srgb, var(--color-accent) 10%, transparent); cursor: pointer }.bootstrap.js::wireRowDrillsbinds left-click on
<td.cell-accent-menu>→ opens the menu drill (same code path as the
⋯ button).stopPropagation()prevents the row-level CLICK drill
from firing on Class B mix cells. Documents an operator-locked
exception to "left clicks move LEFT" — App2 may break the rule when
the explicit visual cue makes the affordance discoverable. - Apps sweep. All 13 existing
CellAccentTextcallsites in
apps/l1_dashboard/app.pymigrated toDrillable. Class C wires:
Transactions Audittransfer_id→ Posting Ledger (uses the existing
_DP_TX_TRANSFERlanding pad +_wide_date_writes()pattern); Daily
Balances Auditdb_account_id→ Daily Statement; Posting Ledger
account_id→ Daily Statement (addedbusiness_daycolumn to
l1-transactions-dsviadate_trunc_day('posting', dialect)at
SELECT time, taggedColumnShape.DATETIME_DAYso the drill writes a
day-grain date). Class C strip: Posting Ledgertransfer_id
(self-drill). Class D adds: L2FT Violation Detailentity_a;
Investigation Account Network — Touching Edgescounterparty. - Type-system gate at
Table.__post_init__(per
[[feedback_invariants_in_types]]). Walks
conditional_formatting × actions; for eachDrillable.on.column,
asserts ≥1 drill writes from that column. RaisesValueErrorat
construction with diagnostic listing every Drill on the Table + the
columns each one writes from, so the operator can spot off-by-one
column-name typos at a glance. Permanently prevents the bug class
from recurring. - Convention origin memory. "Left clicks move LEFT, right clicks
move RIGHT" is a QuickSight-limitation workaround, not a deep design
principle (operator clarification). App2 may break the rule when a
better affordance exists — Phase DA's cell-click-opens-menu is one
such authorized break. - Deployed-Studio handbook 404 fix. The per-sheet
?button's
handbook source (docs/handbook/_shared/,l1/,executives/, etc.)
moved into the wheel package at
src/recon_gen/docs/_handbook_per_sheet/. The Starlette
/handbook/<path>route updated toparents[2]/docs/_handbook_per_sheet/
so it resolves identically in repo-checkout AND wheel-install. Mkdocs
configured toexclude_docs: _handbook_per_sheet/so the per-sheet
snippets stay out of the curated site build. - QS uppercase-hex fix.
_tint_hexemits{:02X}so the auto-derived
background tint satisfies QS's^#[A-F0-9]{6}$validation pattern
(CI 27439942692 caught the original lowercase-hex regression).
Full audit + locks + per-site resolution: docs/audits/da_0_clickability_audit.md.
App2-side parity screenshots: docs/audits/da_7_app2_snaps/.
Parity verify checklist: docs/audits/da_7_parity_verify.md.
2. Tom Select clear-button (× to clear a selection). Single-select
pickers previously required click-into + Delete-key to clear; the
operator flagged this as friction. Wired Tom Select's clear_button
plugin so a one-click ✕ at the right edge of the picker clears the
selection. Multi-select pickers also carry both remove_button
(per-chip ×) AND clear_button (clear-all ×) for symmetric affordance.
Visual polish iterated against operator dogfood feedback:
- Override vendor cascade so the × pins to the right edge.
- 1.5rem circular click target with danger-tint hover background.
- Vertical-centered via
top: 50% + translateY(-50%)(overrides
vendor's fixedtop: 8pxwhich assumed default 20px-tall
.ts-control). - Swapped the glyph from U+2A2F ⨯ (math operator — math-axis ink, not
geometric center) to U+2715 ✕ (canonical UI close glyph, designed by
font vendors to optically center) via.clear-button::before.
Eliminates the "× floats above text baseline" perception.
Anti-regression test in test_html_filter_widgets.py pins the plugin
shape so future refactors can't silently drop the affordance.
3. Boot-id cache-bust for page-shell static assets. The shared
page shell in render.py hardcoded bare /static/output.css,
/static/widgets-theme.css, and seven vendor JS+CSS files. Iterating
on widgets-theme.css required the operator to Cmd+Shift+R after every
Studio restart to escape the browser cache.
Lifted Studio's existing _BOOT_ID + asset_url() helper (a
process-lifetime random hex token) from _studio_routes.py up to
render.py so the shared page shell's _VENDOR_CSS + _VENDOR_JS +
_PAGE_SHELL all route through it. Every <link> / <script> URL
the shell emits now carries ?cb=<boot>. Studio restart flips the
token, forcing every browser to refetch every asset on next page load
— no hard refresh needed.
Anti-regression test in test_vendor_assets.py
(test_page_shell_static_urls_cache_busted) regexes every
(href|src)="/static/..." out of a rendered shell and asserts ?cb=
appears in each one.
4. Row-drill MENU-only contract restored. Operator dogfood found
the L1 Overdraft sheet's table was making the entire row a left-click
target despite being declared as DATA_POINT_MENU-only. Root cause:
a clickDrill = drills[0] fallback in
bootstrap.js::wireRowDrills that promoted any first drill to a
row-wide left-click handler, overriding the documented contract.
Fallback removed. The existing
test_menu_drill_adds_ellipsis_button_per_row_and_header_cell test
was wrong (asserted n_drillable == 2 for MENU-only) — updated to
assert the correct shape: MENU-only Tables have n_drillable == 0,
no cursor: pointer on <tr>, and surface their drill ONLY via the
⋯ button + ctxmenu.