skills/night-shift-retro/SKILL.md
--- name: night-shift-retro description: End-of-run retrospective for autonomous swarm runs (night-shift, ralph --afk, planner-worker --afk, agentic-e2e-flow). Writes a sibling markdown + JSON artefact under .nightshift/retros/, captures matrix-row failures and failure modes per slice, files SYSTEM gaps as issues against ronan-skills or factory, files PROJECT gaps as ready-for-human issues against the current repo, and pings Pushover/Telegram with the retro URL as a deep link. Defers to /ro:repo
npx skillsauth add RonanCodes/ronan-skills skills/night-shift-retroInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
End-of-night SYSTEM/PROJECT retrospective. The bookkeeping skill that turns "11 PRs merged" into "11 PRs merged AND 3 ronan-skills issues filed AND 1 dataforce ready-for-human follow-up AND a JSON artefact the future cloud factory can ingest".
The retro exists because run-level signals (matrix-row failure shape, auto-split events, token burn rate per slice) die in the morning Slack window unless something captures them. The first time you skip the retro, the next regression hides until a human stumbles on it. See [[close-the-loop-verification-matrix]] for the matrix this retro tracks failures against and [[night-shift-retro-and-day-shift]] for the async chain it sits inside.
Fires automatically at the end of:
/ro:night-shift (Phase 7, unless --no-retro)/ro:ralph --afk against a real backlog (not --mode single, not --plan-only)/ro:planner-worker --afk against a real backlog/agentic-e2e-flow against a real backlogSkip when:
--plan-only (nothing actually ran)/ro:ralph --mode single standalone)--no-retro on the dispatcherInvoke manually any time to reconstruct a retro from recent git + GH state (e.g. the orchestrator died mid-run and the retro never fired).
# Auto-invoked at end of /ro:night-shift; runs against the most recent run
/ro:night-shift-retro
# Reconstruct a retro from a specific run-id (must exist under .nightshift/runs/)
/ro:night-shift-retro --run-id 20260519-2330
# Generate retro but do NOT commit the markdown + JSON to the project
/ro:night-shift-retro --no-commit
# Generate retro and skip cross-repo issue creation (useful for dry-run)
/ro:night-shift-retro --no-issues
# Reconstruct against a different repo
/ro:night-shift-retro --repo ~/Dev/ai-projects/dataforce
Read <repo>/.ronan-skills.json for the retros.* block. Default shape if missing:
{
"retros": {
"commit_to_project": "ask",
"open_followup_issues": true,
"auto_skill_retro": false
}
}
commit_to_project first-run prompt via AskUserQuestion (header "Retros"):
"Commit night-shift retro artefacts (markdown + JSON) under
.nightshift/retros/to this repo?"Options:
- Yes — retros are tracked. Useful for audit + future cloud-factory ingest.
- No — retros live only in
.nightshift/which gets gitignored. Useful for repos where the retro folder would create noise (work repos, throwaway prototypes).
Persist the answer to .ronan-skills.json. Subsequent runs use the persisted value.
Resolve repo mode via the [[repo-mode]] 4-line snippet. Work-mode behaviour:
.nightshift/retros/ regardless of commit_to_project; gitignore them under work modeThe retro's input is the orchestrator's run trail. Sources in order of preference:
<repo>/.swarm/run-*.md (planner-worker postmortem)<repo>/.swarm/nightsheet-*.md (night-shift nightsheet)<repo>/.ralph/<name>.session.md (ralph session log)<repo>/.swarm/status.md (live state at exit)gh pr list --state merged --search "merged:>=<started-at>" --json number,title,body,mergedAt,mergeCommit (PR side, ground truth)gh issue list --state closed --search "closed:>=<started-at>" --json number,title,labels (issue side)<repo>/.swarm/logs/*.log (per-worker logs — for failure-mode classification)Cross-reference: every merged PR should map to a closed slice issue via Closes #N. Mismatches go in failures[] as misalignment.
If --run-id is given, scope the search to that timestamp window. Otherwise, walk back from the most recent dispatcher exit (look for the most recent nightsheet-*.md or run-*.md).
For each slice the run touched, classify the outcome:
| outcome | Definition |
|---|---|
| merged | PR merged, slice issue closed, matrix passed |
| blocked | Slice escalated to human; PR open or branch sitting |
| deferred | Slice picked but not finished (timed out, wave ended) |
| auto-split | Slice failed three attempts; planner re-decomposed into sub-slices (per [[close-the-loop-verification-matrix]] retry-and-split state machine) |
For each blocked or auto-split slice, classify the failure mode (use the per-slice prompt below if you can't tell from logs):
| mode | Symptom |
|---|---|
| thrashing | Same matrix row fails 3+ times across slices in this run |
| proxy-gaming | Tests passed but evidence shows the spec wasn't met (e.g. e2e asserts page renders, real flow loops) |
| misalignment | Worker built the wrong thing despite passing tests |
| non-convergence | Three attempts, all fail, no two failures look alike |
| context-drift | Late slice ships against an assumption from early slice that no longer holds |
| runaway-resource | Slice burned >2× expected tokens or wall-clock |
| other | Anything else worth naming |
Per-slice classifier prompt (used when log signal is ambiguous):
You are classifying a swarm slice failure for a night-shift retro.
Slice: #<num> — <title>
Outcome: <merged|blocked|deferred|auto-split>
Matrix failures (categories that failed in any attempt): <list>
Per-attempt logs: <inline>
Pick ONE failure mode from: thrashing, proxy-gaming, misalignment,
non-convergence, context-drift, runaway-resource, other.
Output JSON only:
{"mode": "<one-of-above>", "narrative": "<one to two sentences>"}
For each failure and each pattern of matrix_fail_categories repeated across slices, draft an action item:
{
"title": "...",
"gap_class": "SYSTEM | PROJECT | UNCLEAR",
"source_repo": "ronan-skills | factory | <project-name>",
"severity": "high | med | low",
"proof_ref": "PR#156 or issue#229",
"created_issue": null
}
Classification heuristics:
source_repo: ronan-skills. "The factory reconciler missed an inflight slice race" → SYSTEM, source_repo: factory.source_repo: dataforce.severity is a rough triage hint:
| severity | When |
|---|---|
| high | Blocked a merge, recurred 3+ times, or surfaced in production |
| med | Recurred 2+ times, or blocked a single merge |
| low | One-off observation, worth noting but not urgent |
Compose the JSON per the schema in [[night-shift-retro-and-day-shift]] § "Artefact pair". Write to:
<repo>/.nightshift/retros/<YYYY-MM-DD>-<run-id>.json
<repo>/.nightshift/retros/<YYYY-MM-DD>-<run-id>.md
Markdown headers (each section mirrors a JSON block):
# Night-shift retro — <YYYY-MM-DD> run <run-id>
## Run summary
- Dispatcher: <dispatcher>
- Mode: <mode>
- Repo: <repo>
- Branch: <branch>
- Started: <ISO8601>
- Ended: <ISO8601>
- Duration: <wall_clock_minutes>m
- JSON sibling: `.nightshift/retros/<YYYY-MM-DD>-<run-id>.json`
## Stats
| Metric | Value |
|---|---|
| Issues picked | <N> |
| PRs merged | <N> |
| PRs blocked | <N> |
| Issues deferred | <N> |
| Wall clock | <N>m |
| Approx tokens burned | <N> |
## Per-slice breakdown
| Issue | Title | Outcome | PR | Retries | Matrix fails | Wall clock |
|---|---|---|---|---|---|---|
| #234 | role picker | merged | #156 | 1 | e2e, logging | 22m |
...
## Failure modes hit
- #234 — thrashing — e2e + logging failed twice consecutively before the third attempt passed. Likely a missing logging contract row in the matrix.
...
## Action items
- [ ] **[SYSTEM][ronan-skills][high]** Add D1 migration-slot pre-assignment to planner-worker dispatch prompt (proof: PR#156)
- [ ] **[PROJECT][dataforce][med]** Onboarding router-shadowing: investigate `/onboarding/role` redirect cycle (proof: issue#229)
- [ ] **[UNCLEAR]** Worker for #240 spent 38m investigating Nango SDK before producing useful diff; revisit if pattern recurs
## SYSTEM gaps surfaced
- The verify-logging contract caught two silent catch blocks; matrix row 5 worked. No SYSTEM gap.
- The e2e matrix row caught zero failures in three slices that shipped routing changes; SYSTEM gap: the slicer's "e2e mandatory" block isn't reaching workers consistently. File against ronan-skills.
## Cross-repo issues opened
- ronan-skills#<N> — "[from-retro] Pre-assign Drizzle migration slots in planner-worker dispatch"
- dataforce#<N> — "[ready-for-human] Onboarding router-shadowing investigation"
retros.morning_briefing: true)Default true. The briefing is the "wake-up-to-coffee" artefact: a single self-contained HTML page that Skip (or Ronan) opens first thing to see what shipped, what's open, and what to do today. It's also the deep-link target for the Pushover + Telegram tail call (more useful than the raw markdown).
Write to:
<repo>/.nightshift/briefings/<YYYY-MM-DD>[-<run-id-suffix>].html
If a briefing already exists for the date (e.g. you ran morning + evening night-shifts the same day), suffix the filename with the run-id so they don't collide:
.nightshift/briefings/2026-05-19.html # morning run
.nightshift/briefings/2026-05-19-evening.html # evening run
.nightshift/briefings/2026-05-19-20260519T1729Z.html # if run-ids needed for disambiguation
Section shape (every briefing has these in this order):
night-shift briefing brand, date / repo / run-id meta.Good morning. <N> PRs shipped, <signal>, <signal>.), one paragraph follow-up summarising the run's shape.merged / ready-for-agent / deferred.<verb> <noun>: <specific command or link>.<repo>/.nightshift/assumptions.md and render every row as a card: the decision, what was chosen, the rationale, and whether it's reversible. Lead with a one-line count (I made <N> calls for you, <M> still want a yes/no). This is the section the user scans first after an AFK self-grill run, so put it high and make each row a tap-to-confirm/correct prompt. Omit the section entirely when the assumptions file is absent (interactive run).needs-human issue filed by workers + any PR review awaiting author action./ro:cf-ship for Cloudflare, /ro:fly-deploy for Fly).Place the Assumptions section right after the Hero card on self-grill runs (it's the highest-signal thing the user wakes up to), shifting the rest down.
Reuse the GitHub dark palette CSS from any prior briefing on the same repo (look for .nightshift/briefings/*.html) so visual identity stays consistent across runs. If no prior briefing exists, use the canonical CSS block documented at [[night-shift-briefing-template]] (or fall back to a minimal one-pane stylesheet).
Implementation hint: build the HTML via a Python heredoc or a small briefing.py helper that reads the retro JSON + gh pr view for each merged PR. Don't shell out to a templating engine; the briefing is a single self-contained page.
Path emitted to .nightshift/last-briefing-url.txt (file:// URL) so US-7 picks it up.
open_followup_issues: true)For each action item with created_issue: null:
case "$gap_class:$source_repo" in
SYSTEM:ronan-skills)
issue_num=$(gh issue create -R RonanCodes/ronan-skills \
--title "[from-retro] $title" \
--label "from-retro" \
--label "source-repo:$current_repo" \
--label "run-id:$run_id" \
--body "$(retro_body_for $item)" \
| grep -oE '[0-9]+$')
;;
SYSTEM:factory)
issue_num=$(gh issue create -R RonanCodes/factory \
--title "[from-retro] $title" \
--label "from-retro" \
--label "source-repo:$current_repo" \
--label "run-id:$run_id" \
--body "$(retro_body_for $item)" \
| grep -oE '[0-9]+$')
;;
PROJECT:*)
# Current repo only; ready-for-human (NOT swarm) — must be grilled first
issue_num=$(gh issue create \
--title "$title" \
--label "ready-for-human" \
--label "from-retro" \
--body "$(retro_body_for $item)" \
| grep -oE '[0-9]+$')
;;
UNCLEAR:*)
issue_num="" # stays in retro markdown only
;;
esac
Body template for retro_body_for:
## Source
Surfaced by `/ro:night-shift-retro` against `$current_repo` run `$run_id` (<ISO8601>).
Retro artefact: `<repo>/.nightshift/retros/<date>-<run-id>.md`
## Failure context
<narrative from the failure[] entry, or pattern summary if synthesised>
## Proof
<proof_ref — link to PR / issue / log excerpt>
## Severity
<severity>
---
This issue was filed automatically. If it does not represent real work, close with `wontfix` and leave a comment so the retro classifier can learn.
Update the JSON's action_items[].created_issue with the newly-minted issue number.
commit_to_project: true)cd "$repo"
git add .nightshift/retros/<date>-<run-id>.{md,json}
git commit -m "📝 docs(retro): night-shift run $run_id ($N merged, $M blocked, $K followups)"
Use the repo's commitlint format. The dataforce repo uses emoji-conventional with the 📝 docs prefix; ronan-skills + factory use the same. Skip the commit under --no-commit or commit_to_project: false.
Deep-link target priority (most useful first): briefing HTML > retro markdown > completion-report HTML. If the briefing was rendered (US-4b), Pushover + Telegram point at it; otherwise fall back to the retro markdown.
If the dispatcher (night-shift / ralph / planner-worker / agentic-e2e-flow) has already fired Pushover + Telegram, the retro patches the message via --update:
url="file://$repo/.nightshift/briefings/$date.html"
[ -f .nightshift/last-briefing-url.txt ] && url=$(cat .nightshift/last-briefing-url.txt)
bash ~/Dev/ronan-skills/skills/pushover/scripts/notify.sh \
--update \
"$(echo "$dispatcher message" + retro summary line)" \
--url "$url"
If the dispatcher has NOT yet fired the notifications, prepare the deep-link path so the dispatcher's tail call picks it up:
echo "$repo/.nightshift/briefings/$date.html" > .nightshift/last-briefing-url.txt # preferred
echo "$repo/.nightshift/retros/$date-$run_id.md" > .nightshift/last-retro-url.txt # fallback
Dispatchers read last-briefing-url.txt first, then last-retro-url.txt, and inject whichever exists into their Pushover + Telegram tail call. The Telegram script does not accept --url; embed the URL inline in the message body instead.
auto_skill_retro: true)When the run touched a SKILL.md file (rare in autonomous runs, but possible when the swarm targets ronan-skills itself), invoke /ro:skill-retro --since <run-start> automatically after the retro is written.
Default auto_skill_retro: false keeps /ro:skill-retro as the manual consolidation gate — they have different shapes:
/ro:night-shift-retro — run-level signals (what happened during ONE swarm run)/ro:skill-retro — skill-level evolution (which SKILL.md files need editing across many runs)| Condition | Message |
|---|---|
| No .swarm/, .ralph/, or recent merged PRs | "No run data found. Pass --run-id <id> or run from a repo that just finished a /ro:night-shift / /ro:ralph --afk / /ro:planner-worker --afk." |
| .ronan-skills.json missing | Initialise with the default retros.* block and continue. |
| gh auth failed AND open_followup_issues: true | "gh CLI not authed; skipping cross-repo issue creation. Action items captured in markdown only. Re-run with gh auth refresh to file follow-ups." |
| Repo mode unset | Run the [[repo-mode]] first-run prompt, persist, continue. |
| Two retros for the same run_id | "Retro already exists at <path>. Pass --run-id <new-id> or delete the existing file first." |
ready-for-agent / swarm. PROJECT issues need a human grill round; the ready-for-human label gates that./ro:night-shift-retro and /ro:skill-retro in one commit. Different shapes, different cadence, different repos. Keep them apart.--plan-only runs. Nothing happened; there's nothing to retro. The dispatcher should skip the invocation./ro:day-shift — the morning sibling that grills the backlog for the next run/ro:skill-retro — the manual SKILL.md consolidation gate (different shape)/ro:night-shift — the primary dispatcher; Phase 7 invokes this skill/ro:planner-worker — single-wave dispatcher; emits failures[] entries the retro reads/ro:pushover + /ro:telegram — notification deep-link recipients/onboarding/role redirect loop). Without a retro the SYSTEM gap (e2e mandatory in every slice body) would have surfaced one regression at a time. The retro turns the post-mortem into structured data: SYSTEM action items file against ronan-skills / factory; PROJECT action items file against the current repo with ready-for-human. Born alongside [[close-the-loop-verification-matrix]] + [[night-shift-retro-and-day-shift]]..nightshift/briefings/<date>.html and made the Pushover + Telegram deep-link target. The retros.morning_briefing: true config flag had been dangling, no skill consumed it; this US wires it up. Surfaced during the evening night-shift run that merged 7 PRs and did not produce a briefing automatically. Also clarified that Telegram's notify script does not accept --url, so the briefing path is embedded inline in the message body.development
--- name: worktree description: Coordinate multiple agents on one repo via a worktree-lock pool, so two agents never clobber each other's working tree. Acquire the first free slot (main, then beta/gamma… worktrees, created on demand), work there on your own branch, release when you've pushed. Use before modifying any repo that might be in use by another agent (factory, dataforce, etc.), or whenever you're told a repo is being worked on. Backed by `ro worktree`. category: development argument-hin
testing
--- name: ship description: Ship a feature branch the local-CI-first way — run the full local gate, push, open a PR, squash-merge, then deploy, without waiting on GitHub Actions. Use when a branch is ready for main and you want it merged and deployed now. Reads CI policy from `ro ci` (default skips remote CI because GitHub Actions billing keeps hitting limits). Sibling to /ro:gh-ship (waits on GitHub checks) and /ro:cf-ship (the deploy half). Triggers on "ship it", "ship this", "merge and deploy
testing
--- name: setup-logging description: Set up (or audit) the observability stack in a TanStack Start + Cloudflare Workers app so it is "diagnosable by default" — structured logging (logtape) with a request context carrying trace_id + userId + tenant/orgId, a trace_id propagated FE→BE→logs→Sentry→PostHog, Cloudflare Workers observability enabled, and Sentry + PostHog wired. Two modes: `setup` (wire it into an app) and `audit` (check an existing app + report gaps). Use when scaffolding a new app, wh
development
Manage credentials INSIDE the active ~/.claude/.env file — read which token/account to use for a given app (Simplicity vs Dataforce vs Ronan-personal), add or update a secret WITHOUT it passing through the chat (an interactive Terminal window prompts for it), and track secrets that were exposed in a transcript so they get rotated. Sibling to /ro:context (which switches WHICH env file is active). Use when the user wants to add an API key/token/secret, asks "which credential do I use for X", needs the env organized/labelled, or a secret was pasted into the chat and should be rotated.