Skip to main content
Version: v0.0.80

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

  1. 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 and honeyframe.dashboards.updated_at bumped instead.
  2. 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 strips data_source + mock_data + mock_reason from card_config in the same transaction so the rewritten SQL actually executes against the warehouse.
  3. 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". executeAllCards only sent operator-typed paramValues; filterColumn / filterStart / filterEnd went into the column-based filters payload, which wraps SQL but doesn't bind {{name}} tokens. New buildToolbarParams() derives synthetic {<bare_col>_start/_end/_value} entries using the same naming convention as wire_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 — missing honeyframe.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_modified scenario 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).

  1. saas/frontend/src/api/client.ts — interceptor now forwards X-Org-Id and X-Project-Id from 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.
  2. saas/frontend/src/pages/Login.tsx — both login paths (form + Google) now call persistOrgContext(meRes.data) so selected_org_id and selected_project_id are written to localStorage on first login.
  3. paas/backend/routers/webapps.py GET /{key} — admin / superadmin: no org_id filter (cross-tenant visibility), result ordered so the caller's own org wins on ties. Regular user: scoped to user.org_id OR is_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.
  • .gitignore picks up the one-off capture-* / scrape-* / smoke-* scripts and saas/outputs/ pptx artifacts.