Context. Implementation companion to
docs/launch-strategy.md. This file is the concrete, actionable backlog for the 2-week pre-launch sprint.Status. Pending kickoff. Each item has an effort estimate and a blocker/blocked-by relationship. Total: ~80 hours of focused work.
- Items marked [BLOCKER] must ship before launch.
- Items marked [STRONG] materially improve launch outcomes; ship if time allows.
- Items marked [OPTIONAL] are 1-day improvements with diminishing returns.
- Effort is in person-hours assuming a single developer with Claude assistance.
- Stand up a public hosted instance using mock mode (no Azure auth needed).
- Pin a deterministic mock dataset that tells a complete product story (visitors trending up, one anomaly, one slow endpoint, readiness score 68).
- Subdomain on a domain we control. Not a Render-generated URL (looks amateur).
- Cloudflare in front for caching + DDoS during launch spike.
- Health check + uptime monitoring (free tier of UptimeRobot or BetterStack).
The current README is informative but reads like docs, not like a pitch. Rewrite around this structure:
- One-line tagline that includes "Azure" + "AI" + a number. Example: "Turn Azure App Insights into shareable Marketing & Technical dashboards in under 2 minutes — AI-powered, MIT-licensed."
- Hero GIF (see A3).
- Why it exists (3 lines max — the Azure portal pain).
- Try it now — single
docker run …line + demo URL. - What's inside — bulleted feature list with one screenshot per group.
- Comparison table (vs Azure portal, vs Datadog, vs Power BI).
- Privacy & security (the trust paragraph: no raw data leaves tenant, here's the auditable code path).
- Roadmap — link to
docs/backlog/. - Contributing + license + ⭐ ask.
The first screen of the README determines 70% of stargazers vs. bouncers.
- Shipped: README rewritten end to end. Tagline as suggested
("Turn Azure App Insights into shareable Marketing & Technical
dashboards in under 2 minutes — AI-mapped schema, deterministic KQL,
nothing raw ever leaves your tenant. MIT."). All 9 sections in
order; the "What's inside" list highlights the just-shipped B1-B4
surfaces (alias/regex schema mapping, narration panel, first-run
banner, period-over-period chips) so a HN visitor can see the
product is real, not slideware. Comparison table includes Azure
Portal / Datadog / Power BI with a one-line honesty disclaimer
underneath ("the columns we're least kind to are also the most
mature and have features we don't"). Privacy & security paragraph
links the SECURITY.md auditable controls and the seven encoded
checks. Roadmap links the per-track backlog and the
launch-strategy traction-gate doc. The old "API endpoints" + "Env
variables" wall-of-text trimmed to a short Configuration reference
pointing at
src/server.jsfor the full surface. - Asset placeholders: the hero GIF block is an HTML comment
pointing at
docs/assets/hero.gif— needs A3. The "What's inside" bullet list has a sibling HTML comment marking where one screenshot per group would let it breathe. The "Live demo · coming with the public launch" line waits on A1 for the real URL. All three are now tracked indocs/maintainer-todo.mdas discrete TODOs so the maintainer can swap them in without spelunking. - Validation: README renders cleanly (Markdown only, no broken links to the existing tracked files). Tests still 74/74 — README changes are pure-doc and don't touch any code path.
- 30-45 second screen recording: connect → dashboard renders → switch tabs → see readiness score → copy a prompt.
- Use the demo dataset from A1 so it always looks the same.
- Annotate with overlay text ("2 minutes from zero to dashboard", "AI maps your custom dimensions automatically", "no raw data leaves your tenant").
- Compress with Gifski or upload as MP4 to GitHub Issues for inline embed.
- 1200×630 image with logo + tagline + dashboard preview.
- Set OG meta on root URL, demo URL, and GitHub repo (description + topics).
- Test with the OG debuggers (Twitter card validator, LinkedIn post inspector, Slack).
- Every share link must visually look like a real product, not a GitHub fallback card.
- Above the fold: tagline + screenshot + two CTAs ("Try the demo", "Star on GitHub").
- Below: comparison table, security paragraph, FAQ (3 questions: "does it store my data?", "how do you connect to Azure?", "is the AI required?").
- Footer: GitHub link, license, contact email.
- No tracking beyond Plausible/Umami self-hosted (privacy-aligned with product positioning).
- Shipped:
public/index.html#landingPagerewritten with the spec's structure end to end. Tagline now matches the README ("Azure App Insights → Marketing & Technical dashboards in 2 minutes. AI-mapped schema. 22 KQL templates. Nothing raw ever leaves your tenant. MIT."). CTAs reordered so the demo button is primary, Connect-Azure is ghost, and a third button links the repo with a ★ icon. Below-the-fold sections, in order: feature cards (lightly updated copy to mention period-over-period chips and the paste-into-Cursor framing), comparison table (Keren Analytics vs Azure Portal / Datadog / Power BI; 6 rows; honesty disclaimer underneath), security paragraph (links the seven encoded controls- the SECURITY.md reporting path), 3-question FAQ (data storage /
Azure connection / AI required), docs link, and a footer with
GitHub / MIT License / Security / Contact. All sections styled in
public/styles.cssusing the existing accent palette so the page works in both light and dark mode without a second pass.
- the SECURITY.md reporting path), 3-question FAQ (data storage /
Azure connection / AI required), docs link, and a footer with
GitHub / MIT License / Security / Contact. All sections styled in
- No new tracking: the landing footer copy promises "no tracking
cookies; the only analytics is whatever Plausible / Umami snippet
the operator pastes in." An HTML-comment slot at the bottom of
#landingPagemarks the exact insertion point so the snippet doesn't leak into the dashboard. Tracked indocs/maintainer-todo.mdunder §3 Plausible / Umami. - Tests: new supertest case in
tests/api.test.jsasserts the landing copy is wired (tagline, CTAs, comparison table headers, FAQ questions, footer email). 74 → 75 tests; security audit clean. - Asset placeholders (maintainer-side): the hero screenshot above
the fold is an HTML-comment block expecting
<img class="landing -hero-shot">; the Plausible/Umami slot is a similar comment near the bottom. Both land indocs/maintainer-todo.md.
- Today: 13 manual portal steps. Hard wall for non-IT users.
- Goal: 3-5 steps OR one Bicep one-click.
- Ship a
deploy/azure-app-registration.bicepthat creates the app registration, sets redirect URI, generates secret, optionally assigns Reader + Log Analytics Reader roles. - README install path becomes:
az deployment sub create -f deploy/azure-app-registration.bicep \ -p redirectUri=... docker run -p 3000:3000 -e AZURE_CLIENT_ID=... keren-analytics
- Keep the long manual guide as a fallback section.
- Shipped: went with a single bash script (
deploy/azure-app-registration.sh) rather than Bicep — the Microsoft.Graph Bicep extension can't surface a client-secret value, so a pure-IaC path would still need an out-of-band step. The script is idempotent (re-runs reuse the app and append a fresh secret), GNU/BSD-portable, and prints the exact env vars to paste. README install path now readsaz login→ run script →docker compose up -d.docs/setup-entra-id.mdkeeps the manual portal flow as a fallback for tenants where CLI app-registration is restricted.
- Implement the alias table + regex patterns for userId / sessionId /
pagePath / referrer in
src/core/mapping.js. - Add
matchTypefield (builtin|alias|pattern) to mapping output. - Cross-table consistency bonus.
- 4-6 unit tests covering the new resolution chain.
- Why it's a blocker: the current mapping is exact-match-only. Half of
HN visitors trying their own tenant will see "no userId mapping" because
they used
uidorvisitorId. That's a launch-killing first impression. - Shipped:
ALIASEStable + regex per canonical field, cross-table consistency boost (confidence: highon 2+ tables),matchType/matchedKey/tablesSeenexposed on each canonical mapping, custom keys sanitized before injection,allowedKqlExpressions(mapping)extends the renderer whitelist with alias-derived exprs. +11 tests.
- On the demo URL only (mock mode), surface a "What we found" panel that
reads like the LLM output described in
ai-environment-analysis.md. - Canned response, no actual LLM call. Cost: 0 €.
- Tagline appears in screenshots and the hero GIF: "AI explains what your telemetry looks like."
- Real LLM integration ships post-launch (
ai-setup-wizard.mdproper). - Shipped:
src/core/narration.js— deterministic generator that composes a 3-4 sentence "Environment analysis" paragraph from the dashboard payload (visitors, sessions, top campaign source, peak hour, error-rate band, userId mapping type). Wired intobuildOverviewDashboardso the payload now carriesnarration: { headline, paragraph, badge, tagline, mode }. Frontend renders it in a new panel above the KPIs on the Marketing tab (public/index.html→#narrationPanel,public/app.js→renderNarration,public/styles.css→.narration-*). - Honesty tweak vs original spec: rather than a fully canned string,
the same generator runs in both modes — mock mode shows it without
badge, real mode shows it with a "Preview — real LLM coming soon"
badge. The numbers come from the dashboard the user already sees,
so nothing is invented; the "AI explains" tagline is honest because
the panel does interpret the data, just deterministically. When
Azure OpenAI integration ships post-launch, the same payload shape
is what the frontend consumes — only the
paragraphgets richer. - Tests: 10 unit tests in
tests/narration.test.js(mode toggle, badge presence, KPI presence in paragraph, peak-hour formatting, error-rate threshold branch, mapping-type branches, empty-data fallback, no template-token leakage, length bounds). Plus the api integration test assertsdashboard.narration.mode === "mock"end to end. 56 → 66 tests. - Maintainer-side: screenshots for the launch should include the
Marketing tab with this panel visible (above the fold). Tracked in
docs/maintainer-todo.mdunder the press-kit / hero-GIF items.
- Compose a deterministic banner from existing readiness + mapping data: "Your environment scores 68/100. Two quick wins available: [Add user identity (+15)] [Capture browser timings (+8)]".
- Click on a quick win → scroll to the matching prompt card.
- No LLM needed for v1; the AI version is post-launch.
- Shipped: server-side,
computeReadinessScorenow returns aquickWinsarray (top 3 unavailable signals by points;src/core/readinessScore.js). Frontend renders an<aside id="firstRunBanner">at the top of#dashboardPanel— visible across all three tabs because it sits above the tab-toolbar. Layout: a left-side score chip (68/100in accent color), a title ("Your environment scores 68/100. 2 quick wins available:"), and pill-shaped buttons for each quick win (User Identity (+15)). Clicking a chip switches to the Readiness tab via the existingactivateTab("readiness")helper, scrolls smooth-into-view to#signal-row-${signal}(each score-row wrapper now carries that stable id), and fires a 1.6s background flash to draw the eye. - Dismiss + persistence:
×button writeseaa.firstRunBanner.dismissed.v1=1tolocalStorage. The banner also auto-hides when there are no quick wins (perfect score) or when readinessScore is null/empty. No DB needed (consistent with the in-memory metadataStore). - Tests: +3 unit tests in
tests/readinessScore.test.js(quickWins shape, perfect-score returns empty, null-report returns empty). 66 → 69 tests, audit clean. UI verification (banner visible- chip click + dismiss persistence) is the maintainer's eyeball
pass —
docs/maintainer-todo.mdalready has the "open dev server and check the dashboard" item.
- chip click + dismiss persistence) is the maintainer's eyeball
pass —
- Limited scope: only the 3 most prominent KPI tiles get a delta vs. previous period.
- KQL templates accept a
compareTo: previousparameter. - UI: small green/red delta chip + "vs last week" caption.
- Full comparison + deployment markers ship post-launch.
- Shipped server side: new
previousTimeRange(timeRange)helper incore/timeRange.jsmaps the launch ranges to their predecessors (today → yesterday,7d → prev7d,30d → prev30d) andnullfor anything else (custom range hides the chips). Newkql/previous-kpis.kqltemplate runs a singlesummarizeover the prior window returning all three KPIs (uniqueVisitors,sessions,pageViews = count()) — keeps query count to +1 instead of +3.core/dashboard.jsruns that query when a predecessor exists, thendeltaEntry()builds{ current, previous, deltaPct, direction }per KPI with a0.5%neutral band and an explicitnulldeltaPct when previous is zero (so a first-time tenant doesn't see misleading +∞%). Final payload:dashboard.kpis.comparison = { previousRangeKey, label, uniqueVisitors, sessions, pageViews }ornull. Cache key uses the prev range key, so previous results live in their own cache slot. - Shipped UI: the 3 KPI tiles on the Marketing tab gain a
.kpi-meta-rowwith a colored.kpi-deltapill (+13.6%green up,-x%red down,~0%neutral grey) and a.kpi-comparecaption ("vs last week" / "vs yesterday" / "vs last month"). Hidden when comparison is null. No new dependency. - Mock data:
src/azure/mockData.jsgainsRANGE_SCALEentries foryesterday: 0.06,prev7d: 0.22,prev30d: 0.88(slightly lower than current so the demo screenshots show the screenshot- friendly green positive deltas) plus apreviousKpisquery handler. - Tests: new
tests/timeRange.test.js(5 cases — predecessor key, window length, today→yesterday, custom/null/unknown returns null, comparisonLabel mapping).tests/api.test.jsend-to-end assertsdashboard.kpis.comparison.previousRangeKey === "prev7d"with all three KPIs and a validdirection. 69 → 74 tests, audit clean. - Out of scope (post-launch, per spec): the deployment-markers layer and full per-chart comparisons stay deferred.
- Server-side render a PNG of the current dashboard view using the existing state. Stores nothing.
- One button on every dashboard tab.
- HN demographic loves "screenshot for Slack" workflows. Cheap viral lever.
- Audit
src/azure/mockData.js: every chart on every tab must have a visually interesting story. - Ensure: visible weekly seasonality, one anomaly day, one slow endpoint, meaningful geo distribution, varied browser mix.
- The demo is also the launch screenshot source — boring data = boring launch.
- Shipped: visitor traffic spike 4 days ago (×2.4, deterministic so
it lands in both 7d and 30d windows, marked
anomaly: "traffic_spike"),/api/checkoutis the unmistakable slow + error outlier (p99 ~6s, 8.2% error rate), 12-country geo distribution with Spain/Italy added, browser mix expanded to 6 entries (Mobile Safari + Samsung Internet) and OS to 5 (Android added), KPI sparklines have a clearer upward trend on visitors/sessions and a subtle improving-perf trend on response time alongside the existing trailing error spike, lunch dip on hourly trend.
- Verify no token /
code_verifier/ session secret is logged anywhere (grep + manual review ofsrc/server.js,src/azure/realClient.js,src/azure/tokenStore.js). - Run
npm auditand address criticals. - Add a
SECURITY.mdwith reporting policy. - CSP review: minimize
unsafe-inline, justify CDN allowlist. - Make the "no raw data leaves your tenant" claim provable: link to the exact lines in the code from the README/landing FAQ.
- Shipped: turned the audit into a repeatable check
(
scripts/security-audit.mjs,npm run audit:security) with 7 encoded controls (sensitive logging, session hardening, CSP, raw telemetry persistence, committed env files, npm audit). GitHub Actions workflow runs it on push/PR plus a Monday cron, so newly disclosed transitive vulnerabilities turn the badge red even without commits. README gains the green badge;SECURITY.mddocuments the reporting policy, the audited posture, and the two legitimate fs sinks (core/audit.jsmetadata events,core/metadataStore.jssetup-state JSON — no raw rows). One real finding fixed in flight: bumped transitivepath-to-regexp8.3.0 → 8.4.2 andqs6.14.1 → 6.15.1 vianpm audit fix(no breaking changes, tests still 43/43).
- About / topics / website URL / description filled in.
- Issue templates (bug, feature request, question) in
.github/. CONTRIBUTING.md(short — link to docs).CODE_OF_CONDUCT.md(Contributor Covenant).SECURITY.md(pointer to email).- Pin the v0.1.0 release with proper notes.
- Add badges: license, CI status (if CI is set up — see C3), Docker pull count.
- Shipped:
LICENSE(MIT, copyright Lionel Garnier and contributors) withpackage.jsonaligned ("license": "MIT", author populated).CONTRIBUTING.mdis short and points atCLAUDE.mdfor invariants and at the per-track backlog for what's deliberately deferred.CODE_OF_CONDUCT.mdadopts Contributor Covenant 2.1 by URL reference (avoids inlining the canonical text; reporting goes to the same email asSECURITY.md)..github/ISSUE_TEMPLATE/ships three structured forms (bug / feature / question) plusconfig.ymlthat disables blank issues and surfaces the security reporting path. A short.github/PULL_REQUEST_TEMPLATE.mdreminds contributors to readCLAUDE.mdand runnpm test+npm run audit:security. README gains license + Node-version badges next to the existing security badge.SECURITY.mdupdated with the real contact email and the closed-as-fixed gaps removed (SESSION_SECRETfail-loud + rate limiting). One adjacent fix carried in flight:src/config.jsnow throws whenNODE_ENV=productionandSESSION_SECRETis missing or a known placeholder, with 5 new tests intests/config.test.js. - Deferred (Settings-side, not file-tracked): About / topics / website URL / description on the GitHub repo page; pinning the v0.1.0 release. These need the maintainer to click through GitHub Settings; nothing to commit.
- Deferred (depends on other tracks): the Docker-pull-count badge
needs a published Docker image (Track A6 territory); the CI-status
badge needs the
npm testworkflow from C3.
- GitHub Actions: run
npm install+npm teston push and PR. - Status badge in README.
- Without this, "MIT-licensed" feels less production. Cheap signal.
- Shipped:
.github/workflows/tests.ymlrunsnpm ci+npm teston push and PR tomain(alsoworkflow_dispatchfor manual reruns) on Node 22 with the npm cache. Status badge added to the README right next to the existing security-audit badge. The redundantnpm teststep insidesecurity-audit.ymlis left in place as a belt-and-braces sanity check (the audit asserts the tests still pass before declaring the security posture green); both workflows are independent so a failing test does not red-badge the audit and vice versa, but a regression that breaksnpm testis now surfaced under the unambiguous "Tests" name rather than buried under "Security audit".
- Document v0.1.0 launch contents.
- Future commits append.
- Shipped:
CHANGELOG.mdfollows Keep-a-Changelog 1.1.0 with two sections:[Unreleased]for what's landed since v0.1.0 (the C1-C4 trust track + LICENSE/CoC/templates/CI badge), and[0.1.0]for the initial public release grouped as Added (product), Added (distribution), Security, Documentation, and Tests. Doubles as the v0.1.0 release-notes draft when the maintainer pins the release in GitHub Settings (seedocs/maintainer-todo.md§2).
- Title format: "Show HN: Keren Analytics — 2-min Azure App Insights dashboards (open source, AI-mapped)".
- Body: 4-6 paragraphs:
- What it is (one paragraph).
- Why I built it — concrete pain point with Azure portal.
- How the AI angle works (Layer 1 alias + Layer 2 narration).
- What's open and what's deliberately left for later.
- Tech stack one-liner.
- Ask for feedback.
- Pre-write the first founder-comment that addresses the predictable "but Azure portal already does X" objection.
- Three different angles, one per subreddit. Same product, framed for the
audience:
- r/azure: "I built an OSS alternative to portal analytics — feedback?"
- r/devops: focus on the readiness score + LLM-suggested instrumentation.
- r/selfhosted: focus on the Docker one-liner + privacy posture.
- Avoid "look at my project" framing. Lead with a concrete thing the user cares about.
- Long-form (1500-2500 words) on the technical deep dive most likely to catch HN: "Building a natural-language-to-KQL layer for Azure logs (without Azure OpenAI as a hard dep)".
- Cross-post to dev.to + Hashnode + personal blog.
- 10-15 named contacts: Microsoft MVPs in Azure data/devops space, MS DevRel folks, well-known Azure newsletter authors, maintainers of Awesome-Azure lists.
- Personalized 3-line DM template ready to send post-launch (not before).
- LinkedIn for non-engineers, Twitter/X for engineers, email as fallback.
- Plausible / Umami self-hosted on the demo URL.
- Track: source (HN / Reddit / Twitter), conversion to GitHub stars, demo click-through rate, time on page.
- Real-time dashboard the founder can watch during launch day.
- One PDF / page with: logo (SVG + PNG), tagline, 280-char pitch, 1500-char pitch, 3 product screenshots, founder photo + bio.
- Hosted at
/presson the demo URL. - Send proactively to MVPs / newsletter authors who pick up the post.
- Per-IP rate limit on the demo (60 req/min hard cap).
- Friendly "high traffic, try in a few minutes" page rather than 502/504.
- Hard daily cap on LLM cost (when B2 ships): cut over to canned responses past €10/day on the demo.
- Shipped:
src/core/rateLimit.js— minimal in-memory fixed-window per-IP limiter, no new dep (CLAUDE.md mandates a minimal dep set, demo is single-instance). Wired insrc/server.jsas two named buckets:api(60 req/min, all dynamic routes) andauth(stricter 20 req/min, on/auth/*so OAuth burning can't also DoS the dashboard). Both bypassNODE_ENV=test. Friendly 429 page (HTML or JSON depending onAccept) plusRetry-After,X-RateLimit-Limit, andX-RateLimit-Remainingheaders. +8 unit tests. - Deferred (with B2): the daily LLM cost cap. Lives in the same file when the mock-LLM narration ships — needs a separate counter keyed by day and a kill-switch flipping the demo to canned responses past the cap. No mock-LLM = nothing to cap yet.
- Pre-cache the mock dashboard for the demo dataset.
- All static assets behind Cloudflare with long max-age.
- BetterStack free tier or self-hosted.
- Linked from the demo footer.
- During an outage, "we're aware" beats silence by a wide margin.
- Single doc: how to roll back the demo, how to rate-limit harder, who to contact at Render/Cloudflare, where the LLM kill switch is.
- Founder reads it the morning of launch. Saves hours under stress.
- Shipped:
docs/launch-day-runbook.md— single page intentionally short and copy-pasteable: T-2h pre-launch checklist (gated on the same "ready to launch" definition this file has), what to watch during the window (Plausible / GitHub stars / Render / demo URL), triage trees for "it's slow / it's down" and "someone found a security issue", how to rate-limit harder (specific edits tosrc/server.js:83-98), rollback recipe (revert + push, never force-push tomain), HN/Reddit moderation playbook, and a contacts section. The LLM kill-switch section is stubbed with a pointer to where it'll live when B2 ships.
Added 2026-05-11 after ADR 0005. The "AI-mapped schema / AI explains your telemetry" claims were AI-washing — no LLM was wired. This track closes that gap before launch by shipping a real audit + AI mapping + recommendations flow on Azure AI Foundry, persisted in SQLite. The other AI surfaces (
ai-natural-language-queries.md,ai-instrumentation-assistant.md) stay post-launch. Seedocs/adr/0005-ai-first-scope.md.
- Install
better-sqlite3(sync, simple) or use Node 22.5+node:sqlitenative module. ADR 0005 § Decision 2. - Schema:
tenants,scans,mappings,signals,validations— with per-tenant scope and timestamps. File atdata/keren.db. - Rewrite
src/core/metadataStore.jsto use the SQLite schema. Keep the same exported interface (getTenant,updateTenant,logStateTransition) so callers don't change. - Migration script: if
data/store.jsonexists at boot, import it into the SQLite schema, then rename tostore.json.legacy(don't delete). - Backup policy: hourly
VACUUM INTO-based snapshot todata/backups/(cap 24 snapshots). Production-side: Azure Blob upload via Container Apps Jobs cron (separate task, F1 ships the script). - Tests: existing 5 metadataStore tests must keep passing; add 3 new tests for migration path + concurrent transactions.
- Extend
src/core/schemaProfile.js→ newsrc/core/schemaScan.js. - Capture per-tenant : event volumes by
name, top-NcustomDimensionskeys with cardinality + sample values (PII-scrubbed via regexemail|phone|ssn), timestamps span, gaps detected (e.g. nouserId-like field present, no campaign tracking, no session duration). - Persist scan output as a JSON document in
scanstable, indexed bytenantId + scannedAt. Latest scan is the active one; history kept. - Cap on scan KQL: use existing
queryTimeoutMs(12s), summary queries only (notake 10000). Total ≤ 5-6 queries per scan. - Tests: schema parse roundtrip, PII scrub coverage, gap detection heuristics.
- New
src/ai/azureFoundry.jsprovider implementing the contract indocs/architecture-ai.md. Authenticate via the existing Managed Identity (no API keys). Endpoint + deployment name in env vars (AZURE_FOUNDRY_ENDPOINT,AZURE_FOUNDRY_DEPLOYMENT). - Prompt structuré (JSON schema response) that takes a F2 scan as input
and returns:
mapping_proposals[](canonical field → tenant column, with confidence + explanation),missing_signals[](signal name + recommended KQL to instrument),dashboard_recommendations(which charts to show prominently given what's available). - Quota guard: in-memory daily counter; hard cap at 10 €/day worth
of tokens (computed from
gpt-4o-minipricing); when exceeded, return a deterministic fallback (existing alias/regex mapping + empty recommendations) plus adegraded: trueflag the UI surfaces. - Cache scan→AI-output in SQLite (so a re-load doesn't re-spend tokens). Invalidate on re-scan.
- Mode
AI_PROVIDER=nonemust continue to work (deterministic-only output, no LLM call) — tests run in this mode. - Bicep update: extend
infra/main.bicepwith Azure AI Foundry Hub + Project + connection + model deployment + role assignment (Cognitive Services Userto the existing MI). - Tests: provider contract test, JSON schema validation, fallback trigger on quota exceeded, cache hit/miss.
- New route
/setup(or extend existing post-OAuth flow). Multi-step:- Scanning — spinner + live narration ("Reading custom dimensions… Found 47 event types… Detecting user identity…").
- AI findings — proposed mappings with confidence badges, missing signals with recommended KQL (copy button), dashboard recommendations.
- Validate/edit — user can accept all, override individual
mappings, dismiss recommendations. Persists to
validationstable. - Save & continue — redirects to the dashboard with the validated mapping active.
- Re-scan button in settings ("Found new event types? Re-scan now").
- Empty-state handling: tenant with no events yet → wizard explains what to instrument and waits.
- Tests: end-to-end supertest covering the 4-step flow + override path.
- Update
docs/backlog/ai-setup-wizard.mdstatus from "post-launch optional" to "pre-launch, see Track F". Add the concrete F1-F4 scope (replace speculative sections). - Update
docs/backlog/ai-environment-analysis.mdsimilarly — Layer 1 alias is done (B1), Layer 2 LLM ships as part of F3. - Update
docs/architecture-ai.mdstatus from DRAFT to ACCEPTED, with theazure-foundryprovider as the canonical implementation. - Remove the
Preview — real LLM coming soonbadge fromnarration.jsgenerator output whenAI_PROVIDER=azure-foundryand the call succeeds.
F1 (SQLite) ─┬─→ F2 (scan) ─→ F3 (AI mapping) ─→ F4 (wizard UI)
│ ↑
└───────────────────────┘
(cache + persistence)
F5 in parallel once F1-F4 are conceptually clear (~day 3 of Track F).
Maintainer dependency : Azure AI Foundry workspace + gpt-4o-mini
deployment must exist before F3 can run end-to-end. See
docs/maintainer-todo.md § "Provisionner Azure
AI Foundry".
| Track | Blockers | Strong | Optional | Total |
|---|---|---|---|---|
| A — Public surface | 23h | 5h | 0h | 28h |
| B — Product polish | 15h | 24h | 4h | 43h |
| C — Trust signals | 7h | 3h | 1h | 11h |
| D — Distribution prep | 9h | 4h | 2h | 15h |
| E — Anti-fragility | 3h | 3h | 1h | 7h |
| F — AI-first scope (added 2026-05-11) | 88-120h | 8-16h | 0h | 96-136h |
| Total blockers (must ship) | 145-177h | |||
| Total with strong items | 192-232h | |||
| Total all-in | 200-240h |
Realistic launch timeline with all blockers + most strong items: ~5-6 weeks at ~40h/week focused. Most of Phase A blockers already shipped (infra, custom domain, CI/CD, GitHub polish, release v0.1.0). Remaining heavy lift is Track F (AI setup wizard, ~15 jours focus) plus the content/asset drafts in Track D and the Hero GIF / OG image in Track A.
Week 1 — product + content
- Day 1-2: B1 (alias heuristics) + B6 (demo dataset polish).
- Day 3: A6 (Bicep) + C1 (security pass).
- Day 4: A1 (demo URL stand-up) + A4 (OG image).
- Day 5: A2 (README rewrite) + A3 (hero GIF).
Week 2 — polish + launch prep
- Day 6: B2 (mock LLM narration) + B3 (first-run banner).
- Day 7: B4 (period comparison) + B6 polish round 2.
- Day 8: A5 (landing page) + C2 (GitHub repo polish) + C3 (CI).
- Day 9: D1-D5 (all distribution prep).
- Day 10: Soft test in 1-2 small communities. Fix anything broken.
Day 11 — launch.
All [BLOCKER] items shipped. Three independent strangers can:
- Open the demo URL on a phone and understand the product within 30s.
- Run the install command on a fresh machine and reach the dashboard.
- Read the README and articulate the security posture without asking questions.
If any of these fails on launch eve, delay the launch by one week. A blown launch is much worse than a 1-week delay.
These exist in the broader backlog and explicitly do not block launch:
- Hosted multi-tenant SaaS (Phase 3).
- Postgres / Redis persistence (Phase 3).
- Full LLM-powered setup wizard (
ai-setup-wizard.mdLayer 3+). - Natural-language query explorer (
ai-natural-language-queries.md). - AI instrumentation assistant (
ai-instrumentation-assistant.md). - Custom event funnels and full conversion tab.
- Multi-resource aggregation.
- Goals + weekly digest.
- AWS / GCP connectors (Phase 4).
These are post-traction features. Building them pre-launch is the single most common way solo founders waste their launch window.