cambium ▸ docs/superpowers/plans/2026-04-12-morning-brief-workflow.md
updated 2026-04-12

Morning Brief Workflow — Implementation Plan

Date: 2026-04-12 Spec: docs/superpowers/specs/2026-04-12-morning-brief-workflow-design.md Status: Executing

Overview

Wire the morning.pulse rhythm (already fires at 06:00 Berlin) to a real morning brief: gather context from memory + substrate + git + seeds, call Sonnet, post the result to Discord #general as a top-level message.

Task 1: Git log fetcher utility (TDD)

File: packages/cortex/src/git-log-fetcher.ts Test: packages/cortex/src/git-log-fetcher.test.ts

Task 2: MorningBriefComposer (TDD)

File: packages/cortex/src/morning-brief-composer.ts Test: packages/cortex/src/morning-brief-composer.test.ts

interface MorningBriefDeps {
  llmCaller: LlmCaller;
  recall: typeof recall;           // injected for testability
  recallDeps: RecallDeps;          // store, substrate, daily, compost, etc.
  memoryStore: MemoryStore;        // for findSeedsByWorkspace
  dailyReader: DailyReader;        // for readDay(today)
  gitProjectDirs: string[];        // passed from start-rhythms
  workspaceId: WorkspaceId;
}

async function composeMorningBrief(deps: MorningBriefDeps): Promise<string>

Gathers: 1. Substrate: recall({ about: 'joshua', layers: ['substrate'], workspace_id }) 2. Yesterday’s marks: recall({ about: 'joshua', horizon: '1d', layers: ['marks'], workspace_id }) 3. Routing history: recall({ about: 'joshua.discord.routed', horizon: '1d', layers: ['marks'], workspace_id }) 4. Git log: fetchGitLogs(gitProjectDirs) 5. Pending seeds: memoryStore.findSeedsByWorkspace(workspace_id, 'pending') 6. Today’s daily: dailyReader.readDay(todayDate)

Assembles the prompt from the spec’s template verbatim. Makes one Sonnet call. Returns the text.

Tests mock the LlmCaller. Verify the prompt contains: substrate section, marks section, routing section, git section, seeds section, daily section, the “Be yourself” line.

Task 3: postToChannel on DiscordClient + morning brief receptor

File (modify): packages/integrations/src/discord/discord-client.ts File: packages/cortex/src/morning-brief-receptor.ts Test: packages/cortex/src/morning-brief-receptor.test.ts

Add postToChannel(channelId: string, content: string): Promise<string | undefined> to DiscordClient — simpler than replyInThread, just channel.send(content).

The receptor: - Binds to morning.pulse - Checks if LLM is available (graceful no-op if not) - Calls composeMorningBrief() - Posts via discordClient.postToChannel(channelId, content) - Emits morning.brief_delivered signal - try/catch wraps everything — log error, don’t crash

Task 4: Wire into start-rhythms.ts

File (modify): deployments/obadiah/bridge/start-rhythms.ts

Task 5: Integration test

File: packages/cortex/src/morning-brief.integration.test.ts

Task 6: Full monorepo gate

pnpm turbo test && pnpm turbo typecheck && pnpm turbo build

Notes