This commit is contained in:
92
docs/addiction-features/01-memory-streak.md
Normal file
92
docs/addiction-features/01-memory-streak.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# 01 — Memory Streak Counter
|
||||
|
||||
**Author:** CC (Block B design pass)
|
||||
**Date:** 2026-05-01
|
||||
**Status:** AWAITING_RATIFICATION
|
||||
**Estimate:** ~180 LOC + ~3-4h wall-clock
|
||||
**Touches:** apps/web (3 files), packages/server (1 route), packages/core (1 store)
|
||||
|
||||
---
|
||||
|
||||
## User story
|
||||
|
||||
As a user, when I save a memory or have a meaningful chat session, I want to see a visible streak counter (🔥 5 days in a row) somewhere I'll glance at often, so I'm reinforced to come back tomorrow.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
1. Streak chip renders in the desktop StatusBar (bottom-right cluster), wrapped in a HintTooltip explaining the rules.
|
||||
2. Streak count = consecutive days with at least one memory frame committed (any source: chat, harvest, manual).
|
||||
3. Streak resets if 24h pass with zero new frames AND zero qualifying activity (configurable). Default: pure 24h gap = reset.
|
||||
4. Optional "weekend skip" toggle in Settings → Behavior → "Streaks count weekdays only" (default OFF; international users decide).
|
||||
5. Visible cold-start path: streak=0 day 1 hides chip; streak=1 shows "🔥 1 day"; streak=N shows "🔥 N days" with subtle pulse animation when count increments live.
|
||||
6. Live increment fires when a new frame lands today and `streak.lastBumpAt` was a previous day — no full-day debounce, but client throttles repeat re-renders to once/min.
|
||||
|
||||
## UI sketch
|
||||
|
||||
```
|
||||
StatusBar bottom-right (existing cluster):
|
||||
[memory icon] 12 mem [chat icon] 5 sessions 🔥 5 days [time]
|
||||
|
||||
Hover tooltip:
|
||||
Memory streak: 5 days
|
||||
Save at least one memory each day to keep it going.
|
||||
(Settings → Behavior to skip weekends.)
|
||||
```
|
||||
|
||||
Day-1 user: chip suppressed entirely (no shame for a 0-streak); appears at streak=1 onwards.
|
||||
|
||||
Streak break: chip flashes amber for 4 hours after reset, copy reads "Streak broken — start fresh today" with action `Got it`.
|
||||
|
||||
## Data model
|
||||
|
||||
New table `streaks` in personal mind:
|
||||
```
|
||||
id INTEGER PRIMARY KEY,
|
||||
streak_kind TEXT NOT NULL CHECK (streak_kind IN ('memory','chat')),
|
||||
current_count INTEGER NOT NULL DEFAULT 0,
|
||||
last_bump_at TEXT NOT NULL, -- ISO date YYYY-MM-DD (no time)
|
||||
longest_count INTEGER NOT NULL DEFAULT 0,
|
||||
weekend_skip INTEGER NOT NULL DEFAULT 0,
|
||||
updated_at TEXT NOT NULL
|
||||
```
|
||||
|
||||
One row per `streak_kind` per personal mind. v1 ships only `memory` kind; `chat` reserved.
|
||||
|
||||
Server route: `GET /api/streaks` → `{ memory: {current, longest, lastBumpAt, weekendSkip} }`. `POST /api/streaks/bump` (called by frame-store on every frame insert) — server-side computes current_count by checking lastBumpAt vs today.
|
||||
|
||||
## Implementation notes
|
||||
|
||||
- Bump trigger: hook into `FrameStore.createIFrame()` callsite OR via a SQLite trigger on `INSERT ON memory_frames`. Trigger is cleaner — no orchestrator changes needed.
|
||||
- Reset detection: pure read-time computation. When client fetches `/api/streaks`, server compares lastBumpAt to today; if gap > 1 day (or > 1 weekday with weekendSkip), reset current to 0 before returning.
|
||||
- StatusBar wiring: existing `agentStatus`-style hook → new `useStreaks()` hook polling `/api/streaks` every 2 min + on `waggle:frame-saved` event.
|
||||
|
||||
## Estimate
|
||||
|
||||
- Server route + SQLite migration: ~50 LOC
|
||||
- `useStreaks()` hook + StatusBar render: ~60 LOC
|
||||
- Settings toggle: ~30 LOC
|
||||
- Tests (server bump logic, reset boundary, weekend-skip math): ~40 LOC
|
||||
- **Total ~180 LOC, ~3-4h with verification.**
|
||||
|
||||
## Risks + open questions
|
||||
|
||||
1. **Timezone** — bump uses server's local TZ (`new Date().toISOString().slice(0,10)`). Travelers crossing dates lose/gain a day. Acceptable v1; document.
|
||||
2. **What counts as a frame?** — currently any frame insert. PM may want to exclude `temporary` importance from bumps. Default: count all non-`deprecated` frames. Open question.
|
||||
3. **Streak breakage notification** — silent reset, or a one-time toast "Streak broken — yesterday you missed it"? PM call.
|
||||
4. **Cosmetic** — emoji 🔥 may clash with Hive DS aesthetic. Alternative: `bg-amber-500` flame icon from lucide-react. PM call.
|
||||
5. **Migration risk** — adds new SQLite table. Use migration framework. Idempotent CREATE TABLE IF NOT EXISTS.
|
||||
|
||||
## Out of scope (v1)
|
||||
|
||||
- Per-workspace streaks (just personal-mind global v1).
|
||||
- Calendar heatmap (GitHub-style activity grid).
|
||||
- Streak leaderboards across team. (TEAMS tier only later.)
|
||||
- Streak freeze / "streak protector" purchases. (Anti-pattern in Waggle DS — no buyable shortcuts.)
|
||||
|
||||
## PM decisions needed
|
||||
|
||||
- [ ] GO / MODIFY / SKIP
|
||||
- [ ] Frame inclusion rule (all / non-temporary / >threshold importance)
|
||||
- [ ] Weekend-skip default (OFF / ON / detect locale)
|
||||
- [ ] Reset notification (silent / toast / amber flash)
|
||||
- [ ] Emoji vs Lucide icon
|
||||
118
docs/addiction-features/02-daily-brief.md
Normal file
118
docs/addiction-features/02-daily-brief.md
Normal file
@@ -0,0 +1,118 @@
|
||||
# 02 — Daily Brief Notification
|
||||
|
||||
**Author:** CC (Block B design pass)
|
||||
**Date:** 2026-05-01
|
||||
**Status:** AWAITING_RATIFICATION
|
||||
**Estimate:** ~280 LOC + ~5-6h wall-clock
|
||||
**Touches:** apps/web (2 files), packages/server (1 route + 1 cron job), packages/core (1 generator), Tauri capabilities (notification permission)
|
||||
|
||||
---
|
||||
|
||||
## User story
|
||||
|
||||
As a user, every morning I want a 1-2 sentence brief summarising what I discussed yesterday, what I decided, and what's worth revisiting today, delivered as either an in-app banner or a system notification — so I never lose track of work I did 24 hours ago.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
1. At a configurable hour (default 09:00 local), a "Daily Brief" notification fires for the user.
|
||||
2. Brief content: 1-line summary of yesterday's activity (+ count of frames saved + decisions made), 1-line "today suggestion" derived from open tasks / pending follow-ups in workspace state.
|
||||
3. Two delivery channels:
|
||||
- **In-app banner**: top-right toast on first desktop mount of the day, dismissible.
|
||||
- **System notification** (Tauri): native Win/Mac notification with click-to-focus action. Opt-in per-platform.
|
||||
4. Configurable in Settings → Notifications: on/off toggle, hour-of-day picker, channel selection.
|
||||
5. Skips when user has no qualifying activity (yesterday frame count = 0); brief replaced with weekly digest pointer or suppressed entirely.
|
||||
6. One brief per day max — server-side dedupe on `(user_id, date)` so reopening the app doesn't re-fire.
|
||||
|
||||
## UI sketch
|
||||
|
||||
```
|
||||
In-app banner (top-right, 8s fade-out unless hovered):
|
||||
[Brain icon] Yesterday's brief
|
||||
You discussed: API rate limiting, Q3 roadmap. Decided: ship migrations
|
||||
Wednesday. Today's suggestion: review the auth-rewrite blocker.
|
||||
[Read more] [✕]
|
||||
|
||||
System notification (Tauri):
|
||||
Title: Waggle — Daily Brief
|
||||
Body: Yesterday: 12 memories, 3 decisions. Today: review auth blocker.
|
||||
Action: Open app → focuses Chat / Memory tab
|
||||
```
|
||||
|
||||
## Data model
|
||||
|
||||
New table `daily_briefs`:
|
||||
```
|
||||
id INTEGER PRIMARY KEY,
|
||||
brief_date TEXT NOT NULL UNIQUE, -- 'YYYY-MM-DD'
|
||||
content TEXT NOT NULL, -- 1-2 sentence summary
|
||||
yesterday_frame_count INTEGER,
|
||||
yesterday_decision_count INTEGER,
|
||||
today_suggestions TEXT, -- JSON array
|
||||
delivered_at TEXT, -- ISO timestamp; null until shown
|
||||
delivered_via TEXT, -- 'banner' | 'system' | both
|
||||
created_at TEXT NOT NULL
|
||||
```
|
||||
|
||||
Settings additions (existing `settings.json`):
|
||||
```
|
||||
dailyBrief: {
|
||||
enabled: boolean, // default true
|
||||
hour: number, // default 9
|
||||
channels: { banner: bool, system: bool } // default { banner: true, system: false }
|
||||
}
|
||||
```
|
||||
|
||||
## Server-side generator
|
||||
|
||||
Cron job runs daily at configured hour (per-user; v1 single global hour) — generates brief by:
|
||||
1. Querying yesterday's frames (`created_at >= startOfYesterday AND < startOfToday`).
|
||||
2. Extracting decisions (importance=critical OR content matches "Decision X").
|
||||
3. Pulling open progress items / blockers from workspace-state.
|
||||
4. Sending to LLM with prompt: "Summarise in 1-2 sentences. Be terse. List top decision."
|
||||
5. Storing in `daily_briefs` table with `delivered_at=null`.
|
||||
|
||||
Client polls `/api/daily-brief/today` on desktop mount. If row exists with `delivered_at=null`, render banner + (if Tauri & user opted in) fire system notification, then `POST /api/daily-brief/today/ack` to set `delivered_at`.
|
||||
|
||||
## Implementation notes
|
||||
|
||||
- Cron infrastructure already exists (`cronStore` + `CronScheduleLike` in workspace-context.ts).
|
||||
- LLM cost: 1 call/day × ~500 tokens output ≈ $0.005/day on Sonnet. Acceptable.
|
||||
- Dedupe: server enforces `UNIQUE(brief_date)`. Client never generates locally.
|
||||
- System notification: Tauri `notification` capability — already requestable; needs `tauri.conf.json` allowlist update.
|
||||
- Skip for fresh users: cron job pre-checks `yesterday_frame_count > 0`; if 0 and totalFrames=0 (cold start), skips entire brief.
|
||||
|
||||
## Estimate
|
||||
|
||||
- Server route (`/api/daily-brief/today`) + ack endpoint: ~60 LOC
|
||||
- Cron job + LLM generator: ~80 LOC
|
||||
- Settings tab additions: ~50 LOC
|
||||
- DailyBriefBanner component: ~60 LOC
|
||||
- Tauri notification wiring: ~30 LOC
|
||||
- Tests (cron logic, brief generation, dedupe): ~50 LOC
|
||||
- **Total ~330 LOC including tests, ~5-6h with verification.**
|
||||
|
||||
## Risks + open questions
|
||||
|
||||
1. **Hour-picker timezone** — server cron runs in server-local TZ; users on other timezones see briefs at wrong hour. v1: keep server-local; document. v2: per-user TZ.
|
||||
2. **LLM cost** — $0.005/day × N users = $0.15/user/month. Negligible for now but tracks.
|
||||
3. **Empty days** — first 7 days of new user have no yesterday. Either skip silently or use "Welcome" copy. Open question.
|
||||
4. **System notification permission** — Tauri requires explicit allowlist + user permission grant. v1 fallback to banner-only when permission denied.
|
||||
5. **Generator failure** — LLM down → no brief that day. Acceptable; user just sees yesterday's content next day. Don't retry within a day.
|
||||
6. **Cross-device** — user has multiple devices; brief generated server-side once, both devices show it. Already handled by server-side dedupe.
|
||||
|
||||
## Out of scope (v1)
|
||||
|
||||
- Multiple briefs per day (morning + evening) — wait for usage data.
|
||||
- Personalised tone / persona-flavored briefs — just a plain factual summary v1.
|
||||
- "Snooze brief" controls — just dismiss or off.
|
||||
- Email delivery — Slack-style integrations later.
|
||||
- Per-workspace briefs — global personal brief v1.
|
||||
|
||||
## PM decisions needed
|
||||
|
||||
- [ ] GO / MODIFY / SKIP
|
||||
- [ ] Default delivery channel(s) — banner, system, or both
|
||||
- [ ] Default hour (09:00 local? user-configurable on first run?)
|
||||
- [ ] Empty-day behavior (skip / welcome copy / encourage activity)
|
||||
- [ ] Brief tone (factual / encouraging / persona-flavored)
|
||||
- [ ] Generator model (Sonnet for cost / Haiku for speed)
|
||||
87
docs/addiction-features/03-continuity-banner.md
Normal file
87
docs/addiction-features/03-continuity-banner.md
Normal file
@@ -0,0 +1,87 @@
|
||||
# 03 — Continuity Moments (Auto-Resume Banner)
|
||||
|
||||
**Author:** CC (Block B design pass)
|
||||
**Date:** 2026-05-01
|
||||
**Status:** AWAITING_RATIFICATION
|
||||
**Estimate:** ~120 LOC + ~2-3h wall-clock
|
||||
**Touches:** apps/web (1 file: ChatApp.tsx OR new ContinuityBanner.tsx), packages/server (1 endpoint extension)
|
||||
|
||||
---
|
||||
|
||||
## User story
|
||||
|
||||
When I open Chat after closing it for hours/days, I want a 1-line banner reminding me where I left off — what I last decided, what's still open — and a one-click way to continue or start fresh, so I don't have to re-orient myself manually.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
1. When ChatApp mounts AND `messages.length === 0` AND last session for the workspace was within the past 7 days, render a "Picking up where you left off" banner at the top of the chat.
|
||||
2. Banner content: 1-line headline ("Yesterday you decided X") + 2 buttons: `Continue` (loads last session messages) + `Start fresh` (dismisses banner, opens empty input).
|
||||
3. Banner suppressed for fresh workspaces (sessionCount=0) and for sessions older than 7 days (handoff to LoginBriefing's domain).
|
||||
4. Dismissing banner via `Start fresh` sets a per-workspace flag `continuity:dismissed:{wsId}` so the banner doesn't re-render same session.
|
||||
5. Continuity banner replaces neither WorkspaceBriefing nor LoginBriefing — it's a third surface specific to "between-session" memory.
|
||||
|
||||
## UI sketch
|
||||
|
||||
```
|
||||
ChatApp top, above message list:
|
||||
┌──────────────────────────────────────────────────────────┐
|
||||
│ ⟳ Picking up where you left off ✕ │
|
||||
│ Yesterday you decided: ship migrations Wednesday. │
|
||||
│ 2 open follow-ups · last session 16h ago │
|
||||
│ │
|
||||
│ [ Continue conversation ] [ Start fresh ] │
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
|
||||
After click Continue: banner fades, last session messages stream into the chat.
|
||||
After click Start fresh: banner removed for this session, input focused.
|
||||
```
|
||||
|
||||
## Data model
|
||||
|
||||
No new tables. Extends existing endpoints:
|
||||
- `GET /api/workspaces/:id/context` — already returns `recentThreads[]`. Pull `recentThreads[0]` if its `lastActive` ≤ 7 days. Add `lastDecision` field (top decision content from yesterday) — drawable from existing `recentDecisions[0]`.
|
||||
- New endpoint `POST /api/sessions/:id/load` — already exists conceptually as `getHistory(workspaceId, sessionId)`. Confirm wire-up.
|
||||
|
||||
Frontend localStorage:
|
||||
```
|
||||
continuity:dismissed:{wsId} = ISO timestamp
|
||||
```
|
||||
Set on Start-fresh click. Banner suppressed for that workspace until next mount where lastActive moves forward (i.e. user has actually had new activity).
|
||||
|
||||
## Implementation notes
|
||||
|
||||
- Existing `WorkspaceBriefing` already shows `recentThreads[]` as a list. Continuity banner is a focused alternative: ONE thread, biggest decision, action-oriented buttons.
|
||||
- Decision: keep WorkspaceBriefing for the "browse" affordance (5 recent threads + memories + decisions list) and add ContinuityBanner as the "resume" affordance (1 thread, 1 click to continue). They co-exist, both above chat list, ContinuityBanner above WorkspaceBriefing.
|
||||
- Continue button: calls existing session load mechanism — `setActiveSession(threadId)` then `loadSessionHistory(threadId)`.
|
||||
- Decision extraction: `recentDecisions[0]` from workspace-context already filtered for last 24h elsewhere; reuse.
|
||||
|
||||
## Estimate
|
||||
|
||||
- ContinuityBanner component: ~70 LOC
|
||||
- ChatApp wire-up + localStorage logic: ~30 LOC
|
||||
- Server context extension (lastDecision field): ~10 LOC
|
||||
- Tests: ~30 LOC (banner render conditions, dismissal flag, time-window logic)
|
||||
- **Total ~140 LOC, ~2-3h with verification.**
|
||||
|
||||
## Risks + open questions
|
||||
|
||||
1. **Three surfaces collide** — ContinuityBanner + WorkspaceBriefing + LoginBriefing all surface "what you did last" content. Need clear visual hierarchy: LoginBriefing (cross-workspace), WorkspaceBriefing (this workspace overview), ContinuityBanner (one-click resume).
|
||||
2. **Stale banner** — user opens Chat at 2am after a 12-hour break. Banner says "Yesterday you...". Linguistic edge case (was it really yesterday?). Use existing `timeAgo()` helper.
|
||||
3. **Continue vs new session** — clicking Continue should load the OLD session's messages OR start a NEW session that references them? v1: load old session messages so user sees full context.
|
||||
4. **Multiple workspaces** — banner only fires for the active workspace. Cross-workspace continuity prompts are LoginBriefing's job.
|
||||
5. **Fresh user** — sessionCount=0 → banner suppressed. But what about a returning user with one stub workspace and no real sessions? Same: suppress.
|
||||
|
||||
## Out of scope (v1)
|
||||
|
||||
- Voice continuity ("Resume our conversation where I asked about X").
|
||||
- Multi-thread continuity (continue the most-impactful thread, not just newest).
|
||||
- Smart "you might want to follow up on X" suggestion engine. (That's the Daily Brief's domain.)
|
||||
- Cross-device continuity sync (already handled by server-side session storage; no client work needed).
|
||||
|
||||
## PM decisions needed
|
||||
|
||||
- [ ] GO / MODIFY / SKIP
|
||||
- [ ] Time window — 7 days reasonable? or shorter (3 days)? or 30 days?
|
||||
- [ ] Dismissal scope — per-session (re-show next mount) or per-day?
|
||||
- [ ] Continue button behavior — load old messages vs new session w/ context inject
|
||||
- [ ] Co-existence with WorkspaceBriefing — both above chat OR Continuity replaces Briefing for last-7-day case?
|
||||
126
docs/addiction-features/04-weekly-wins-digest.md
Normal file
126
docs/addiction-features/04-weekly-wins-digest.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# 04 — Memory Wins Digest (Weekly)
|
||||
|
||||
**Author:** CC (Block B design pass)
|
||||
**Date:** 2026-05-01
|
||||
**Status:** AWAITING_RATIFICATION
|
||||
**Estimate:** ~250 LOC + ~4-5h wall-clock
|
||||
**Touches:** apps/web (1 component + 1 tab in Memory app), packages/server (1 cron job + 1 route), packages/core (1 generator)
|
||||
|
||||
---
|
||||
|
||||
## User story
|
||||
|
||||
Once a week (Monday morning by default), I want a summary card showing how Waggle's memory paid off the prior week — N facts saved, N agent recalls that used them, estimated minutes saved on context-explaining — so I can quantify the value and feel the compounding effect.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
1. Weekly digest fires once per week (configurable day/hour, default Monday 09:00 local).
|
||||
2. Delivered as a Memory app tab card + an in-app banner on first desktop mount of the week.
|
||||
3. Card content (3 metrics + 1 narrative line):
|
||||
- Frames saved this week: N
|
||||
- Recalls that hit a saved frame: M
|
||||
- Estimated time saved (M × 9min context-restore baseline): ~T minutes
|
||||
- Narrative: "Your top theme this week: <theme>. Top decision: <decision>"
|
||||
4. Card persists in Memory app's "Wins" tab indefinitely — historical record of weekly progress, not just one-time.
|
||||
5. First-week edge case: if user has < 7 days of history, banner suppressed but Wins tab shows "Come back next week for your first digest" placeholder.
|
||||
|
||||
## UI sketch
|
||||
|
||||
```
|
||||
Memory App > Wins tab:
|
||||
┌─ Week of April 24-30 ──────────────────────────────────────┐
|
||||
│ ▲ 12 frames saved ▲ 5 agent recalls ⏱ ~45 min saved │
|
||||
│ │
|
||||
│ Top theme: API rate limiting + auth-rewrite │
|
||||
│ Top decision: Ship migrations Wednesday │
|
||||
│ │
|
||||
│ [Open Memory] [Open Last Decision Source] │
|
||||
└────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─ Week of April 17-23 ──────────────────────────────────────┐
|
||||
│ ▲ 8 frames · ▲ 3 recalls · ⏱ ~27 min saved │
|
||||
│ Top theme: Onboarding wizard polish │
|
||||
└────────────────────────────────────────────────────────────┘
|
||||
|
||||
(older weeks collapsed by default, click to expand)
|
||||
|
||||
Banner on Monday morning:
|
||||
[Trophy icon] This week saved you ~45 min — see the breakdown
|
||||
[Open Wins] [✕]
|
||||
```
|
||||
|
||||
## Data model
|
||||
|
||||
New table `weekly_wins`:
|
||||
```
|
||||
id INTEGER PRIMARY KEY,
|
||||
week_start TEXT NOT NULL UNIQUE, -- 'YYYY-MM-DD' (Monday)
|
||||
frame_count INTEGER NOT NULL,
|
||||
recall_count INTEGER NOT NULL,
|
||||
estimated_minutes_saved INTEGER NOT NULL,
|
||||
top_theme TEXT,
|
||||
top_decision TEXT,
|
||||
top_decision_source_session_id TEXT,
|
||||
generated_at TEXT NOT NULL,
|
||||
delivered_at TEXT -- nullable until banner shown
|
||||
```
|
||||
|
||||
For `recall_count`, need to instrument frame retrieval:
|
||||
- Existing `HybridSearch.search()` already returns frame IDs
|
||||
- Add `RecallEvent` log: every search/retrieval that hits a frame logs `{ frame_id, ts, source: 'agent' | 'manual' }` to a `recall_events` table
|
||||
- Aggregate weekly count per (week, agent-source-only)
|
||||
|
||||
## Server-side generator
|
||||
|
||||
Cron runs Monday 00:30 local (off-peak):
|
||||
1. Query frames where `created_at >= weekStart AND created_at < weekStart+7d`
|
||||
2. Query recall_events where `source='agent' AND ts in [weekStart, weekStart+7d]`
|
||||
3. Compute estimated_minutes_saved = recall_count × 9 (calibrated baseline; configurable)
|
||||
4. Theme extraction: LLM call ("From these N frames, what's the dominant theme in 5-10 words?")
|
||||
5. Top decision: highest-importance critical/important frame matching decision pattern from the week
|
||||
6. Insert row, set delivered_at=null, await client poll
|
||||
|
||||
Estimated minutes baseline (the "9 min context-restore"): documented derivation needed; placeholder until UX research lands.
|
||||
|
||||
## Implementation notes
|
||||
|
||||
- Recall instrumentation is the hardest part — needs hook in `HybridSearch.search()` callsite to log frame IDs returned and identifier of the consumer (agent loop vs manual UI search).
|
||||
- Weekly cron: trivial extension of cron infrastructure; runs `generateWeeklyWins(weekStart)` on schedule.
|
||||
- Wins tab in Memory app: paginate if N > 12 weeks.
|
||||
- Edge: time zones again — week boundary is server-local Monday 00:00. Document.
|
||||
|
||||
## Estimate
|
||||
|
||||
- Recall events table + instrumentation: ~70 LOC (touches HybridSearch + agent-loop)
|
||||
- Generator + cron: ~80 LOC
|
||||
- Server route `/api/weekly-wins`: ~30 LOC
|
||||
- WinsCard + WinsTab components: ~80 LOC
|
||||
- Banner + ack route: ~30 LOC
|
||||
- Tests: ~50 LOC
|
||||
- **Total ~340 LOC, ~4-5h with verification.**
|
||||
|
||||
## Risks + open questions
|
||||
|
||||
1. **Recall instrumentation** is the cost driver — need to wire `HybridSearch` to log every retrieval. Hot path; throttle/buffer logs to avoid SQLite write storms.
|
||||
2. **Estimated-minutes-saved calibration** — 9 min baseline is a guess. Run a small UX study or pilot a percentile estimate. v1: hardcode + flag for revision.
|
||||
3. **First week** — user installs Monday afternoon, what do they see Tuesday? Nothing — wait for next Monday. Banner suppressed for ~7 days.
|
||||
4. **Theme/decision LLM cost** — 1 call/week × ~700 tokens ≈ $0.01/user/week. Negligible.
|
||||
5. **Privacy** — frames may contain sensitive content; theme summary on personal mind only, not Team/shared workspaces. Hard rule: no cross-workspace digests.
|
||||
6. **What counts as a "recall"** — open question. Agent retrieval via `recall_memory` MCP tool? Hybrid search hits during chat? UI-driven Memory app search? Default: instrument all three; aggregate by source.
|
||||
|
||||
## Out of scope (v1)
|
||||
|
||||
- Comparison to prior week ("up 30% from last week") — wait until 4+ weeks of data.
|
||||
- Per-workspace digests — global personal-mind digest only v1.
|
||||
- Email digest delivery — in-app only.
|
||||
- "Share to team" affordance.
|
||||
- Streak integration ("you've maintained a 4-week digest streak"). (Streak feature is separate; wait until both exist.)
|
||||
|
||||
## PM decisions needed
|
||||
|
||||
- [ ] GO / MODIFY / SKIP
|
||||
- [ ] Default delivery day/hour (Monday 09:00 reasonable? Friday end-of-week instead?)
|
||||
- [ ] Recall sources to count (agent only / agent+UI / all)
|
||||
- [ ] Estimated-minutes baseline (9 min default, or skip the metric until calibrated?)
|
||||
- [ ] Banner vs. tab-only delivery (can banner be opt-out?)
|
||||
- [ ] Theme extraction model (Sonnet / Haiku)
|
||||
109
docs/addiction-features/05-milestone-cards.md
Normal file
109
docs/addiction-features/05-milestone-cards.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# 05 — First-Time Milestone Cards
|
||||
|
||||
**Author:** CC (Block B design pass)
|
||||
**Date:** 2026-05-01
|
||||
**Status:** AWAITING_RATIFICATION
|
||||
**Estimate:** ~150 LOC + ~2-3h wall-clock
|
||||
**Touches:** apps/web (1 new component + 1 hook), packages/server (1 endpoint extension)
|
||||
|
||||
---
|
||||
|
||||
## User story
|
||||
|
||||
When I cross meaningful memory thresholds for the first time (1st memory saved, 10th, 100th, 1000th), I want a brief celebration — confetti animation + congratulatory copy + share affordance — so I feel the compounding value and have a moment to share if I want to.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
1. Milestone card fires automatically when total personal-mind frame count crosses thresholds: 1, 10, 100, 1000.
|
||||
2. Card is full-screen overlay with confetti animation + copy + 2 buttons: `Continue working` (dismisses) + `Share` (copies preformatted text to clipboard, optional native share where available).
|
||||
3. Each milestone fires exactly once — server-side tracks which thresholds have been celebrated.
|
||||
4. Card animation runs ~3 seconds; auto-dismiss after 8s if user doesn't click.
|
||||
5. First-memory milestone (1) is the most important — sets the tone for the addictive feedback loop. Make it feel earned.
|
||||
|
||||
## UI sketch
|
||||
|
||||
```
|
||||
Full-screen overlay (z-9999, dark backdrop blur):
|
||||
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ ✨ confetti animation ✨ │
|
||||
│ │
|
||||
│ 🎯 10 │
|
||||
│ Memories saved! │
|
||||
│ │
|
||||
│ Your second brain is taking shape — every save makes │
|
||||
│ Waggle a little smarter for you. │
|
||||
│ │
|
||||
│ [Share] [Continue working] │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
|
||||
Milestone copy (per threshold):
|
||||
1 "First memory saved! Welcome to your second brain."
|
||||
10 "10 memories saved! Your second brain is taking shape."
|
||||
100 "100 memories — you're building real persistent context."
|
||||
1000 "1,000 memories. You've crossed into a different category of user."
|
||||
|
||||
Share text format:
|
||||
"Just hit {N} memories on Waggle — my second brain that remembers
|
||||
across every chat. waggle-os.ai 🐝"
|
||||
```
|
||||
|
||||
## Data model
|
||||
|
||||
New table `milestones`:
|
||||
```
|
||||
id INTEGER PRIMARY KEY,
|
||||
milestone_kind TEXT NOT NULL, -- 'frames_1', 'frames_10', 'frames_100', 'frames_1000'
|
||||
achieved_at TEXT NOT NULL, -- ISO timestamp of crossing
|
||||
celebrated_at TEXT, -- nullable; null until card dismissed
|
||||
UNIQUE(milestone_kind)
|
||||
```
|
||||
|
||||
One row per kind per personal mind, idempotent.
|
||||
|
||||
Server route: `GET /api/milestones/pending` → `{ pending: [{ kind, achievedAt }] }` returns any rows with `celebrated_at=null`. Client renders card, then `POST /api/milestones/{kind}/ack` sets celebrated_at.
|
||||
|
||||
Crossing detection: on every frame insert, server-side trigger checks if `total_frame_count` crossed any threshold and inserts a milestone row. Cheap query.
|
||||
|
||||
## Implementation notes
|
||||
|
||||
- Confetti: use `canvas-confetti` npm package (~5kb). MIT license.
|
||||
- Card component: `MilestoneCard.tsx` with full-screen `motion.div` wrapper.
|
||||
- Hook: `useMilestone()` polls `/api/milestones/pending` every 30s + on `waggle:frame-saved` event.
|
||||
- Multiple milestones queued: render in sequence (1 → 10 if user goes from 0 to 12 in one batch import). 8s auto-dismiss between cards.
|
||||
- Share button: use `navigator.share()` on supported browsers, fall back to clipboard copy + toast.
|
||||
|
||||
## Estimate
|
||||
|
||||
- Milestones table + server trigger: ~30 LOC
|
||||
- API routes (pending, ack): ~30 LOC
|
||||
- MilestoneCard component + confetti: ~70 LOC
|
||||
- useMilestone hook + Desktop wiring: ~30 LOC
|
||||
- Tests (threshold crossing, dedupe, share): ~40 LOC
|
||||
- **Total ~200 LOC, ~2-3h with verification.**
|
||||
|
||||
## Risks + open questions
|
||||
|
||||
1. **Backfill** — existing users (with thousands of frames already) shouldn't suddenly see all 4 cards on next launch. Either: (a) on first migration, mark all already-crossed thresholds as celebrated_at=now; (b) only fire for thresholds crossed AFTER feature ships. Recommend (b). Migration script sets celebrated_at for any row where `achieved_at < featureShipDate`.
|
||||
2. **Confetti accessibility** — animation may trigger motion-sensitive users. Respect `prefers-reduced-motion`; fall back to static congratulations.
|
||||
3. **Share text** — currently embeds product URL. Tier-aware copy (Free user share vs Pro share)? PM call.
|
||||
4. **Threshold choice** — 1, 10, 100, 1000 powers-of-10. Could add 50, 500. v1 keep simple. PM call.
|
||||
5. **What counts as a frame** — same question as Streak feature. Recommend consistent rule across all addiction features (count non-deprecated, non-temporary frames).
|
||||
6. **Celebration sound?** — optional subtle "ding" audio cue. v1 silent (less intrusive).
|
||||
|
||||
## Out of scope (v1)
|
||||
|
||||
- Custom milestones (user-defined "celebrate at 50").
|
||||
- Per-workspace milestones.
|
||||
- Streaks integration ("milestone + 7-day streak combo unlocks X").
|
||||
- Achievement gallery / trophy room.
|
||||
- Social proof leaderboard.
|
||||
|
||||
## PM decisions needed
|
||||
|
||||
- [ ] GO / MODIFY / SKIP
|
||||
- [ ] Threshold set (1/10/100/1000 only, or add 50/500?)
|
||||
- [ ] Backfill strategy (mark existing as celebrated, or fire all once on first launch?)
|
||||
- [ ] Share text content (current draft, or different angle?)
|
||||
- [ ] Sound effect (silent / subtle ding / configurable)
|
||||
- [ ] Frame inclusion rule (same as Streak — must align)
|
||||
80
docs/addiction-features/06-tour-replay.md
Normal file
80
docs/addiction-features/06-tour-replay.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# 06 — Tour Replay Button
|
||||
|
||||
**Author:** CC (Block B design pass)
|
||||
**Date:** 2026-05-01
|
||||
**Status:** AWAITING_RATIFICATION
|
||||
**Estimate:** ~60 LOC + ~1-1.5h wall-clock
|
||||
**Touches:** apps/web (1 file: SettingsApp Advanced tab + useOnboarding hook)
|
||||
|
||||
---
|
||||
|
||||
## User story
|
||||
|
||||
As a returning user (or one who skipped onboarding), I want a "Replay tour" button in Settings → Advanced so I can re-trigger the post-wizard coachmark sequence without having to wipe my onboarding state or use a DEV-only URL parameter.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
1. Settings → Advanced section contains a "Replay onboarding tour" button.
|
||||
2. Clicking it: clears the `waggle:tooltips_done` localStorage flag AND the `tooltipsDismissed` field on `onboardingState`, then triggers a re-render so OnboardingTooltips mounts.
|
||||
3. Tour content: same 3 BASE_TIPS + CLOSING_TIP as the original tour, optionally including TEMPLATE_TIPS based on current active workspace's templateId.
|
||||
4. Button has a subtle confirmation toast ("Tour restarting…") so user knows the click registered.
|
||||
5. Optional: "Replay onboarding wizard" sibling button for full-flow restart (clears `waggle:onboarding` localStorage too — DEV/Power users only? PM call).
|
||||
|
||||
## UI sketch
|
||||
|
||||
```
|
||||
Settings App > Advanced tab:
|
||||
|
||||
Section: Help & Tutorials
|
||||
|
||||
[icon] Replay onboarding tour
|
||||
Show the 4-slide coachmark tour again. Useful if you want a refresher
|
||||
on Waggle's core gestures.
|
||||
[ Replay tour ]
|
||||
|
||||
[icon] Replay onboarding wizard (advanced)
|
||||
Restart the full 8-step setup. Will not delete any data — your
|
||||
workspaces, memories, and preferences are preserved.
|
||||
[ Replay wizard ]
|
||||
```
|
||||
|
||||
## Data model
|
||||
|
||||
No new tables. Pure localStorage manipulation:
|
||||
- Tour replay: `localStorage.removeItem('waggle:tooltips_done')` + update onboarding state `tooltipsDismissed: false`.
|
||||
- Wizard replay: clear `waggle:onboarding` storage entirely; reload page (or set `state.completed = false`).
|
||||
|
||||
## Implementation notes
|
||||
|
||||
- `useOnboarding` already has a `reset()` function (line 142-146) — use it for the wizard replay path.
|
||||
- Add a new `replayTour()` function: clear localStorage tour flag + setOnboardingState(prev => ({ ...prev, tooltipsDismissed: false })).
|
||||
- SettingsApp's Advanced tab exists; just add a section.
|
||||
- Toast affordance: existing `useToast` hook.
|
||||
|
||||
## Estimate
|
||||
|
||||
- `replayTour()` in useOnboarding: ~10 LOC
|
||||
- SettingsApp section: ~30 LOC
|
||||
- Tests (localStorage cleared, state flipped, render): ~20 LOC
|
||||
- **Total ~60 LOC, ~1-1.5h with verification.**
|
||||
|
||||
## Risks + open questions
|
||||
|
||||
1. **Tour vs wizard distinction** — users may not know the difference. Settings copy should make it clear: tour = post-launch coachmarks, wizard = full setup flow. Done above.
|
||||
2. **Wizard replay edge cases** — if user already has 5 workspaces and a year of memory, re-running wizard is confusing. Either: (a) hide wizard replay for non-DEV builds, (b) gate behind double-confirm, (c) skip the workspace-creation step on replay. Recommend (b) for v1.
|
||||
3. **Tour replay during ongoing tour** — defensive: if Tour is already mounted, click is no-op or restarts the tour from step 0.
|
||||
4. **Cross-tab sync** — multi-window users: replay click in one window should re-render Tour in all windows. Existing `waggle:onboarding-sync` event handles this for state; tour localStorage clear needs equivalent broadcast.
|
||||
|
||||
## Out of scope (v1)
|
||||
|
||||
- Per-workspace tour variants.
|
||||
- Custom tour authoring (Power users design their own coachmark sequences).
|
||||
- Onboarding wizard partial-replay (resume at step 5 only).
|
||||
- Analytics on which sections of tour users replay most often.
|
||||
|
||||
## PM decisions needed
|
||||
|
||||
- [ ] GO / MODIFY / SKIP
|
||||
- [ ] Include "Replay wizard" button alongside tour, or tour-only?
|
||||
- [ ] Confirm dialog for wizard replay (yes / skip)
|
||||
- [ ] Toast copy ("Tour restarting…" / "Coachmarks reset" / silent)
|
||||
93
docs/addiction-features/07-pending-imports-reminder.md
Normal file
93
docs/addiction-features/07-pending-imports-reminder.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# 07 — "Pending Imports" Reminder Banner
|
||||
|
||||
**Author:** CC (Block B design pass)
|
||||
**Date:** 2026-05-01
|
||||
**Status:** AWAITING_RATIFICATION
|
||||
**Estimate:** ~110 LOC + ~2h wall-clock
|
||||
**Touches:** apps/web (1 banner component, Memory app integration), packages/core (1 detector helper)
|
||||
|
||||
---
|
||||
|
||||
## User story
|
||||
|
||||
If I skipped the Memory Import step in the onboarding wizard, I want a periodic gentle reminder in the Memory app — "You can import 6 months of your AI history any time" — that points me to the Harvest tab, so I don't forget the value prop and can decide on my own schedule.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
1. When user opens Memory app AND has not yet imported any history (zero harvest events) AND skipped the import step in onboarding, render a dismissible banner at the top of the Memory app.
|
||||
2. Banner copy: "You can import 6 months of your AI history any time — Open Memory → Harvest". CTA button: "Open Harvest" (switches to Harvest tab).
|
||||
3. Dismissable; reappears weekly (every 7 days from last dismiss) until user actually imports history. After first successful import, banner permanently retires.
|
||||
4. Auto-detect Claude Code: if backend's `scanClaudeCode()` returns `found=true`, banner upgrades to specific copy: "Found N Claude Code conversations on this machine — import them now? [Harvest now]".
|
||||
5. Banner placement: fixed at top of MemoryApp main view, above tabs, dismissible with `✕` button.
|
||||
|
||||
## UI sketch
|
||||
|
||||
```
|
||||
Memory App, top of view:
|
||||
|
||||
Default version:
|
||||
┌──────────────────────────────────────────────────────────┐
|
||||
│ ↗ You can import 6 months of your AI history any time. │
|
||||
│ ChatGPT, Claude, Gemini, Perplexity, Cursor + 14 more │
|
||||
│ ✕ │
|
||||
│ [Open Harvest →] │
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
|
||||
Auto-detect upgrade (Claude Code found):
|
||||
┌──────────────────────────────────────────────────────────┐
|
||||
│ ⚡ Found 156 Claude Code conversations on this machine. │
|
||||
│ One click to extract decisions and preferences. │
|
||||
│ ✕ │
|
||||
│ [Harvest now →] │
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Data model
|
||||
|
||||
localStorage flags only — no new tables:
|
||||
- `waggle:import-banner-dismissed-at`: ISO timestamp of last dismiss
|
||||
- `waggle:import-banner-retired`: boolean — permanently retired after first import
|
||||
|
||||
Server side:
|
||||
- Existing `adapter.scanClaudeCode()` for auto-detect upgrade
|
||||
- Existing `getHarvestStatus()` (already exists in HarvestTab) returns total ingested events count — banner uses this to decide retirement
|
||||
|
||||
## Implementation notes
|
||||
|
||||
- Banner mounts inside MemoryApp top section, before `tabs`.
|
||||
- Detect skipped-import: `onboardingState.completed === true && totalHarvestedEvents === 0`. The wizard's ImportStep allows skipping; if user skipped (didn't import) and now has 0 harvest events, banner is eligible.
|
||||
- Re-show cadence: 7-day timer from last dismiss; subsequent dismiss extends the timer. Once user imports anything, set retired=true and never show again.
|
||||
- Auto-detect upgrade: on banner mount, check `scanClaudeCode()`; swap copy + CTA if `found=true`.
|
||||
- "Open Harvest" CTA: switches MemoryApp's active tab to "Harvest" via existing tab-switch event (`waggle:open-app` with `appId=memory, tab=harvest`).
|
||||
|
||||
## Estimate
|
||||
|
||||
- ImportReminderBanner component: ~60 LOC
|
||||
- MemoryApp wire-up + tab switch event: ~20 LOC
|
||||
- localStorage helpers (read/write dismissed-at, retired): ~20 LOC
|
||||
- Tests (re-show cadence, retirement, auto-detect upgrade): ~30 LOC
|
||||
- **Total ~130 LOC, ~2h with verification.**
|
||||
|
||||
## Risks + open questions
|
||||
|
||||
1. **Frequency** — weekly may be too aggressive for users who actively don't want to import. Consider: weekly for first 4 weeks, then monthly, then never. v1 ships pure weekly + dismiss-permanently option ("Don't show again"). PM call.
|
||||
2. **Auto-detect banner on every Memory app open** — Claude Code auto-detect runs on every mount; if user has 156 conversations and dismisses the banner, next mount re-detects and re-shows. Add: dismissal also includes the auto-detect signature so re-detect doesn't re-fire. Track `dismissed-with-cc-count: 156`.
|
||||
3. **Retired flag timing** — set when first import event lands. Race condition: user imports, banner is mid-render with old state. Acceptable; resolves on next mount.
|
||||
4. **Empty Memory app** — for fresh user with no memories AND no imports, banner is helpful. For returning user with rich memory but who never imported, banner is also valid (they may have other AI history they forgot about). v1: show in both cases.
|
||||
5. **Cross-platform** — Claude Code detection is local-only (filesystem scan); banner upgrade only fires on Tauri builds where the sidecar can scan. Web-app users see default version.
|
||||
|
||||
## Out of scope (v1)
|
||||
|
||||
- Email reminder for users who churn (haven't opened Memory in N days).
|
||||
- Social proof copy ("Average user imports 1,200 conversations").
|
||||
- Analytics on which import source (ChatGPT vs Claude) gets clicked most.
|
||||
- Multiple-source auto-detect (Cursor history, Perplexity, Gemini Takeout) — currently only Claude Code is detectable on local FS.
|
||||
- Direct in-banner upload widget (just CTA → Harvest tab, no inline UX).
|
||||
|
||||
## PM decisions needed
|
||||
|
||||
- [ ] GO / MODIFY / SKIP
|
||||
- [ ] Re-show cadence (weekly v1 / weekly→monthly→stop / configurable / one-shot)
|
||||
- [ ] "Don't show again" affordance — separate button vs. just X
|
||||
- [ ] Auto-detect upgrade copy (current draft, or different framing — "1-click migration" vs. "found conversations")
|
||||
- [ ] Trigger eligibility (skipped-import only OR also returning users with 0 imports?)
|
||||
122
docs/addiction-features/README.md
Normal file
122
docs/addiction-features/README.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# Addiction Features — Block B Design Docs (2026-05-01)
|
||||
|
||||
**Purpose:** Set of seven design-stage docs covering the "addiction features" cluster from the 2026-05-01 PM walkthrough brief. Each doc is design-only — **no code shipped yet**. Marko ratifies per feature (GO / MODIFY / SKIP) before CC implements.
|
||||
|
||||
**Status:** AWAITING_RATIFICATION — all 7 docs pending Marko review.
|
||||
|
||||
**Scope:** Loops that get users to come back tomorrow. Quantified-value reinforcement (streaks, weekly digests), pickup affordances (continuity banner, daily brief), milestone celebrations, replayable onboarding, gentle-nudge import prompts.
|
||||
|
||||
---
|
||||
|
||||
## Index
|
||||
|
||||
| # | Feature | LOC est. | Hours est. | One-line summary |
|
||||
|---|---------|----------|------------|------------------|
|
||||
| 1 | [Memory Streak](01-memory-streak.md) | ~180 | 3-4 | Daily streak counter in StatusBar; resets if 24h gap |
|
||||
| 2 | [Daily Brief](02-daily-brief.md) | ~330 | 5-6 | Morning notification summarising yesterday + today suggestion |
|
||||
| 3 | [Continuity Banner](03-continuity-banner.md) | ~140 | 2-3 | "Picking up where you left off" banner on Chat re-open |
|
||||
| 4 | [Weekly Wins Digest](04-weekly-wins-digest.md) | ~340 | 4-5 | Monday card: frames saved, recalls, est. minutes saved |
|
||||
| 5 | [Milestone Cards](05-milestone-cards.md) | ~200 | 2-3 | Confetti + congrats at 1/10/100/1000 frames |
|
||||
| 6 | [Tour Replay](06-tour-replay.md) | ~60 | 1-1.5 | Settings button to re-trigger post-wizard coachmark sequence |
|
||||
| 7 | [Pending Imports Reminder](07-pending-imports-reminder.md) | ~130 | 2 | Memory-app banner for users who skipped import |
|
||||
| | **Totals** | **~1,380 LOC** | **~20-25h** | All seven shipped |
|
||||
|
||||
---
|
||||
|
||||
## Cross-feature decisions Marko needs to make once
|
||||
|
||||
These appear in multiple docs and benefit from a single ruling:
|
||||
|
||||
1. **What counts as a "frame"?**
|
||||
- Used by: Streak (#1), Milestones (#5), Wins Digest (#4)
|
||||
- Options: (a) all frames, (b) non-deprecated only, (c) non-deprecated AND non-temporary, (d) importance ≥ normal
|
||||
- Recommendation: (c) — count non-deprecated, non-temporary. Aligns with existing `composeWorkspaceSummary` filter.
|
||||
|
||||
2. **Notification timezone strategy**
|
||||
- Used by: Streak (#1, day boundary), Daily Brief (#2, fire hour), Wins Digest (#4, week boundary)
|
||||
- Options: (a) server-local TZ for v1 + document, (b) per-user TZ from settings, (c) detect from browser
|
||||
- Recommendation: (a) v1, (b) v2 if cross-TZ usage emerges.
|
||||
|
||||
3. **Empty-state behaviour for fresh users**
|
||||
- Used by: Streak (#1, day-1 user), Daily Brief (#2, no yesterday), Continuity (#3, no last session), Wins Digest (#4, < 7 days history)
|
||||
- Options: (a) suppress entirely, (b) show educational copy, (c) show motivational copy
|
||||
- Recommendation: (a) suppress — fresh users have higher-priority surfaces (wizard, Tour).
|
||||
|
||||
4. **Banner / overlay z-index hierarchy**
|
||||
- Three new surfaces (Continuity, Daily Brief, Imports Reminder) plus existing OnboardingTooltips, LoginBriefing, MilestoneCard.
|
||||
- Need a documented ordering rule. Recommendation: only ONE high-priority overlay can render at once; the rest queue.
|
||||
|
||||
5. **LLM cost approval**
|
||||
- Daily Brief generator: ~$0.005/user/day = $0.15/user/month
|
||||
- Wins Digest theme extraction: ~$0.01/user/week = $0.04/user/month
|
||||
- Total: ~$0.19/user/month for both. Per-user margin impact on FREE tier: minor; on PRO: negligible.
|
||||
|
||||
---
|
||||
|
||||
## Ratification checklist
|
||||
|
||||
Marko, please mark each feature with one of: **GO** (build as designed), **MODIFY** (open the doc, leave inline comments), **SKIP** (defer or kill).
|
||||
|
||||
- [ ] **#1 Memory Streak** — GO / MODIFY / SKIP
|
||||
- [ ] **#2 Daily Brief** — GO / MODIFY / SKIP
|
||||
- [ ] **#3 Continuity Banner** — GO / MODIFY / SKIP
|
||||
- [ ] **#4 Weekly Wins Digest** — GO / MODIFY / SKIP
|
||||
- [ ] **#5 Milestone Cards** — GO / MODIFY / SKIP
|
||||
- [ ] **#6 Tour Replay** — GO / MODIFY / SKIP
|
||||
- [ ] **#7 Pending Imports Reminder** — GO / MODIFY / SKIP
|
||||
|
||||
Plus the cross-feature decisions:
|
||||
- [ ] Frame inclusion rule
|
||||
- [ ] Timezone strategy v1
|
||||
- [ ] Empty-state behaviour (suppress / educate / motivate)
|
||||
- [ ] Overlay z-index queue rule
|
||||
- [ ] LLM cost approval ($0.19/user/month for #2 + #4)
|
||||
|
||||
---
|
||||
|
||||
## Build order recommendation (assuming all GO)
|
||||
|
||||
CC's recommended sequencing (each phase ships independently, no blockers between them):
|
||||
|
||||
**Phase 1 — Quick wins (2-3h, low risk, immediate user-visible)**
|
||||
- #6 Tour Replay (~1.5h)
|
||||
- #7 Pending Imports Reminder (~2h)
|
||||
|
||||
**Phase 2 — Streak loop (3-4h)**
|
||||
- #1 Memory Streak (single SQLite addition + StatusBar wire)
|
||||
|
||||
**Phase 3 — Celebration (2-3h)**
|
||||
- #5 Milestone Cards (uses same frame-count signal as Streak)
|
||||
|
||||
**Phase 4 — Continuity surface (2-3h)**
|
||||
- #3 Continuity Banner (uses existing `recentThreads` from workspace-context)
|
||||
|
||||
**Phase 5 — Daily Brief (5-6h, LLM cost approval gate)**
|
||||
- #2 Daily Brief (cron job + LLM generator + Tauri notification permission)
|
||||
|
||||
**Phase 6 — Weekly Wins (4-5h, recall instrumentation gate)**
|
||||
- #4 Weekly Wins Digest (requires HybridSearch instrumentation — heaviest)
|
||||
|
||||
Total parallel-friendly: Phases 1+2+3 can ship together (~8h). Phase 4 independent. Phases 5+6 require backend work + LLM approval.
|
||||
|
||||
---
|
||||
|
||||
## Out of scope for this design pass
|
||||
|
||||
- Tier-gated variants (does FREE see streak chip? does PRO see different milestone copy?). Defer to per-feature `MODIFY` notes once Marko ratifies each.
|
||||
- Localization. v1 ships English-only copy.
|
||||
- A/B testing infrastructure for which copy variants drive engagement. Wait for usage data.
|
||||
- Cross-feature combo bonuses ("Streak + 100 frames = bonus card"). Wait until each individual feature ships.
|
||||
|
||||
---
|
||||
|
||||
## Implementation contract
|
||||
|
||||
Once Marko ratifies (per-feature GO), CC will:
|
||||
|
||||
1. Open the corresponding doc, drop a `## Implementation log` section at the bottom, link the eventual commits there.
|
||||
2. Implement in the recommended phase order unless Marko prefers a different order.
|
||||
3. After each phase, halt and PM Pass for verification before starting the next.
|
||||
4. Each phase commits include the feature number in the message (`feat(streak): ship #1 Memory Streak counter`) for backlink.
|
||||
|
||||
No feature ships before its doc has GO from Marko.
|
||||
Reference in New Issue
Block a user