Work Summaries
How to read this doc
Sections 1β2 frame the goal and architecture. Section 3 lists all data sources and their surface-area mapping. Sections 4β5 cover the schema and the two-pass AI summarization approach. Section 6 describes the intranet UI β the two view modes and AI explanation strip. Sections 7β8 are the edge functions and files list. Sections 9β11 are the build sequence, verification checklist, and deferred v2 items. Markdown source: docs/devtasks/day-in-review.md.
Contents
1. Context
Leadership has no unified view of what happened over any given period. Activity is spread across five separate systems: Claude Code sessions in Cloudflare D1, task activity in Supabase, git history in GitHub, image generation in public.images, Gmail inbound/outbound, plus Cloudflare deploy events and backup logs.
Goal
An on-demand Work Summary page on the intranet where any authorized user can generate a structured summary for any time range. Each summary:
- Shows an org-level "what we accomplished" section β headline and 3β5 bullets.
- Offers two view modes: By Person (each contributor's activity) and By Surface (by area of the product/org, with attribution).
- Includes an AI explanation strip on every section: a plain-English sentence or two on what was accomplished, not just what happened β powered by a lightweight Haiku pass.
- Provides traceability: every activity item links to its source record where possible (GitHub commit, Supabase task, session, etc.).
- Collects feedback reactions per-section to drive a self-improving prompt loop.
What's deferred
Email delivery and scheduled CRON are deferred entirely to v2. Build the intranet page first, validate the product, then reintroduce automation. See Β§13.
2. Architecture
3. Data sources
Same 9 sources as the original plan. Queries now accept start_at / end_at parameters (TIMESTAMPTZ) instead of midnight-to-midnight, enabling arbitrary ranges like "last 4 hours" or "last 30 days".
| Source | Access | Captures |
|---|---|---|
| Claude Code sessions | ccd_session_mgmt MCP | count, duration, cost, titles, worktrees |
| Gmail inbound | Gmail MCP search_threads | new threads, senders, subjects |
| Gmail outbound | Gmail MCP search_threads | sent threads, recipients |
| Task activity | Supabase task_activity | actions, actors, task titles |
| Task prompt runs | Supabase task_prompt_runs | headless runs, cost, status, risk |
| Git commits | git log --since / --until | count, authors, prefixes, messages |
| Image generation | Supabase public.images | new images, cost by provider, categories |
| Backup logs | Supabase public.backup_logs | status, duration |
| Cloudflare deploys | CF Pages API | deploy count, success/failure, project name |
Surface area mapping
How raw sources become the By Surface view:
| Surface | Sources |
|---|---|
| Development | git commits, Claude Code sessions, Cloudflare deploys (app) |
| Communications | Gmail inbound, Gmail outbound |
| Tasks & Projects | task_activity, task_prompt_runs |
| Assets & Content | public.images |
| Infrastructure | backup_logs, Cloudflare deploys (worker/infra) |
Cloudflare deploy events are classified as Development or Infrastructure by the Sonnet synthesis pass based on the deploy's project name. Source registry stored in public.work_summary_sources.
4. Database schema
Migration: apps/control/migrations/20260507_work_summaries.sql. Four tables:
-- One row per generated report
CREATE TABLE public.work_summaries (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
start_at TIMESTAMPTZ NOT NULL,
end_at TIMESTAMPTZ NOT NULL,
title TEXT, -- e.g. "Last 24 hours Β· 7 May 2026" (auto-generated)
raw_data JSONB,
review JSONB, -- org_summary + contributor_sections[] + surface_sections[]
generated_by UUID REFERENCES public.app_users(id),
agent_cost_usd REAL,
template_version TEXT,
created_at TIMESTAMPTZ DEFAULT now()
);
review JSONB shape
{
"org_summary": {
"headline": "string",
"bullets": ["string Γ 3-5"],
"period_label": "Last 24 hours"
},
"contributor_sections": [{
"contributor_name": "string",
"contributor_id": "uuid|null",
"personal_summary": "string (Haiku, 1-2 sentences)",
"activity_groups": [{
"source": "git_commits",
"label": "3 commits to mobile",
"ai_explanation": "string (1-3 sentences)",
"items": [{ "description": "string", "link": "url|null" }]
}]
}],
"surface_sections": [{
"surface": "development",
"label": "Development",
"ai_explanation": "string",
"activity_groups": [{
"contributor": "string",
"label": "string",
"items": [{ "description": "string", "link": "url|null" }]
}]
}],
"source_errors": ["string"]
}
-- Feedback per section
CREATE TABLE public.work_summary_feedback (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
summary_id UUID REFERENCES public.work_summaries(id) ON DELETE CASCADE,
feedback_type TEXT NOT NULL CHECK (feedback_type IN ('reaction', 'free_text')),
section_key TEXT,
rating TEXT CHECK (rating IN ('too_much', 'just_right', 'need_more', 'skip')),
note TEXT,
reviewer_id UUID REFERENCES public.app_users(id),
processed BOOLEAN DEFAULT false,
created_at TIMESTAMPTZ DEFAULT now()
);
-- Single-row instructions β the live prompt the edge fn reads
CREATE TABLE public.work_summary_instructions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
current_prompt TEXT NOT NULL,
template_version TEXT NOT NULL DEFAULT '1.0.0',
history JSONB DEFAULT '[]', -- [{version, prompt, changed_at, reason}]
last_updated_at TIMESTAMPTZ DEFAULT now()
);
-- Source registry
CREATE TABLE public.work_summary_sources (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
source_key TEXT UNIQUE NOT NULL,
display_name TEXT NOT NULL,
surface TEXT, -- development | communications | tasks | assets | infrastructure
is_active BOOLEAN DEFAULT true,
query_config JSONB,
last_active_at TIMESTAMPTZ,
auto_discovered BOOLEAN DEFAULT false,
notes TEXT,
created_at TIMESTAMPTZ DEFAULT now()
);
5. AI summarization β two-pass approach
Pass 1 β Claude Sonnet (main synthesis)
- Reads
work_summary_instructions.current_promptat runtime - Receives raw source data for the requested range
- Outputs structured JSON:
contributor_sections[],surface_sections[],raw_data - Adaptive rules: skip zero-activity sections, compress routine (e.g. 30 CI bumps β one line), surface notable events, lead with highest-signal item
Pass 2 β Claude Haiku (explanation, parallel)
- One call per activity group in each section, run in parallel
- Input: group label + item list
- Output:
ai_explanationβ 1β3 sentences in plain language on what this accomplished - Also generates:
personal_summaryper contributor,ai_explanationper surface section - Separate call:
org_summary.headline+org_summary.bullets(3β5 bullets)
Why Haiku for Pass 2
The data is already structured at this point β Haiku is summarizing a known list of items, not reasoning over raw logs. It's cheaper and faster for many parallel calls. Sonnet handles the harder aggregation and structuring pass.
Example explanations
"3 commits to mobile"β "Fixed the LiveKit audio scope bug that was causing crashes on Android emulator.""2 Cloudflare deploys"β "Deployed the updated intranet search feature to production.""12 task activity events"β "Moved the Work Summaries feature through planning and into the spec-revision phase."
6. Intranet UI
New section "work-summaries" added to apps/control/src/types/intranet.ts.
Routes
src/app/[lang]/(intranet)/work-summaries/page.tsx β list + generate form
src/app/[lang]/(intranet)/work-summaries/[id]/page.tsx β summary detail view
Generate form (top of list page)
- Start / End
<input type="datetime-local">β follows the sessions-tab.tsx date filter pattern - Quick-select buttons: Last 24h, Last 7 days, Last 30 days β pre-fill the range
- "Generate Work Summary" button β POST to edge fn β spinner overlay β redirect to
/work-summaries/[id]on completion
Detail page layout
[Org Summary card β always visible at top]
Headline
β’ Bullet 1
β’ Bullet 2
β’ Bullet 3
[View toggle: By Person | By Surface] β sub-tabs.tsx URL pattern, amber underline
[By Person view]
[Contributor card β expandable]
AI personal summary (1β2 sentences) β ai-explanation-strip
[Activity group card]
[AI explanation strip] "What this accomplishedβ¦"
βΈ item 1 with trace link
βΈ item 2 with trace link
[By Surface view]
[Surface card β expandable]
AI surface summary β ai-explanation-strip
[Activity group card β per contributor]
[AI explanation strip]
βΈ item 1 with trace link (Contributor: Name)
[Feedback β bottom of page]
Per-section reaction bar (too_much | just_right | need_more | skip)
Free-text feedback box
AI explanation strip β the "swimlane"
A lightly styled callout band at the top of each expandable card β bg-slate-50 border-l-2 border-slate-300, italic text β showing the Haiku-generated "what was accomplished" summary. Visible without opening the full item list. This is how the page stays navigable without being overwhelming: the AI layer floats above the raw detail.
Components
src/components/work-summaries/
summary-list.tsx β card grid of past summaries
generate-form.tsx β date inputs + quick-select + Generate button + async state
org-summary-card.tsx β headline + bullet points
contributor-view.tsx β renders contributor_sections[]
surface-view.tsx β renders surface_sections[]
activity-group-card.tsx β shared: ai_explanation strip + expandable items + trace links
ai-explanation-strip.tsx β reusable callout band
reaction-bar.tsx β too_much | just_right | need_more | skip
free-response-box.tsx β open text + submit β feedback_type='free_text'
7. Edge functions
generate-work-summary/
Called by the intranet UI. Runs the two-pass synthesis and stores the result.
- Validates
start_at,end_at,generated_by - Queries all
is_active = truesources in parallel (Gmail MCP, sessions MCP, Bash git log, Supabase, CF API) - Continues on individual source failure; records failures in
source_errors[] - Sonnet synthesis pass β Haiku explanation pass (parallel) β INSERT into
work_summaries - Returns
{id}immediately; status polled via a lightweight status endpoint
update-summary-instructions/
Triggered by Supabase webhook on work_summary_feedback INSERT. Same self-improvement design as the original plan.
- Reads all
processed = falsefeedback for the relevant summary - Calls Claude API:
current_prompt+ feedback β revised prompt + change summary - Archives old prompt in
historyJSONB; bumpstemplate_version - Marks processed feedback as
processed = true
Key design decision β instructions are pre-compiled
The edge fn reads current_prompt from one DB row at generation time. It doesn't reason about feedback history on every run β that reasoning happens when feedback arrives and produces a clean updated prompt. This keeps each generation fast and the instructions human-readable at any point in time.
8. Files to create / modify
New files
apps/control/migrations/20260507_work_summaries.sql
apps/control/supabase/functions/generate-work-summary/index.ts
apps/control/supabase/functions/update-summary-instructions/index.ts
apps/control/src/app/[lang]/(intranet)/work-summaries/page.tsx
apps/control/src/app/[lang]/(intranet)/work-summaries/[id]/page.tsx
apps/control/src/components/work-summaries/summary-list.tsx
apps/control/src/components/work-summaries/generate-form.tsx
apps/control/src/components/work-summaries/org-summary-card.tsx
apps/control/src/components/work-summaries/contributor-view.tsx
apps/control/src/components/work-summaries/surface-view.tsx
apps/control/src/components/work-summaries/activity-group-card.tsx
apps/control/src/components/work-summaries/ai-explanation-strip.tsx
apps/control/src/components/work-summaries/reaction-bar.tsx
apps/control/src/components/work-summaries/free-response-box.tsx
Modified files
apps/control/src/types/intranet.ts β add "work-summaries" to IntranetSection + DEFAULT_TABS
Existing files to read first (patterns to follow)
apps/control/src/components/devcontrol/sessions-tab.tsx β date filter + expandable card
apps/control/src/components/intranet/sub-tabs.tsx β view toggle (amber underline)
apps/control/src/types/intranet.ts β section/tab structure
apps/control/supabase/functions/daily-task-reminder/index.ts β edge fn + Supabase pattern
apps/control/supabase/functions/_shared/ β shared utilities
apps/control/src/components/tasks/task-detail-drawer.tsx β AI summary UI reference
9. Credentials
All secrets in Bitwarden, collection DevOps-sponicgarden (ALPU.CA org). See infra/runbook.md for unlock recipe.
| Secret | BW item name | Env var |
|---|---|---|
| Supabase URL + service role key | SponicControl Supabase | SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY |
| Anthropic API key | Anthropic API | ANTHROPIC_API_KEY |
| Cloudflare API token | Cloudflare β API Token | CLOUDFLARE_API_TOKEN |
| D1 sessions binding | configured in apps/control/cloudflare/claude-sessions/wrangler.toml | via wrangler |
Gmail and ccd_session_mgmt accessed via MCP β no additional secrets needed beyond what the intranet already uses.
10. Bootstrap prompt (new session)
Paste this into a fresh Claude Code session to start implementation:
Implement the Work Summaries feature for the Sponic Gardens monorepo.
Full spec: docs/devtasks/day-in-review.md
Start with Phase 1 (the DB migration):
apps/control/migrations/20260507_work_summaries.sql
using the schema from Β§4. Follow existing migrations in that folder as a pattern.
Then ask me before moving to Phase 2.
Before starting, read these files to understand patterns:
apps/control/src/components/devcontrol/sessions-tab.tsx
apps/control/src/components/intranet/sub-tabs.tsx
apps/control/src/types/intranet.ts
apps/control/supabase/functions/daily-task-reminder/index.ts
apps/control/supabase/functions/_shared/
Credentials are in Bitwarden DevOps-sponicgarden β see infra/runbook.md for unlock recipe.
11. Build sequence
- Migration β
work_summaries,work_summary_feedback,work_summary_instructions,work_summary_sourcestables + seed data - Edge fn: generate-work-summary β Sonnet synthesis pass + Haiku explanation pass + INSERT
- Edge fn: update-summary-instructions β self-improvement loop + Supabase webhook
- Intranet section β types β generate-form + list page β org-summary-card β ai-explanation-strip + activity-group-card β contributor-view + surface-view β reaction-bar + free-response-box
- Source registry admin tab β manage
work_summary_sources, activate auto-discovered sources
12. Verification
| Check | Pass condition |
|---|---|
| Generate (Last 24h) | work_summaries row inserted with non-null review JSON |
| Org summary | Headline + 3β5 bullets visible at top of detail page |
| By Person view | Each contributor card renders with personal_summary and activity groups |
| By Surface view | Each surface card renders with ai_explanation and contributor attribution |
| AI explanation strips | Haiku-generated explanation visible above item list in each card |
| Trace link | Click a git commit item β opens GitHub commit URL |
| Reaction feedback | Submit "too_much" on sessions group β row in work_summary_feedback |
| Self-improve | After feedback INSERT, work_summary_instructions.last_updated_at updated |
| Source error | Disconnect a source; summary still generates; source_errors has entry |
13. Deferred to v2
- Email delivery β formatted HTML email via Resend to leadership team
- Scheduled CRON β nightly generation at 8 PM CT
- Mobile push notifications
- Slack integration
- Multi-workspace Gmail (single authenticated account only)
- Historical import (reviews start from launch date)
- Public-facing version