v0.0.75 — v0.0.74 closeout
Released: 2026-05-17. Ten commits.
Mostly a fixup release: three wire-filter regressions caught post-v0.0.74, seven migrations that shipped but were never registered with migrate.py, and an /apps/<key> org-context gap on the saas viewer.
Wire-filter — three post-v0.0.74 bugs
- Apply rewrite 500 on
UndefinedColumnError(dashboard_cards.updated_at)— the column doesn't exist; card mtime is tracked at the parent dashboard level. The bad clause is dropped andhoneyframe.dashboards.updated_atbumped instead. - Wired card kept returning baked 1,000 (MOCK badge) for L7D and L30D alike — the execute path short-circuits to mock rows before parameter binding when
card_config.data_source == "mock". The apply UPDATE now stripsdata_source+mock_data+mock_reasonfromcard_configin the same transaction so the rewritten SQL actually executes against the warehouse. - Toolbar period chip had no effect on cards wired with
{{appointment_date_start/end}}— hand-edited SQL showed "Card references a dashboard parameter with no value set".executeAllCardsonly sent operator-typedparamValues;filterColumn/filterStart/filterEndwent into the column-based filters payload, which wraps SQL but doesn't bind{{name}}tokens. NewbuildToolbarParams()derives synthetic{<bare_col>_start/_end/_value}entries using the same naming convention aswire_filter._param_name_for_column.
Edit Card drawer follow-up
The wizard fix covered the wire-filter path, but the team's workaround went through the Edit Card drawer. MOCK_PASSTHROUGH_KEYS unconditionally re-attached data_source / mock_data / mock_reason / mock_origin to the outgoing PATCH — correct for display-only edits (the form has no UI for these), wrong when the operator changed the SQL itself. The preserve is now gated on cardSql === editCard?.sql_query. SQL changed → drop mock fields. SQL unchanged → preserve them.
7 missing v0.0.74 migrations + CI guard
v0.0.74 shipped 7 migrations that were never wired into scripts/migrate.py's MIGRATIONS list. honeyframe update reported "0 failed" but the migrations were never considered — the allow-list ended at 2026-05-13_flow_recipes_version.sql. Broke:
/api/agents/library(500 — missinghoneyframe.agent_pins)- Batch H data-policy audit drawer (missing
data_policy_audit+data_policies.created_by_id) - Data API row-scope enforcement (missing
api_keys.row_scope) - Scenarios mail reporter (missing
scenario_reporters) trigger_python/dataset_modifiedscenario triggers- Team reviews #19 (missing
agent_test_trait_votes)
Fix: register the 7 migrations + add paas/tests/unit/test_migrations_registered.py — a CI guard that fails if any paas/scripts/migrations/*.sql is neither in MIGRATIONS nor in an explicit allow-list. The allow-list captures the current 91 legacy unregistered files; future ones must opt in deliberately. Also catches duplicate registrations.
The fix was hot-patched by hand on test ECS the same day; v0.0.75's tarball re-applies via the corrected migrate.py for any tenant still on a fresh v0.0.74 install.
/apps/<key> org-context gap
Three coordinated changes targeting the class of bug surfaced when seeding a webapp onto a tenant prod org: the asset created cleanly, but /api/webapps/<key> returned 404 on the saas viewer because the saas tab never sent X-Org-Id and the endpoint scoped strictly by JWT user.org_id (NULL for superadmins).
saas/frontend/src/api/client.ts— interceptor now forwardsX-Org-IdandX-Project-Idfrom localStorage, mirroring paas. The backend auth middleware already honors these headers for superadmins (cross-tenant) and multi-org users (only orgs they belong to). One-line config fix that closes a whole class of "404 from a backend that has the row" surprises on saas.saas/frontend/src/pages/Login.tsx— both login paths (form + Google) now callpersistOrgContext(meRes.data)soselected_org_idandselected_project_idare written to localStorage on first login.paas/backend/routers/webapps.py GET /{key}— admin / superadmin: noorg_idfilter (cross-tenant visibility), result ordered so the caller's own org wins on ties. Regular user: scoped touser.org_idORis_public=true, so a webapp marked public can be share-linked without strict org membership.
Saas — webapps router missing
The saas webapp viewer at acme.example.com/apps/<key> returned 404 because saas/backend/main.py never included webapps.router. Hot-patched on prod and shipped in v0.0.75.
nginx-test — hashed bundles under /apps
Previous inner-location regex pattern had a broken alias — nginx appended the full URI to the alias path instead of stripping the location prefix, giving 403 / 404 on every JS/CSS hit under /apps/assets/. Switched to top-level ^~ prefix locations placed before the SPA fallbacks: /apps/assets/* and /assets/* get 1y immutable, /apps/* and /* get no-cache SPA fallback.
Webapp seed — prod-friendly knobs
The webapp seed gains ASSET_KEY / ASSET_TITLE env vars and falls back to the first-N cards in dashboard render order when the explicit card_id list misses everything (prod dashboards have different ids than test). A useProdLayout branch alongside useTestLayout lets a seed map hand-picked card ids into a curated layout when running against prod.
Polish
- ChromeConfig gains
alert_pill_text+alert_pill_severity. The pill is no longer hardcoded "Alert pill placeholder"; severity maps to red / amber / blue badge colors. A seed can set a tenant's own alert string. .gitignorepicks up the one-offcapture-*/scrape-*/smoke-*scripts andsaas/outputs/pptx artifacts.