How FlowProxy gets the semantic layer. Implements ADR-0012 … ADR-0015. Plan: ws7-plan.md. Spike: ws7-spike-findings.md.
git (sem_*.yml) → CI validate (plan-only) → publish bundle → ManifestStore → proxy hot-swap → BI
Verified live: CI published a bundle to a store; the proxy booted in STORE mode,
loaded it (5 cubes exposed), and served correct semi-additive balances
(AMER 3000, EMEA 2000) over the wire — no dbt project checkout at runtime.
| Piece | Module | Notes |
|---|---|---|
| Bundle + store | engine/manifest_store.py |
Bundle (manifest + metadata + profiles.template), sha256 integrity, FsspecStore (s3/gs/az/file/https via fsspec), open_store() |
| from_bundle | engine/compiler.py |
SemanticCompiler.from_bundle() composes a synthetic dbt skeleton (profile name derived from the template) + injects creds; no project checkout |
| CI gate | flowproxy_cli/ |
python -m flowproxy_cli validate <dir> [--publish URI] — plan-only (MetricFlow explain, no warehouse), validates saved queries + auto-cubes, breaking-change diff, publishes bundle + validation report |
| Hot-swap | engine/live_layer.py, network/refresh.py |
LiveSemanticLayer (compiler+extractor+catalog+responder) behind an atomic ref; SemanticLayerManager blue/green swap serialized under a lock (global-state finding); poll loop + SIGHUP + admin-reload |
| Runtime wiring | main.py |
STORE mode (FLOWPROXY_MANIFEST_URI) vs DIRECT mode (back-compat); server reads the current layer per request via layer_provider |
| CD | .github/workflows/semantic-layer.yml |
validate on PR; publish only on release: published |
| Image | Dockerfile |
runtime consumes from store — no dbt parse, no project mount; telemetry off |
- Full bundle (manifest + metadata.json + profiles.template.yml) — auditable.
- Poll + signal refresh, atomic blue/green swap.
- Env / mounted profiles creds — never in the bundle (SELECT-only at runtime).
- Offline plan-only gate — no warehouse creds in CI.
- Publish only on production release — PRs validate without publishing.
- Integrity: a tampered bundle (bytes ≠ recorded sha256) is refused.
- Blue/green: a failed build keeps the current version serving — a bad publish never takes the proxy down.
- Immutable snapshots: an in-flight request's captured layer is unaffected by a concurrent swap.
- Canonical parse: one
parse_manifest_from_dbt_generated_manifestfeeds both registry and engine — no drift (also fixed a latentexplainresult attribute bug:sql_statement.sql, notrendered_sql.sql_query).
test_ws7a_manifest_store.py (11) · test_ws7d_from_bundle.py (3) ·
test_ws7b_validate_gate.py (4) · test_ws7c_hotswap.py (5).
| Variable | Meaning |
|---|---|
FLOWPROXY_MANIFEST_URI |
bundle store URI (s3://…, file://…); enables STORE mode |
FLOWPROXY_MANIFEST_POLL_INTERVAL |
poll seconds (default 300; 0 = signal-only) |
FLOWPROXY_ADMIN_TOKEN |
bearer gating POST /admin/reload (mount pending) |
- Mounting
POST /admin/reloadon the MCP/admin HTTP app (handler built innetwork/refresh.py; needs the HTTP surface from WS4/MCP-HTTP). - Online (execute-a-smoke-query) CI stage — opt-in staging pipeline.
SecretProviderabstraction — env/mounted-file covers Phase 1 (ADR-0015).