Automated Weekly Stakeholder Updates with Claude Code, Step by Step
Date: July 6, 2026 • Author: Second Axis
It's Thursday afternoon and you're assembling the weekly update again. Tab through Jira for what closed, scroll the GitHub releases page, search Slack for where the pricing decision landed, pull up the dashboard to see if activation moved, then write it three times: one page for the exec, detail for the team, and a customer version with the shipped feature and nothing else. PMs describe the load in plain terms: "I had seven calls today. I have six calls tomorrow", one Hacker News commenter estimated roughly 40% of the day going to status updates. The update isn't even the hard part of the job. It's just the part that eats the calendar.
Notice what the work actually is. Every fact in the update already exists somewhere: the closed ticket, the merged PR, the decision thread, the metric. The work is assembly (four systems, four logins, four mental models) and translation (the same week told three ways for three audiences), plus the part nobody writes down: remembering that last week you promised the exec "the migration ships next week" and now you'd better say whether it did.
Can you build this with Claude Code? Yes, and the primitives fit unusually well: MCP connections to your trackers, subagents for the per-audience drafts, a critic to check claims against sources, scheduled routines to run it every Friday. What Claude Code does not do is assemble those primitives into a system that stays truthful and continuous week after week. That part is engineering, and this post is the complete playbook.
What you're building
Every Friday morning (or Monday, pick your ritual), without you touching anything:
- An exec one-pager: shipped, slipped, blocked, the metrics that moved, and open risks, every claim traceable to a ticket, PR, or thread.
- A team update with the detail level the exec version deliberately drops: which PRs, which blockers, who's waiting on whom.
- A customer-facing summary containing shipped work only, with internal names and internal problems scrubbed.
- Continuity: each update closes out last week's "next week we will" promises (done, slipped with a reason, or explicitly dropped) and never repeats a previously reported item as news.
- A review gate: you edit the draft in minutes, the system diffs your edits against its draft, and it learns what you keep cutting.
The PM's Thursday becomes a ten-minute review instead of a two-hour assembly job.
Why copy-pasting into a chatbot fails on week two
Be precise about the difficulty, because it is not where you'd expect: this is not a big-data workflow. A week of closed tickets, merged PR titles, decision threads, and metric deltas is a few tens of thousands of tokens. It fits in a context window with room to spare. If scale were the problem, one careful prompt would solve it.
The hard parts are different:
- Multi-source assembly, recurring. Pasting from four tools into a chat works exactly once. By week three it's a manual ritual with extra steps, and the first week you're on vacation, the update doesn't go out.
- Narrative continuity. A good update is a serial, not a snapshot. "Still blocked on the SSO vendor, second week" is information; "blocked on SSO vendor" repeated identically is noise. Continuity requires knowing what was already said.
- Promise tracking. The fastest way to lose an exec's trust is to write "shipping next week" and then never mention the item again. Someone, or something, has to keep the ledger of open promises.
- Trust. One "shipped X" where X didn't ship, read aloud in a leadership meeting, ends the experiment. Every claim needs a verifiable source, mechanically checked, every week.
Chatbot copy-paste solves none of these. The system below maps each one to a specific piece of engineering: MCP pulls, committed update history, a state file of open promises, and a critic agent.
Phase 0: Prerequisites
- Claude Code installed (
npm install -g @anthropic-ai/claude-code) and authenticated, on a Pro, Max, Team, or Enterprise plan if you want the scheduled-routine and artifact-publishing steps. - Tracker access. Linear has an official MCP server at mcp.linear.app; Jira is covered by Atlassian's official MCP server. Either gives Claude targeted read access to issues, states, and dates.
- GitHub access. No MCP needed: the
ghCLI handles merged-PR and release queries from Bash, the simpler path here. - Slack access, decided deliberately. There is no official Slack MCP server. The community option is korotovsky/slack-mcp-server; the alternative is a small export script hitting Slack's API for the two or three channels where decisions actually land. A community server holding workspace credentials deserves a security review first, so many teams start with the export script.
- Metrics access, if you want "the numbers that moved" in the update. PostHog, Amplitude, and Mixpanel all ship official MCP servers (PostHog's docs here); a read-only warehouse query works too. Keep it to a handful of named metrics.
- A head start worth taking. Anthropic's knowledge-work-plugins include a Product Management plugin with a
/stakeholder-updatecommand. Install it and steal its structure, but be clear about the gap: it drafts an update from what you give it. The pulling, the promise ledger, the critic, and the schedule are what this playbook adds. - A dedicated git repo. Non-negotiable, because committed history is the memory mechanism everything else depends on.
Phase 1: The repo, which is also the memory
weekly-updates/
├── CLAUDE.md # rules for every session
├── .mcp.json # Linear/Jira (+ Slack) connections, team-shared
├── .claude/
│ ├── agents/
│ │ ├── exec-writer.md
│ │ ├── team-writer.md
│ │ ├── customer-writer.md
│ │ └── critic.md
│ └── skills/
│ └── weekly-update/
│ └── SKILL.md # the orchestration recipe
├── mapping/
│ └── roadmap-map.yml # PR/label/epic → roadmap item names
└── updates/ # committed: the narrative memory
└── 2026-07-03/
├── pulled/ # small JSON per source: tickets, PRs, decisions, metrics
├── drafts/ # exec.md, team.md, customer.md as generated
├── final/ # what the PM actually sent, after edits
└── state.json # open promises, ongoing blockers, running threads
The updates/ directory is the load-bearing decision. A scheduled run is a fresh session; it does not arrive knowing what last Friday's session wrote. Continuity is therefore something you engineer, and the mechanism is simple: last week's update, its state file, and the PM's final edited version are committed, and reading them is step one of every run. state.json is the promise ledger: every "next week we will" sentence any draft ever shipped, with its status. When this week's run starts, open promises are the first thing on its desk.
CLAUDE.md sets the rules of evidence:
# Weekly Stakeholder Updates
## Definitions
- Shipped: roadmap item whose ticket closed OR whose mapped PRs all
merged this week. Not "PR opened", not "90% done".
- Slipped: item that appeared in last week's "in flight" and did not
close, or whose due date moved.
- Blocked: item with an explicit blocker flag or a blocker recorded
in state.json.
## Rules
- Every claim must trace to an item in this run's pulled/ directory.
- Read the previous updates/<date>/ before writing anything.
- Every open promise in the last state.json must be addressed.
- Never present a previously reported item as new.
- Customer draft: shipped items only, no internal names or blockers.
Phase 2: Source pulls and the signal filter
The skill's pull step writes one small JSON file per source into pulled/:
- Tracker (Linear or Jira MCP): issues closed this week, issues in flight, issues with moved dates or blocker flags. Targeted queries, not a project dump.
- GitHub (
ghCLI): PRs merged to main this week and any releases cut, titles and links only. - Slack: messages from the named decision channels for the week, via the community MCP or your export script. Only those channels; the system summarizes decisions, it does not surveil the workspace.
- Metrics: the handful of named numbers, this week vs. last.
Then comes the filter that separates a useful update from a changelog: mapping/roadmap-map.yml. Raw merged PRs are noise at stakeholder altitude; nobody above the team lead cares that fix: retry logic in webhook consumer merged. The mapping file connects PR labels, epics, or ticket prefixes to roadmap items with stakeholder-legible names:
- roadmap_item: "Self-serve SSO"
jira_epic: PLAT-210
github_labels: ["sso"]
audiences: [exec, team, customer]
- roadmap_item: "Billing migration"
jira_epic: PLAT-198
github_labels: ["billing-v2"]
audiences: [exec, team] # customers hear about this at launch, not weekly
"Shipped" is claimed at roadmap-item level, backed by the mapped tickets and PRs. Merged PRs that map to nothing go to a triage note at the bottom of the team draft, never into the exec or customer version. The mapping file is committed, reviewed in PRs, and is the piece you will maintain most.
Phase 3: Writers and the critic
One writer subagent per audience, because "write three versions" done in one pass produces three lengths of the same document rather than three documents. Each writer gets the pulled data, the mapping file, last week's update for its audience, and the open-promise list.
.claude/agents/exec-writer.md
---
name: exec-writer
description: Drafts the one-page executive update
tools: Read
model: inherit
---
Write the exec update from pulled/ and mapping/roadmap-map.yml.
One page. Sections: Shipped / Slipped / Blocked / Metrics / Risks.
Rules:
- Roadmap-item names only, per the mapping file. No PR titles,
no ticket IDs in prose (link them).
- Address every open promise from last week's state.json by name:
done, slipped (with the reason from the tracker), or dropped.
- An item reported last week appears again ONLY if its status
changed, and the sentence must say what changed.
- Any forward commitment must be phrased as owner + item + week,
and flagged for the new state.json.
The team writer keeps ticket IDs and blocker specifics; the customer writer receives only items whose mapping entry includes the customer audience and is instructed to name zero internal systems or people.
.claude/agents/critic.md is what makes the output shippable to people who can fire you:
---
name: critic
description: Verifies every draft against the pulled data
tools: Read, Grep
---
For each draft:
1. Every "shipped" claim: find the closed ticket or merged PR in
pulled/ via the mapping file. No match → FAIL.
2. Every open promise in last week's state.json: confirm the draft
addresses it explicitly. Silent disappearance → FAIL.
3. Diff against last week's final/: any sentence conveying the
same status for the same item → FAIL.
4. Customer draft: any internal codename, person, or blocker → FAIL.
5. Every metric claim: matches pulled/metrics.json exactly.
Return PASS, or FAIL with the specific line and the specific fix.
Checks 1 and 2 are the trust mechanism, mechanized. The writer never gets the benefit of the doubt; the critic re-derives every claim from the pulled files.
Phase 4: Scheduling and passing state forward
The /weekly-update skill sequences it: read previous update and state, pull sources, run writers in parallel, run the critic (max two revision loops), write drafts and the new state.json into today's updates/ directory, commit, deliver.
Managed path: run /schedule and create a cloud routine, "Fridays 7am: run /weekly-update." The routine clones the repo's default branch, which is exactly why the memory design works: updates/ and state.json arrive with the clone. The run pushes its commits on a claude/-prefixed branch; for a repo that contains only updates, direct push is a reasonable call, but decide it explicitly. Configure source credentials as the routine's environment variables, since the cloud session doesn't have your laptop's .env. Routine runs draw on your plan's usage.
Self-hosted path: system cron on a machine you own, running headless mode: claude -p "/weekly-update" --output-format json with --allowedTools scoped to the MCP tools, gh, and the repo. Or a GitHub Actions cron with the official claude-code-action, whose docs include a scheduled daily-report example that is this workflow's close cousin.
Either way, add a Stop hook in .claude/settings.json that posts run status to Slack, so a silently skipped Friday doesn't read as "nothing to report."
Phase 5: Sharing with the team
Delivery starts with the gate, not the send button. The draft lands wherever you'll actually see it Friday morning: a Slack DM with the three drafts, or a PR against the repo. You edit for a few minutes, send the versions through your normal channels, and commit what you actually sent into final/.
That commit is not bookkeeping; it's training data. A step in next week's skill reads the last few draft-vs-final diffs and summarizes the pattern: what you always trim, what you promote. Sentences the PM cuts three weeks running should stop being written. This is how the system converges on your voice instead of drifting away from it.
For distribution beyond Slack, Claude Code can publish the update as a hosted artifact page on claude.ai, giving "this week's update" a stable URL, with organization-scoped visibility on Team and Enterprise plans. One caveat to plan around: artifact publishing works from the CLI and desktop app, not from every automated surface, so in a fully unattended setup the publish step may be the human moment anyway, which for this workflow is exactly where you want one.
Phase 6: Maintenance (what you own now)
- The mapping file. New epics, new repos, and renamed roadmap items all rot it quietly. Make "unmapped merged PRs" a standing line in the team draft so rot is visible, and gardening it a two-minute weekly habit.
- Source drift. Tracker workflows change states, someone renames the decisions channel, the community Slack server needs an update. The pull step should fail loudly (empty
pulled/file means FAIL, not an empty section). - Tone drift. Writer prompts age as the audience changes. The draft-vs-final diffs tell you when; treat
.claude/edits like code, with review, because a prompt change edits what your executives read. - The review-gate discipline. The failure mode isn't the system writing badly; it's the PM approving without reading once the drafts get good. Keep the review real: the week a wrong "shipped" goes out unedited costs more trust than the system ever saved time.
What this costs
This is the light end of the series. There's no separate API line item and no data pipeline to feed: one run pulls a few small JSON files, fans out three writers over a few thousand tokens each, and runs a critic. That sits comfortably inside a Pro or Max subscription's included usage; routine runs draw on the same allowance. Plan tier matters only at the edges: cloud routines need Pro and up, org-scoped artifact sharing needs Team or Enterprise.
The real cost is time, so say it plainly: a setup effort of wiring MCPs, writing the mapping file, and tuning four prompts until the critic and the writers stop fighting, and then a permanent tax of mapping upkeep, prompt gardening, and the weekly review itself. The system turns hours of assembly into minutes of editing; it does not turn into zero minutes, and it shouldn't.
The four practical challenges
- Setup at scale: the scale here is sources, not data. Four systems, three auth stories (one of them a community server or a script you now own), and a mapping file that turns raw activity into stakeholder-legible signal (Phases 0-2).
- Sharing with the team: the review gate plus delivery, with the artifact-publishing caveat that the publish step lives on CLI and desktop, not every automated surface (Phase 5).
- Maintenance: mapping upkeep, source drift, tone drift, and the discipline of actually reading the draft (Phase 6). The recurring human duties are small but weekly, forever.
- Cost: trivially light in tokens, entirely real in setup and upkeep time. Budget engineering hours, not API dollars.
The managed alternative
If you want status without building anything, the adjacent tools are standup and reporting products. Geekbot and its peers collect async check-ins from the team and post digests to Slack; Jira and Linear both have native reporting that will chart sprint progress, cycle times, and project status from ticket state. These are fast to adopt and well supported, and for a single-team, single-tool view they may be all you need. The structural gap is that they report the state of tickets in one system, while a stakeholder update is a narrative across systems: what shipped as customers would name it, what a Slack thread decided, which promise from two weeks ago just slipped again, told differently to an exec than to a customer. That cross-source, multi-audience, promise-tracking layer is the part these tools don't attempt, and the part this playbook builds.
Doing this with Second Axis
Everything above is the do-it-yourself path. On Second Axis, weekly stakeholder updates are a workflow in the marketplace: connect Jira or Linear, GitHub, and Slack, choose the workflow, and set the goal ("Friday exec and team updates in our voice"). It runs on schedule with the signal mapping, per-audience drafting, and narrative memory built in, and it monitors itself: a broken source connection or a failed Friday run gets found and alerted, instead of everyone quietly assuming there was nothing to report.