← All docs

Work Summaries

An on-demand intranet page that aggregates Claude sessions, tasks, git commits, email, images, and deploys into a two-view report β€” By Person and By Surface β€” with AI explanation strips, traceability links, and a self-improving prompt loop.
2026-05-07 Build plan / RFC ~2 weeks Touches: Supabase Β· Control intranet Β· Anthropic API Β· Gmail MCP Β· CF Pages API

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
  2. Architecture
  3. Data sources
  4. Database schema
  5. AI summarization
  6. Intranet UI
  7. Edge functions
  8. Files to create / modify
  9. Credentials
  10. Bootstrap prompt
  11. Build sequence
  12. Verification
  13. Deferred to v2

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:

  1. Shows an org-level "what we accomplished" section β€” headline and 3–5 bullets.
  2. Offers two view modes: By Person (each contributor's activity) and By Surface (by area of the product/org, with attribution).
  3. 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.
  4. Provides traceability: every activity item links to its source record where possible (GitHub commit, Supabase task, session, etc.).
  5. 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

[Work Summaries page β€” intranet] β”œβ”€ date-range picker (Start / End datetime) β”œβ”€ Quick-select: Last 24h | Last 7 days | Last 30 days β”œβ”€ "Generate Work Summary" button β”‚ β†’ POST to generate-work-summary edge fn β”‚ β†’ async: shows spinner, polls, redirects on completion └─ list of past summaries (newest first) [generate-work-summary edge fn] β”œβ”€ queries all active sources in work_summary_sources β”‚ (parameterised by start_at / end_at) β”œβ”€ Claude Sonnet: aggregates raw_data β†’ structured sections JSON β”‚ (contributor_sections[], surface_sections[]) β”œβ”€ Claude Haiku (parallel per section): ai_explanation β”‚ "what was accomplished" in 1–3 sentences β”œβ”€ Claude Haiku: org_summary headline + bullets └─ INSERT into public.work_summaries [update-summary-instructions edge fn] triggered by Supabase webhook on work_summary_feedback INSERT reads unprocessed feedback β†’ calls Claude API β†’ revised prompt β†’ updates work_summary_instructions

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".

SourceAccessCaptures
Claude Code sessionsccd_session_mgmt MCPcount, duration, cost, titles, worktrees
Gmail inboundGmail MCP search_threadsnew threads, senders, subjects
Gmail outboundGmail MCP search_threadssent threads, recipients
Task activitySupabase task_activityactions, actors, task titles
Task prompt runsSupabase task_prompt_runsheadless runs, cost, status, risk
Git commitsgit log --since / --untilcount, authors, prefixes, messages
Image generationSupabase public.imagesnew images, cost by provider, categories
Backup logsSupabase public.backup_logsstatus, duration
Cloudflare deploysCF Pages APIdeploy count, success/failure, project name

Surface area mapping

How raw sources become the By Surface view:

SurfaceSources
Developmentgit commits, Claude Code sessions, Cloudflare deploys (app)
CommunicationsGmail inbound, Gmail outbound
Tasks & Projectstask_activity, task_prompt_runs
Assets & Contentpublic.images
Infrastructurebackup_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)

Pass 2 β€” Claude Haiku (explanation, parallel)

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

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)

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.

update-summary-instructions/

Triggered by Supabase webhook on work_summary_feedback INSERT. Same self-improvement design as the original plan.

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.

SecretBW item nameEnv var
Supabase URL + service role keySponicControl SupabaseSUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY
Anthropic API keyAnthropic APIANTHROPIC_API_KEY
Cloudflare API tokenCloudflare β€” API TokenCLOUDFLARE_API_TOKEN
D1 sessions bindingconfigured in apps/control/cloudflare/claude-sessions/wrangler.tomlvia 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

  1. Migration β€” work_summaries, work_summary_feedback, work_summary_instructions, work_summary_sources tables + seed data
  2. Edge fn: generate-work-summary β€” Sonnet synthesis pass + Haiku explanation pass + INSERT
  3. Edge fn: update-summary-instructions β€” self-improvement loop + Supabase webhook
  4. 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
  5. Source registry admin tab β€” manage work_summary_sources, activate auto-discovered sources

12. Verification

CheckPass condition
Generate (Last 24h)work_summaries row inserted with non-null review JSON
Org summaryHeadline + 3–5 bullets visible at top of detail page
By Person viewEach contributor card renders with personal_summary and activity groups
By Surface viewEach surface card renders with ai_explanation and contributor attribution
AI explanation stripsHaiku-generated explanation visible above item list in each card
Trace linkClick a git commit item β†’ opens GitHub commit URL
Reaction feedbackSubmit "too_much" on sessions group β†’ row in work_summary_feedback
Self-improveAfter feedback INSERT, work_summary_instructions.last_updated_at updated
Source errorDisconnect a source; summary still generates; source_errors has entry

13. Deferred to v2