Skip to content

Changelog

All notable changes to Gaby are documented in this file.

[Unreleased]

[0.3.0] - 2026-05-24

Post-Iter-18 — investigation goes real, multi-channel escalation, deploy story

The headline: the autonomous investigation path is now wired end to end. Before this, the worker ran on stub adapters and, in a fresh deploy, nothing consumed the ticket queue. Now a real ticket is claimed, investigated against real systems via MCP connectors, and answered.

  • Real connector dispatchRealToolDispatcher bridges the agent loop's ToolDispatcher protocol to MCPHost; build_real_loop_dependencies assembles the real bundle; an in-process InvestigationConsumerRuntime (in the API lifespan, gated on GABY_ENABLE_INVESTIGATION_CONSUMER) claims queued tickets and runs the loop read-only by default. The demo FakeLLMProvider worker was retired. A seam test proves a scripted tool call actually reaches a connector and drives a verdict.
  • Multi-channel escalation — fan-out to Slack, email (SMTP), SMS (Twilio), Opsgenie, and SIGNL4. DB-backed channel store with envelope-encrypted config, per-channel test + failure isolation, a Settings tab, and a legacy single-Slack compatibility shim.
  • Writeback works for all sourcesTicketWritebackAdapter (renamed from the misnamed ZohoWritebackAdapter) works for Zoho, email, and Slack alike via the base TicketAdapter protocol.
  • Onboarding step 5 now reads /api/connectors/catalogue so Redis/ Sentry/Stripe appear instead of a stale hardcoded list.
  • Bootstrap URL points at the web UI's public origin (GABY_PUBLIC_WEB_URL) so reverse-proxy deploys print a reachable link.
  • Docker build fixsetuptools_scm version pinned via build-arg so the backend image builds without .git.
  • Marketing → docs — the repo-root index.html was retired; the landing page is now docs/index.md, deployed to gaby.skycloak.io. Added BUSINESS.md (honest OSS-vs-commercial position).
  • Deployment docs — a first-investigation dogfooding runbook and a Kubernetes deployment guide + raw manifests (ops/k8s/) for running Gaby in-cluster with no VPN to internal systems.

v0.3 — open Gaby to channels and operators (Iters 11–18)

v0.3 is the "open Gaby to channels and operators" release. v0.1 made the agent loop real on tickets; v0.2 made it safe and observable; v0.3 ships the surfaces real teams actually want: an embeddable chat widget, Slack inbound, a Support Lead persona, read-only Redis/Sentry/Stripe connectors, an Ask Gaby read-only Q&A console, a Unified Inbox over every ingress, a cost dashboard, and a versioned MkDocs site. Plus a hardening commit that policy-excludes money-touching writes from the Gaby tool surface entirely.

Eval-graded autoresolution numbers (founder, support-lead, widget, slack suites) are deferred to v0.4 — the eval harness rebuild is the v0.4 headline iter. v0.3.0 ships behavior-tested code: 837 backend tests, 100% safety branch coverage, contract tests on every connector, honest gzip bundle measurement in the release gate.

Release plumbing — Iter 18

  • Release gate hardened. scripts/release_gate.py gzips bundles in-memory at level 9 instead of falling back to raw bytes (was misreporting the 12 KB widget as a 31 KB FAIL). CURRENT_VERSION bumped to 0.3.0 so optional_until: v0.3.0 markers flip. Bundle gate is real-measured for the first time.
  • release.yml builds the widget before the gate so the bundle check runs against the published artefact. Adds a gated npm publish step for @gaby/widget (skipped until NPM_TOKEN secret is provisioned). NODE_AUTH_TOKEN flows through job-level env so the step-level if: can read it — secrets.* is unavailable in step if: conditionals.
  • Widget package.json is publish-ready — drops private, adds main, files, publishConfig: {access: public}, repository, license, homepage. README written for npm landing page.
  • Eval suites deferred to v0.4release_gate.yaml marks founder/support-lead/widget/slack as optional_until: v0.4.0. The eval harness is structurally incomplete (1 LLM transcript vs. 15 fixtures; no --json on the investigate CLI). v0.4 ships the rebuild.
  • Perf baseline path corrected to backend/tests_opt_in/perf/baselines/memory_graph.json after the earlier "remove skipped/deselected tests" cleanup moved it.

