R-Dash Wave 2 CODE-COMPLETE — Closeout
Decision
Wave 2 pass 3 shipped in commit 3587d49 (2026-04-17). 7 files / +803 lines. Wave 2 CODE-COMPLETE. Cube.js HTTP client (apps/api/app/modules/query_engine/cube_client.py) with httpx async against /cubejs-api/v1/load, 5-minute HS256 JWT token with user securityContext claims for Cube-side RLS evaluation, normalized QueryResult output. Two additional RLS contract tests: workspace-owner-scope (4 assertions: own+editable-workspaces match, cross-user leak check, no-workspace-user sees only own content) and salary-column-mask (11 assertions: compensation-admin bypass, partial-masked for others, NULL passthrough, 4 mask pattern variants with hash stability and join-safety, and a leak-assertion enumerating 6 dangerous role combinations that must never bypass). M4 integration test (apps/api/tests/semantic/test_integration_semantic.py, 8 assertions): metric registry populates correctly from YAML (5 metrics extracted), update bumps version + re-syncs, slug uniqueness, 5 InvalidCubeYAMLError edge cases. M5 integration test (apps/api/tests/governance/test_integration_governance.py, 8 assertions): RLS activation gate rejects missing-test-file + allows present-test-file + permits is_active=False without test, certification SoD blocks self-certify + allows different-user-certify + re-cert revokes prior, column mask uniqueness at DB, sensitivity labels (seeded 4, idempotent apply, unknown-slug raises). Test counts: DB-free unit 95 (was 80), RLS contracts 21 assertions across 3 policies (was 6 in 1), integration 42 assertions ready (was 26). STATE.md flipped to Wave 2 CODE-COMPLETE.
Rationale
Wave 2 is code-complete when (1) all M4+M5 services have unit test coverage, (2) the RLS contract pattern is proven to generalize to multiple policy shapes (3 distinct predicate patterns now: region IN list, owner OR workspace, role-based column mask), (3) integration test scaffolds exist for live-DB validation, (4) Cube.js client exists for Wave 3’s semantic-model-driven execute path. The column-mask LEAK ASSERTION (test 6 dangerous role combinations explicitly) is paranoia-driven — wanting a policy test that enumerates “these role combinations must NEVER bypass” is cheaper than discovering a bypass in production. Cube token TTL at 5 minutes limits replay-attack blast radius without breaking long queries (Cube sessions are request-scoped). Cube client shipped but not wired because the first real semantic model + first dashboard that would use it both land in Wave 3 — shipping dead code is acceptable for a well-defined contract that Wave 3 just calls; the alternative (wire it speculatively in QueryService) would ship code with no tests. Live Cube wiring + governance UI cross-cut into Wave 3 naturally because both pair with dashboard surfaces.
Alternatives Rejected
Ship Cube integration as part of Wave 2 execute path (rejected — no real semantic model exists yet; speculative wiring without test coverage). Longer Cube token TTL for convenience (rejected — 5min bounds replay risk; Cube sessions don’t need longer). Sync httpx for Cube client (rejected — async consistency with FastAPI). Consolidate _apply_policy_in_memory helper across contract tests (deferred — some duplication preserved for readability; future DRY if pattern grows). Skip integration tests entirely (rejected — auto-skip pattern means CI stays green on fresh clone while full coverage runs when dev stack up). Ship Wave 2 “done” without the 2 additional contract tests (rejected — J3 pattern only proven when it scales to multiple predicate shapes). Write contract tests AFTER Wave 3 (rejected — contract tests establish the pattern; future RLS policies clone them). Mask post-processing in M3 executor pre-commit (deferred to Wave 3 — needs dashboard query results to exercise it).
Outcome
Pending
Related
- R-Dash Wave 2 Pass 2
- R-Dash Wave 2 Pass 1
- R-Dash Wave 1 CODE-COMPLETE
- cube-query-uses-5min-hs256-jwt-with-security-context
- cube-js-widget-data-jwt-signing-pattern
- cubejs-jwt-pattern-hs256-5min-exp-issuer-locked
- echarts-chart-renderer-flat-row-shape-assumption
- aggressive-multi-pattern-sed—i-xd-yd-zd-scrub-on-scriptconf