Executive recap of the most recent day's work (default last 24h) across the Overdeck pipeline — what shipped, what's still in flight, what needs the operator's attention, and the numbers — PLUS a separate, shareable user-facing PR-FAQ of what was released. EVERY issue is described in plain language (what it is / what it does), never a bare ID. Use when the user asks "what happened overnight / today / while I was away", "catch me up", "daily recap", "standup", "what did the flywheel do", or wants an up-leveled summary of recent activity.
Install
npx skillscat add eltmon/overdeck/pan-recap Install via the SkillsCat registry.
pan-recap — Executive Recap of Recent Work
What this skill does
Produces a short, executive-style narrative of what happened across the
Overdeck pipeline over a recent window (default: last 24 hours). It is the
answer to "what happened while I was asleep?" — written for an operator who
wants the story and the decisions, not a log.
The output has two parts: (1) an operator recap that groups work by
outcome (shipped / in flight / needs attention), explains every issue in
plain language, and ends with a one-line scoreboard; and (2) a separate,
copy-pasteable user-facing PR-FAQ that announces only the user-visible
releases in benefit language — the thing you'd actually send to users.
Output style — EXECUTIVE (read this first; it is the whole point)
This skill exists because a raw status dump — "PAN-2059 merged, PAN-2064 closed,
PAN-1919 in review" — is useless to a human who doesn't have every issue
number memorized. Up-level it.
Hard rules:
- No bare issue IDs, ever. Every single issue reference carries a 5–12 word
plain-language descriptor of what it is or does. Write
"PAN-2061 —pan strikeskipped the git-worktree step so agents landed on
main", never "PAN-2061 closed". If you cannot say what an issue does, look it
up (gh issue view) before writing the line. This rule is non-negotiable; it
is the reason the skill exists. - Lead with a one-sentence TL;DR that captures the arc of the window
("productive evening, then a 10h stall when the GLM quota ran out"). - Group by outcome, not by phase or issue number: What shipped → In
flight → Needs your attention → By the numbers. Drop empty sections. - Define pipeline jargon inline the first time it appears (strike, convoy,
verifying-on-main, and "ready": the derived state for an approved PR with
green checks that the forge reports mergeable). The reader should never need
a second doc. - Bold the deliverable, then explain it. Each bullet leads with the thing
that happened in bold, followed by the plain-language what/why. - Numbers live in one "By the numbers" line, not sprinkled through prose.
- Link every issue ID as a markdown link (per the repo issue-reference
rule) — but the link is in addition to the descriptor, never instead of it. - Call out incidents and decisions explicitly. A stall, a red main, a
quota exhaustion, an operator gate — these are the headline, not a footnote.
Anti-pattern (do NOT do this):
Last 24h: PAN-2059 merged. PAN-2064, PAN-2061 closed. PAN-1919 in review.
PAN-2063, PAN-1884, PAN-1084 planning. PAN-1722, PAN-1793, PAN-2045 strikes.
That is a list of IDs with no meaning. Rewrite it as the template below.
Two audiences — operator recap AND a user-facing PR-FAQ
The executive recap is for the operator — it includes internal plumbing,
incidents, and decisions. But every recap ALSO produces a user-facing
PR-FAQ: a short, plain-language announcement of what shipped to users,
written the way you'd tell a customer — benefit first, no jargon, no internal
issue IDs in the prose.
- PR-FAQ = "press release + FAQ." A few sentences announcing the
user-visible change(s), then 2–4 anticipated questions with answers. - Only user-facing work belongs in the PR-FAQ: new features, UX or behavior
changes, and bug fixes a user would actually notice. Internal substrate —
pipeline reliability, CI fixes, deacon/orchestrator plumbing, refactors, test
infra — stays in the operator recap and is NOT in the PR-FAQ. - If nothing user-facing shipped, say so honestly: "No user-facing releases
this window — all internal reliability work." Never dress up plumbing as a
feature. - Voice: second person, benefit-first ("You can now …"). Lead with what the
user can do that they couldn't before, then why it matters. Keep issue links
in a trailing reference line, never in the headline.
Steps
0. Set the window
Default to 24h; honor an explicit window in the user's request ("last 8 hours",
"since Friday"). Compute both an ISO timestamp (for gh/JSON filters) and a
human label.
HOURS="${1:-24}" # override if the user names a window
SINCE_ISO="$(date -u -d "$HOURS hours ago" +%Y-%m-%dT%H:%M:%SZ)"
SINCE_DATE="$(date -u -d "$HOURS hours ago" +%Y-%m-%d)"
echo "Window: last $HOURS h (since $SINCE_ISO)"1. What shipped — merged PRs + closed issues in the window
Run from the project root so gh infers the repo. PRs are the cleanest "what
shipped" signal; closed issues catch strikes and close-outs that don't open a PR.
echo "=== merged PRs in window ==="
gh pr list --state merged --limit 50 \
--json number,title,mergedAt,labels \
--jq '[.[] | select(.mergedAt >= "'"$SINCE_ISO"'")] | sort_by(.mergedAt)
| .[] | "\(.number)\t\(.mergedAt)\t\(.title)"'
echo "=== issues closed in window ==="
gh issue list --state closed --search "closed:>=$SINCE_DATE" --limit 50 \
--json number,title,labels,closedAt \
--jq 'sort_by(.closedAt) | .[] | "\(.number)\t\(.title)\t[\([.labels[].name]|join(","))]"'Also scan real commits on main (drop the bookkeeping noise — chore(records),chore(state) and chore(records) are per-issue state syncs, not work):
echo "=== substantive commits on origin/main ==="
git fetch -q origin main 2>/dev/null
git log origin/main --since="$HOURS hours ago" --no-merges \
--pretty='%h %ad %s' --date=format:'%m-%d %H:%M' \
| grep -vE 'chore\((records|state)\)' | head -402. What's in flight — active agents and in-review work
Live agents come from GET /api/agents, which reads the host's terminal
backend (Herdr by default, tmux only under terminal.backend: tmux). Listingtmux -L overdeck sessions instead misses every agent on a Herdr host.
echo "=== live agent / planning / strike sessions ==="
python3 -c "
import json,urllib.request
try:
with urllib.request.urlopen('http://localhost:3011/api/agents', timeout=30) as r:
agents=json.load(r)
except Exception as e:
raise SystemExit(f'(dashboard unavailable: {e})')
for a in sorted(agents, key=lambda a: a['id']):
if a.get('hasLiveTmuxSession'):
print(a['id'], a.get('paneState'), a.get('runtime'), a.get('model'))
"
echo "=== in-review issues (need a merge decision) ==="
python3 -c "
import json,sys,urllib.request
def get(path):
with urllib.request.urlopen('http://localhost:3011'+path, timeout=10) as r:
return json.load(r)
try:
data=get('/api/issues')
# Derived state (PAN-3917): 'ready' = approved PR, green checks, mergeable.
state_by_id={(r.get('issueId') or '').upper(): r.get('state')
for r in get('/api/issues/resource-allocated')}
except Exception: sys.exit(0)
for i in data:
st=(i.get('state','') or '').lower().replace(' ','_')
if st in ('in_review','in_progress'):
print(i.get('identifier'),'-',i.get('title','')[:70],
'(ready)' if state_by_id.get((i.get('identifier') or '').upper())=='ready' else '')
" 2>/dev/null3. Incidents & the flywheel headline
The headline metrics, plus a check for the two most common silent failures: a
red main and a stalled/erroring orchestrator.
echo "=== latest flywheel run headline ==="
LATEST=$(ls -dt ~/.overdeck/flywheel/runs/*/ 2>/dev/null | head -1)
[ -n "$LATEST" ] && python3 -c "
import json; d=json.load(open('$LATEST/latest.json'))
h=d.get('headline',{}); o=d.get('orchestrator',{})
print('run',d.get('runId'),'| orchestrator',o.get('harness'),o.get('model'),
'| bugsFixed',h.get('bugsFixed'),'prsMerged',h.get('prsMerged'),
'awaitingUat',h.get('awaitingUat'))
print('last snapshot age:', d.get('elapsedMs',0)//60000, 'min into run')
" 2>/dev/null
echo "=== main CI conclusion (red main = silent merge-gate killer) ==="
gh run list --branch main --workflow CI --limit 1 \
--json conclusion,headSha,createdAt --jq '.[] | "\(.conclusion) \(.headSha[0:9]) \(.createdAt)"'
echo "=== orchestrator alive & progressing? ==="
tmux -L overdeck capture-pane -t flywheel-orchestrator -p -S -15 2>/dev/null \
| grep -oE 'rate_limit_error|Limit Exhausted|reset at [0-9: -]+|auto_retry_end' | tail -3 \
|| echo "(no orchestrator session)"A frozen "Last tick" older than ~20 min, or repeated rate_limit_error /Limit Exhausted in the orchestrator pane, is an incident — surface it as
the headline, with the provider/plan and reset time if shown.
4. Enrich EVERY issue with plain-language context (MANDATORY)
Collect every issue ID that will appear in the recap. For each one you don't
already understand, fetch its title and write a one-clause descriptor of what it
is / does. Never emit an ID you can't describe.
for n in <every issue number you will mention>; do
printf 'PAN-%s\t' "$n"
gh issue view "$n" --json title,state -q '.title + " [" + .state + "]"'
doneTranslate each title into operator English. Examples of the transform:
bug(strike): pan strike ... skips git worktree add — agent lands on main→
"PAN-2061 —pan strikebuilt a workspace but skipped the git-worktree
step, so strike agents landed onmainand self-aborted."[EPIC] Backlog pickup gate — operator Plan→Release row + AI Objection→
"PAN-2059 — lets the operator pull a backlog issue into the pipeline;
first dashboard slice merged, AI 'objection' state still to come."
5. Compose the executive summary
Write it in the template below. Keep it skimmable — a busy operator should get
the whole picture in ~20 seconds. Omit any section that's empty.
6. Write the user-facing PR-FAQ
From the "what shipped" set, keep only the user-facing items (apply the
classification rule under Two audiences above — drop everything that's
internal substrate). For each survivor, write one benefit-first line in plain
user English. Then add a short FAQ of 2–4 questions a user would actually
ask ("Do I need to do anything?", "Where do I find it?", "Did the way X works
change?"). If the window shipped nothing user-facing, write the single honest
line and skip the FAQ — do not invent user value out of plumbing.
Output template
## Executive recap — last <window>
**TL;DR:** <one sentence capturing the arc of the window — the win and the catch>
**What shipped (landed on main):**
- **<deliverable in bold>** — <plain-language what/why>. ([PAN-XXXX](url))
- **<reliability/bug fix>** — <what was broken, what it means>. ([PAN-XXXX](url))
**In flight (parked, not lost):**
- **In review:** <descriptor> ([PAN-XXXX](url))
- **Planning:** <descriptor>; <descriptor>; <descriptor>
- **Strikes (direct-to-main fixes):** <descriptor>; <descriptor>
**Needs your attention:**
- <incident / decision / gate, stated as a consequence, with the action implied>
**By the numbers:** <N bugs fixed> · <N merged> · <N in flight> · <cost/uptime/incident facts>. Main is <green/red>.Then, as a separate, copy-pasteable block (an operator can share this verbatim
with users/stakeholders without editing):
---
## What's new — shareable (PR-FAQ)
*(when nothing user-facing shipped):*
No user-facing releases this window — the work was all behind-the-scenes reliability.
*(when there are user-facing releases):*
**<Headline: the user-visible capability, stated as a benefit>**
<1–3 sentences: what you can now do that you couldn't before, and why it helps.
Plain language, second person, no issue IDs in the prose.>
- **<Capability / visible fix>** — <benefit-first description>.
- **<Capability / visible fix>** — <benefit-first description>.
**FAQ**
- **<Anticipated question?>** <Answer.>
- **<Anticipated question?>** <Answer.>
*Shipped: [PAN-XXXX](url), [PAN-XXXX](url)*Example PR-FAQ (illustrative)
You can now pull backlog items into the pipeline yourself.
A new pickup control on each issue lets you move a backlog item straight into
planning from the dashboard — no CLI, no waiting on the flywheel to choose it.
- Pi / GLM agent conversations are now viewable — the conversation panel
used to render blank for non-Claude agents; it now shows their transcripts.- Long transcripts no longer get cut off — conversations over 10 MB used
to truncate in the live view; you now see the whole thing.FAQ
- Do I need to do anything? No — it's live on next dashboard load.
- Where's the pickup control? On each issue card and in the issue cockpit.
When to surface this
- "What happened overnight / today / while I was away / asleep?"
- "Catch me up", "give me a recap", "standup", "daily summary"
- "What did the flywheel do?" / start-of-day check-in after autonomous running
- After a long stretch where the operator was offline
When NOT to use this
- For a live "where are we right now" snapshot, use
/pipeline-status(the
dense status board) — recap is about the recent past, not the current frame. - For a single issue's deep history, use
pan show <id>/ the issue drawer.
Notes
- Read-only. Gathers from
gh, git, the dashboard API, flywheel run
snapshots, and theflywheel-orchestratortmux pane. Changes nothing. - The
/api/issuesand orchestrator-pane steps need the dashboard up onlocalhost:3011and a liveflywheel-orchestratorsession; both degrade
gracefully (the recap just omits that slice if absent). - Scope to the current project's tracker. For multi-project recaps, run the
gather steps per repo and label each section with its project.