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 dispatch —
RealToolDispatcherbridges the agent loop'sToolDispatcherprotocol toMCPHost;build_real_loop_dependenciesassembles the real bundle; an in-processInvestigationConsumerRuntime(in the API lifespan, gated onGABY_ENABLE_INVESTIGATION_CONSUMER) claims queued tickets and runs the loop read-only by default. The demoFakeLLMProviderworker 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 sources —
TicketWritebackAdapter(renamed from the misnamedZohoWritebackAdapter) works for Zoho, email, and Slack alike via the baseTicketAdapterprotocol. - Onboarding step 5 now reads
/api/connectors/catalogueso 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 fix —
setuptools_scmversion pinned via build-arg so the backend image builds without.git. - Marketing → docs — the repo-root
index.htmlwas retired; the landing page is nowdocs/index.md, deployed togaby.skycloak.io. AddedBUSINESS.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.pygzips 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 sooptional_until: v0.3.0markers 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 publishstep for@gaby/widget(skipped untilNPM_TOKENsecret is provisioned).NODE_AUTH_TOKENflows through job-level env so the step-levelif:can read it —secrets.*is unavailable in stepif:conditionals. - Widget package.json is publish-ready — drops
private, addsmain,files,publishConfig: {access: public}, repository, license, homepage. README written for npm landing page. - Eval suites deferred to v0.4 —
release_gate.yamlmarks founder/support-lead/widget/slack asoptional_until: v0.4.0. The eval harness is structurally incomplete (1 LLM transcript vs. 15 fixtures; no--jsonon the investigate CLI). v0.4 ships the rebuild. - Perf baseline path corrected to
backend/tests_opt_in/perf/baselines/memory_graph.jsonafter the earlier "remove skipped/deselected tests" cleanup moved it.
Changed — v0.3 hardening: Stripe is read-only (money never inside Gaby)¶
- Removed
issue_refundand 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 thedefault_scopeswrite rule; read-only is the structural guarantee, not configurable.- New regression fence:
test_no_write_tools_at_allasserts every reasonable money-touching tool name stays out of the Stripe module. docs/operations/escalation.mddocuments 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_refundrows 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 pullSPEC.md/ARCHITECTURE.md/FOUNDATION.md/CHANGELOG.md/CONTRIBUTING.mdfrom 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--stricton PR, deploymainalias on merge, versioned +latestalias on tag viamike. - Cost dashboard at Settings → Costs (SPEC OBS-5).
GET /api/settings/costs?window=7d|30d|90dreturns 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 usesMIN(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 frompytest -qoutput. Theslow/perfmarkers were retired; their tests moved tobackend/tests_opt_in/(FalkorDB + AGE backend smokes, 9-cell round-trip matrix, scale benchmarks). Run viamake test-opt-inwhen Docker is reachable. falkordbis now a default dev dep so the optional import doesn't surface as aSKIPPEDline in the main suite.pytest -qnow reports828 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. TicketSourceBridgeadapting the MCP tool surface onto the existingTicketAdapterProtocol — 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 onKEYCLOAK_URL,API_HOSTetc.MCPHost.keepalive_ping+eager_startsurface 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 againstcreate_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-slackTypeScript package undermcp-servers/gaby-mcp-slack/(Apache 2.0, 5 contract tools, Fastify webhook listener bound before MCPinitialize, 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. PublicMCPHost.scrubber_for()replaced private-attr access./api/ticket-sourcesacceptskind="mcp_slack"withenv+webhook_portpayload; delete path stops the subprocess; test path routes toMCPHost.keepalive_ping.- Web
AddConnectorDialogroutesmcp_*kinds through the newenv+webhook_portAPI 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_custombranch, 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 throughTicketSourceBridge, legacy (zoho_desk,email) build their Python adapter. App lifespan starts + stops the runtime behindGABY_ENABLE_TICKET_POLLER(off by default so tests aren't surprised).- Bridge datetime-cursor cleanup —
TicketSourceBridgeno longer writes a syntheticnow()intolast_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. LogoTilefallback — 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.- Backend —
ALLOWED_MCP_KINDSre-admitsmcp_custom; handler 400s whencommandis missing. One-line scope as planned. - Dev-time drift warning — if the backend catalogue surfaces a kind
not in the frontend registry,
console.warnso 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-leadReact 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. Durations30s 5m 2h 1d. Actionsslack_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 viaGABY_PLAYBOOKS_DIRenv var (Docker-friendly) with repo fallback. Three seeds: account-merge, bulk-user-import, refund-request. - Plain-language toggle scaffold —
useAnswerStylehook persists preference in localStorage withpick()fallback. Backend prompt rewrite that populatesreply_plain_markdowndeferred 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/csatprefixes for namespacing consistency. - i18n — new
support_leadnamespace, 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:8080tohttp://127.0.0.1:8080so Docker's own ipv6:8080listener 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 whenSENTRY_PROJECT_SLUGis 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"raisesNotImplementedErroruntil v0.3.1 lands the approval-queue replay mechanism;mode="simulate"computes remaining-refundable math against a live charge lookup. Usesto_dict_recursive()so nested StripeObjects flatten correctly.- Restricted-key probe — read-only probe via
Refund.list+Customer.list. Warns if the API key hasCustomers:Readscope beyond what Gaby needs. Cached per process; opt-out viaSTRIPE_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 catalogue —
BUILTIN_CATALOGUEextended with all three; Stripe carries customdefault_scopesrestricting writes toissue_refundonly. - 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
ageDSL correctness, Stripe read-only probe via Refund.list, Stripeto_dict_recursivefor nested objects).
Scope deferred to v0.3.1¶
- Stripe
issue_refund(mode="execute")— needs approval-queue replay surgery inagent/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 shipstools=[]to the provider, so there is literally nothing for the LLM to call. One retry on citation enforcement failure with a tighter prompt, thenNoCitationsError. - 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_conversationstable + migration 0009. Failed rows persist witherrorcolumn populated; history endpoint surfaces them with afailed: trueflag 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 extension —
WidgetSessionStarted+ChatMessageAppendedvariants, published byWidgetSessionServiceat create + append-message points so Inbox SSE sees real live events. /askand/inboxReact 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_callsaccounting integration for the cost dashboard — happens naturally via existinganthropic.pyaccounting, 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 — purehttpx+ stdlib. - Pre-commit hooks + Conventional Commits enforcement. Ruff, Biome,
file hygiene, conventional-commits at
commit-msgstage..gitmessagetemplate andCONTRIBUTING.mddocumenting the flow. - docs/testing.md — explains the unit / integration / slow / eval
tiers and what each one needs (Docker for AGE, the
falkordblitewheel for FalkorDB,ANTHROPIC_API_KEYfor 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¶
ReviewEngineprocesses historical tickets through Claude HaikuReviewMemoryWriterextracts customer facts, resolution patterns into the memory graphStyleAggregatorbuilds 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,/metricsendpoints - 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 ingestandgaby kb queryCLI commands
Iter 3 -- Safety¶
- Scope DSL, authorization evaluator, hash-chained audit log
- PII redaction and approval queue
- 100% line+branch coverage on
safety/enforced bycov-safety.sh - Property tests for scope algebra and audit chain integrity
gaby audit verifytamper-detection CLI
Iter 4a -- Agent loop¶
- Homegrown state machine on
anthropicdirect SDK - Pure-transition functions with Protocol-based side effects
- 4-breakpoint prompt cache, BudgetGuard (tokens/USD/seconds/iterations)
- FakeLLMProvider with scripted replay
gaby investigateCLI for end-to-end loop execution
Iter 4b -- Memory backends¶
PostgresAGEMemoryGraphon Apache AGE with allow-listed Cypher helperFalkorDBMemoryGraphviafalkordb-py+ testcontainers- 9-cell round-trip test matrix (SQLite default, PG+AGE and FalkorDB slow-gated)
- Hypothesis workspace-isolation property test
gaby memoryCLI: export, import, migrate, forgetMemoryFactLoaderpopulatingapplicable_factsper 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.pyenforcing 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_completedtracking 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