Changed — v0.3 hardening: Stripe is read-only (money never inside Gaby)

  • Removed issue_refund and every other write tool from the Stripe connector. Money-touching actions (refunds, charges, subscription changes) are policy-excluded from the Gaby tool surface — the agent surfaces the case (charge, amount, eligibility, customer history) and a billing-role human takes the action in Stripe directly.
  • BUILTIN_CATALOGUE["stripe"] drops the default_scopes write rule; read-only is the structural guarantee, not configurable.
  • New regression fence: test_no_write_tools_at_all asserts every reasonable money-touching tool name stays out of the Stripe module.
  • docs/operations/escalation.md documents the policy + the v0.4 roles taxonomy (support_l1 / billing / infra / security) + per-workspace channel routing landing next iter.
  • Refund playbook (playbooks/refund-request.yaml) bumped to v2 — last step is "surface to billing human" instead of "queue refund for approval."
  • PCI scope doc updated: issue_refund rows removed from the per-tool PAN-adjacency table.

Added (v0.3 master-plan Iter 17 — MkDocs docs site + cost dashboard)

  • MkDocs Material site at the repo root (mkdocs.yml, docs/). Single-source includes pull SPEC.md/ARCHITECTURE.md/FOUNDATION.md/ CHANGELOG.md/CONTRIBUTING.md from the canonical repo files — no fork, no duplication. Pages: Home, Concepts (5), Connectors (6), Operations (3), Reference (changelog), Contributing, Decisions (2 ADRs).
  • CI workflow .github/workflows/docs.yml — build --strict on PR, deploy main alias on merge, versioned + latest alias on tag via mike.
  • Cost dashboard at Settings → Costs (SPEC OBS-5). GET /api/settings/costs?window=7d|30d|90d returns totals, by-purpose buckets, and by-connector buckets. Rolling windows (now - Nd). Cache- read tokens surfaced separately so operators see the main cost lever.
  • observability/costs.py — three-query rollup. Critic P0 fix: by-connector attribution uses MIN(action.id) per investigation so a multi-action investigation doesn't row-explode its cost across connectors. Regression test for the multi-connector case.
  • EN + FR i18n — 16 new keys per locale under settings.json.

Test infrastructure cleanup

  • Removed the -m "not slow and not perf" default-exclude addopt that hid 11 tests from pytest -q output. The slow/perf markers were retired; their tests moved to backend/tests_opt_in/ (FalkorDB + AGE backend smokes, 9-cell round-trip matrix, scale benchmarks). Run via make test-opt-in when Docker is reachable.
  • falkordb is now a default dev dep so the optional import doesn't surface as a SKIPPED line in the main suite.
  • pytest -q now reports 828 passed, 0 skipped, 0 deselected.

Added (Iter 13a — MCP ticket-source contract)

  • Ticket-source MCP contract (connectors/_contract/ticket_source.md) pivoting ticket sources from first-party Python adapters to MCP subprocesses. 5 required tools (fetch_new_tickets, get_ticket, post_reply, mark_resolved, healthcheck) with opaque cursor semantics, webhook-port binding protocol, and overflow policy.
  • Shared contract test suite (test_ticket_source_contract.py, 8 parametrised tests) + stub ticket-source MCP fixture.
  • TicketSourceBridge adapting the MCP tool surface onto the existing TicketAdapter Protocol — legacy poller unchanged, both cursor types (datetime + opaque string) coexist until Iter 14.
  • secret_scrub.Scrubber — best-effort redaction of env-derived secrets from connector error messages. Word-boundary regex + URL/HOST denylist avoids false positives on KEYCLOAK_URL, API_HOST etc.
  • MCPHost.keepalive_ping + eager_start surface for push-mode ticket sources (consumed by Iter 13b lifespan hook).
  • Supervisor task-boundary regression fence (test_mcp_supervisor_task_boundary.py) — the test the supervisor docstring previously referenced but which didn't exist.
  • Migration 0007 adding webhook_port + last_seen_opaque_cursor, idempotent against create_all-bootstrapped databases.

Tests

  • 710 backend tests (up from 704 at 12b), 0 regressions. Safety still 100% line + branch covered. mypy strict + ruff clean.

Added (Iter 13b — Slack reference MCP + registry)

  • @gaby/mcp-slack TypeScript package under mcp-servers/gaby-mcp-slack/ (Apache 2.0, 5 contract tools, Fastify webhook listener bound before MCP initialize, HMAC + timestamp-skew verification, channel allowlist, in-memory ring buffer with overflow drop-oldest). Passes the full Iter 13a contract suite including real webhook-port bind.
  • TicketSourceRegistry (gaby/connectors/ticket_source_registry.py) — spawns MCP ticket sources from DB rows, encrypts env, eager-starts at boot via a lifespan hook. Public MCPHost.scrubber_for() replaced private-attr access.
  • /api/ticket-sources accepts kind="mcp_slack" with env + webhook_port payload; delete path stops the subprocess; test path routes to MCPHost.keepalive_ping.
  • Web AddConnectorDialog routes mcp_* kinds through the new env + webhook_port API fields; catalogue includes MCP entries synthesized from the registry.
  • Iter 13b critic ceremony: 5 must-fixes applied (MCPHost leak on handshake failure, public scrubber accessor, dropped unreachable mcp_custom branch, docstring fix, webhook rate-limit docs).

Added (Iter 14 — Unified poller runtime)

  • TicketPollerRuntime (gaby/workers/poller_runtime.py) — one task per ticket source, discovers every enabled row regardless of kind and dispatches: MCP kinds route through TicketSourceBridge, legacy (zoho_desk, email) build their Python adapter. App lifespan starts + stops the runtime behind GABY_ENABLE_TICKET_POLLER (off by default so tests aren't surprised).
  • Bridge datetime-cursor cleanupTicketSourceBridge no longer writes a synthetic now() into last_seen_modified_at. Iter 13a critic P5 resolved; opaque cursor is the single source of truth for MCP sources.

Scope note

  • Full TypeScript ports of Zoho Desk (OAuth + threading) and email (IMAP+SMTP + XOAUTH2) deferred to v0.4 — each is an iter of its own. Iter 14 ships the unification that lets those ports swap in transparently.

Added (Iter 15 — Ticket-source registry UI)

  • Curated 13-card registry in the Add dialog: Slack / Zoho Desk / Email as Available; Intercom, Freshdesk, Zendesk, Help Scout, HubSpot, Linear, GitHub Issues, Discord, Microsoft Teams as Coming soon; plus a Custom-MCP power-user tile. Status derives from the backend catalogue so adding a future adapter auto-flips a card to Available.
  • Custom MCP form — command input, args repeater, env repeater with secret-key masking (TOKEN|SECRET|KEY|PASSWORD), optional webhook port, amber warning banner ("runs arbitrary commands — trust only"). Length caps on all fields, stable row UUIDs so mid-list removes don't drop input focus.
  • LogoTile fallback — gradient letter tile keeps Gaby out of vendor-brand-policy territory. Only Slack's public-lockup SVG ships; adding other vendor logos later is documented as a licensing step.
  • BackendALLOWED_MCP_KINDS re-admits mcp_custom; handler 400s when command is missing. One-line scope as planned.
  • Dev-time drift warning — if the backend catalogue surfaces a kind not in the frontend registry, console.warn so regressions surface.
  • EN + FR i18n — 12 new keys per locale (registry_*, custom_mcp_*).
  • Iter 15 critic ceremony — 5 fixes applied (P0 Save-disabled bug, dialog width, drift warning, stable row ids, length caps). Visual verification via Playwright MCP — 3 screens captured, 0 console errors.

Added (v0.3 master-plan Iter 14 — Support Lead persona React port)

  • /support-lead React route tree — dashboard (auto-resolution %, stats grid, avg resolution time, CSAT stats), playbook library, SLA board (4 buckets), CSAT review queue, escalation rules CRUD. Wrapped in a dedicated two-column shell with a scoped violet accent (distinct from the app-wide indigo), 5th top-nav item slotted between Approvals and Settings.
  • Escalation rules tiny DSL (gaby/escalation/rules.py) — hand-written recursive-descent parser + evaluator. Grammar: if <cond> (and <cond>)* then <action>. Operators == != < > <= >= in. Durations 30s 5m 2h 1d. Actions slack_channel("#x"), escalate(), assign_to("user"). Tokenizer hard-capped at 4096 chars for defense-in-depth. Evaluation never raises on missing fields — returns None. 21 unit tests.
  • CSAT responses + escalation_rules tables (migration 0008). CSAT admin-gated via POST /api/csat; v0.4 will add a second short-lived-token route for widget-originated submissions instead of weakening this one's auth.
  • Playbook library — YAML files under playbooks/ resolved via GABY_PLAYBOOKS_DIR env var (Docker-friendly) with repo fallback. Three seeds: account-merge, bulk-user-import, refund-request.
  • Plain-language toggle scaffolduseAnswerStyle hook persists preference in localStorage with pick() fallback. Backend prompt rewrite that populates reply_plain_markdown deferred to v0.3.1 so verdict-eval pass rates don't regress mid-iter.
  • Backend API: /api/support-lead/{metrics,sla,csat-queue,playbooks}
  • full escalation-rules CRUD + /api/csat[,/summary]. Metrics query collapsed to 3 round-trips via GROUP BY (was 7). Two sub-routers with /api/support-lead + /api/csat prefixes for namespacing consistency.
  • i18n — new support_lead namespace, 40 keys EN + FR, full translation verified in Playwright (FR nav reads "Responsable support", hero reads "RÉSOLUS AUTOMATIQUEMENT", etc.).
  • Vite proxy fix — swapped http://localhost:8080 to http://127.0.0.1:8080 so Docker's own ipv6 :8080 listener can't intercept dev API calls.
  • Iter S critic ceremony — 5 P0/P1 fixes applied (playbook path env override, metrics query collapse, DSL tokenizer length guard, router prefix consistency, CSAT docstring clarification).

Tests

  • 753 backend (+40: 21 DSL + 6 playbook loader + 13 API) + 19 web (was 14 — +5 useAnswerStyle) + 32 contract + 16 Slack MCP unit
  • safety still 100%.

Added (v0.3 master-plan Iter 15 — Redis + Sentry + Stripe connectors)

  • connectors/redis/server.py — read-only MCP with 5 tools (healthcheck, info, get_key, list_keys, memory_stats). Absence of write tools is the guarantee, not a runtime flag. list_keys(pattern="*") refuses on keyspaces > 100k. All composite types (list/hash/set/zset) consistently cap at 100 entries with {truncated, total} metadata.
  • connectors/sentry/server.py — read-only MCP with 4 tools (healthcheck, get_issue, list_recent_issues, get_event). Hard-fails (ScopeViolationError) on cross-project queries when SENTRY_PROJECT_SLUG is configured. list_recent_issues(age=...) uses Sentry's DSL (24h/7d) with strict validation.
  • connectors/stripe/server.py — 5 read tools + 1 simulate-only write (issue_refund). mode="execute" raises NotImplementedError until v0.3.1 lands the approval-queue replay mechanism; mode="simulate" computes remaining-refundable math against a live charge lookup. Uses to_dict_recursive() so nested StripeObjects flatten correctly.
  • Restricted-key probe — read-only probe via Refund.list + Customer.list. Warns if the API key has Customers:Read scope beyond what Gaby needs. Cached per process; opt-out via STRIPE_DISABLE_KEY_PROBE=1.
  • docs/security/pci-scope.md — SAQ-A scope statement. Enumerates every tool's PAN-adjacency (all: none) and the operator guidance for key rotation + restricted-key use.
  • Backend catalogueBUILTIN_CATALOGUE extended with all three; Stripe carries custom default_scopes restricting writes to issue_refund only.
  • Contract tests — matrix now covers 5 connectors × 8 tests = 40 passing. Secret-leak heuristic given a minimum-length floor so boolean env flags (STRIPE_DISABLE_KEY_PROBE=1) aren't misclassified.
  • Iter 15M critic ceremony — 4 P1 fixes applied (Redis composite- type truncation flags, Sentry age DSL correctness, Stripe read-only probe via Refund.list, Stripe to_dict_recursive for nested objects).

Scope deferred to v0.3.1

  • Stripe issue_refund(mode="execute") — needs approval-queue replay surgery in agent/runner.py + SqlaApprovalWriter. v0.3 ships simulate-only; execute raises NotImplementedError with a clear pointer at v0.3.1.
  • Connector CI workflow stub (documented, not shipped).

Tests

  • 789 backend (+36: 10 Redis unit + 12 Sentry unit + 14 Stripe unit) + 19 web + 40 contract (5 connectors × 8) + 32 Slack MCP unit + safety still 100%.

Added (v0.3 master-plan Iter 16 — Ask Gaby + Unified Inbox)

  • Ask Gaby engine (gaby/agent/ask/) — three-state read-only Q&A: RETRIEVING → ANSWERING → VERIFYING_CITATIONS. Structural no-writes guarantee: engine ships tools=[] to the provider, so there is literally nothing for the LLM to call. One retry on citation enforcement failure with a tighter prompt, then NoCitationsError.
  • Citation enforcement — every cited token must match a retrieved chunk's (document_uri, line_start, line_end) triple. Headings are intentionally drift-tolerant (model may shorten them).
  • ask_conversations table + migration 0009. Failed rows persist with error column populated; history endpoint surfaces them with a failed: true flag so users see drift.
  • POST /api/ask + GET /api/ask/history — admin-gated, conversation_id is a grouping token (not multi-turn).
  • GET /api/inbox — unified list over tickets + chat sessions via SQL UNION ALL with typed discriminator. Workspace-scoped; cursor pagination.
  • GET /api/inbox/stream — SSE stream over shared EventBus. Workspace-scoped filtering; 15s heartbeat for proxy friendliness.
  • EventBus extensionWidgetSessionStarted + ChatMessageAppended variants, published by WidgetSessionService at create + append-message points so Inbox SSE sees real live events.
  • /ask and /inbox React routes + nav links. Filter pills on inbox (all/ticket/widget/chat), per-kind badges, live indicator, load-more pagination. History pane on Ask.
  • EN + FR i18n for both namespaces.
  • Iter 16 critic ceremony — P0 fix (widget/chat events now actually publish) + 2 P1 fixes (request-as-None anti-pattern, failed-asks visible in history).

Scope deferred to v0.3.1

  • Widget API-key management UI tab (operator key CRUD via direct API for v0.3; Chat settings tab lands with v0.3.1's docs iter).
  • Ask LLM call llm_calls accounting integration for the cost dashboard — happens naturally via existing anthropic.py accounting, but Iter 17's dashboard reads the rows.

Tests

  • 837 backend (+24: 4 Ask engine + 5 citer + 7 Ask API + 8 inbox API)
  • 19 web + 40 contract + 32 Slack MCP unit + safety still 100%.

[0.2.1] - 2026-04-12

Polish release focused on making email onboarding work with modern providers without begging users for app passwords.

Added

  • Gmail + Microsoft 365 OAuth for the email adapter. XOAUTH2 SASL for IMAP and SMTP, token refresh with rotation handling (Microsoft rotates, Google doesn't), asyncio.Lock-guarded refresh to prevent token-endpoint stampedes. New /api/ticket-sources/email/oauth/* routes: providers, start, callback. No third-party OAuth library added — pure httpx + stdlib.
  • Pre-commit hooks + Conventional Commits enforcement. Ruff, Biome, file hygiene, conventional-commits at commit-msg stage. .gitmessage template and CONTRIBUTING.md documenting the flow.
  • docs/testing.md — explains the unit / integration / slow / eval tiers and what each one needs (Docker for AGE, the falkordblite wheel for FalkorDB, ANTHROPIC_API_KEY for the eval smoke).

Tests

  • 554 backend tests (up from 538), 0 regressions. mypy strict clean. Ruff clean on all new and modified files.

[0.2.0] - 2026-04-12

Second release. Ships the history replay engine integrated into onboarding, full Settings tabs, SMTP nightly summary mailer, and connectors UX overhaul.

Iter 9a -- Email adapter + Zoho OAuth refresh

  • Email-to-ticket adapter: IMAP polling with MIME parsing and threading
  • Zoho Desk OAuth2 refresh-token rotation with encrypted storage
  • Ticket source abstraction: unified interface across Zoho, email, and future adapters
  • Admin API for ticket source CRUD and connection testing

Iter 9b -- History replay engine

  • ReviewEngine processes historical tickets through Claude Haiku
  • ReviewMemoryWriter extracts customer facts, resolution patterns into the memory graph
  • StyleAggregator builds a team writing-style profile from past responses
  • Cost estimation using the pricing table before user confirmation
  • Replay session API: create (estimate), start, poll progress, get summary
  • Background task processing with progress callbacks

Iter 9c -- Connectors UX overhaul

  • Two-section layout: catalogue browser + active connectors
  • Flow visualization showing connector pipeline status
  • Improved connector cards with status badges and tool counts
  • Responsive grid layout with accessibility improvements

Iter 10 -- Onboarding replay integration, Settings tabs, SMTP mailer

  • Onboarding wizard expanded to 7 steps (was 6): new "Learn from History" step
  • History replay wired into onboarding: cost estimate, start review, progress bar, summary
  • Settings page with tabbed layout: Escalation, LLM, Memory
  • LLM tab: model picker per purpose, API key status, budget defaults
  • Memory tab: graph stats (node/edge counts), node counts by label
  • Archive endpoint for memory nodes
  • SMTP nightly summary mailer: HTML + plain text email via aiosmtplib + Jinja2
  • Nightly summary template with stat cards and top-5 investigations list
  • SMTP config: host, port, username, password, from/to addresses, schedule hour
  • i18n: EN + FR translations for history replay step, settings tabs
  • Settings nav link in the app shell

[0.1.0] - 2026-04-12

First public release. Ships a fully functional AI support agent that investigates tickets autonomously, resolves what it can, and escalates what it cannot.

Iter 0 -- Scaffold

  • FastAPI backend with /health, /ready, /metrics endpoints
  • First-run bootstrap URL for admin account creation
  • 23 SQLAlchemy models, SQLite-backed MemoryGraph
  • Alembic migrations, mypy strict, ruff linting

Iter 1 -- Connectors

  • MCP connector framework: spawn, handshake, tool call, restart, shutdown
  • First-party connectors: PostgreSQL, Keycloak
  • 8-point contract test suite per connector
  • Admin-only REST API and React UI for connector management

Iter 2 -- Knowledge

  • Token-aware Markdown chunker with pluggable embedder
  • SQLite FTS5 full-text search + brute-force vector store
  • Hybrid RRF retrieval with citation tracking
  • gaby kb ingest and gaby kb query CLI commands

Iter 3 -- Safety

  • Scope DSL, authorization evaluator, hash-chained audit log
  • PII redaction and approval queue
  • 100% line+branch coverage on safety/ enforced by cov-safety.sh
  • Property tests for scope algebra and audit chain integrity
  • gaby audit verify tamper-detection CLI

Iter 4a -- Agent loop

  • Homegrown state machine on anthropic direct SDK
  • Pure-transition functions with Protocol-based side effects
  • 4-breakpoint prompt cache, BudgetGuard (tokens/USD/seconds/iterations)
  • FakeLLMProvider with scripted replay
  • gaby investigate CLI for end-to-end loop execution

Iter 4b -- Memory backends

  • PostgresAGEMemoryGraph on Apache AGE with allow-listed Cypher helper
  • FalkorDBMemoryGraph via falkordb-py + testcontainers
  • 9-cell round-trip test matrix (SQLite default, PG+AGE and FalkorDB slow-gated)
  • Hypothesis workspace-isolation property test
  • gaby memory CLI: export, import, migrate, forget
  • MemoryFactLoader populating applicable_facts per investigation

Iter 5 -- Zoho Desk + worker + observability

  • Zoho Desk polling adapter via httpx with respx-mocked tests
  • Real MCP tool dispatch with anyio cancel-scope supervisor
  • Dialect-aware worker claim (Postgres SKIP LOCKED / SQLite asyncio lock)
  • Six Prometheus metrics, no-op OTel scaffold, structlog context
  • Safety-denied writeback (zero HTTP POSTs to production Zoho)

Iter 6 -- React UI + wizard + Slack

  • Founder onboarding wizard wired to real APIs
  • Dashboard and investigation detail routes with live data
  • Slack outbound webhook client (httpx + retry)
  • "Remember this" memory API
  • Six new backend routers (tickets, investigations, metrics, settings, test-slack, memory)
  • 5 Playwright E2E tests against live Vite + FastAPI

Iter 7a -- Eval harness + release gate

  • 15 YAML eval fixtures: 5 Founder scenarios x 3 variations each
  • Promptfoo config with per-fixture assertions
  • release_gate.py enforcing auto_resolution >= 60% and zero safety violations

Iter 7b -- i18n

  • Full EN + FR translation for all UI routes
  • i18next integration with browser language detection
  • Language switcher component in the app shell

Iter 8 -- Release

  • Refactored onboarding wizard: 6-step flow (use case, help desk, systems, AI, escalation, telemetry)
  • onboarding_completed tracking via localStorage
  • Dockerfiles for backend (Python 3.12 + uv) and web (Node 22 + nginx)
  • GitHub Actions release workflow (tag-triggered, GHCR + PyPI wheel)
  • Landing page CTA updated from "Join Waitlist" to "Install"

What does NOT ship in v0.1

  • SMTP email notifications (Slack only)
  • Helm chart publishing and SBOM generation
  • Database-backed onboarding completion (localStorage for now)
  • Real telemetry collection pipeline
  • RTL layout support