# Good Morning

> Morning kickoff wrapper that runs sprint-snapshot (daily tag), daily-standup-prep (with sprint burndown), and daily-briefing (report only — no focus-block suggestions) in order. Re-runs idempotently — existing same-day artifacts are overwritten in place. Use when user says "good morning", "morning routine", "kick off my day", "start my day", or invokes /good-morning.

- Skill: `risadams/good-morning` (Agent Skill)
- Install (CLI): `npx skillmds@latest add risadams/good-morning`
- Raw SKILL.md: https://api.skillmd.com/api/skills/risadams/good-morning/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Docs & Writing
- Author: risadams (https://skillmd.com/u/risadams)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/risadams/good-morning

---


# Good Morning

Single-command wrapper that runs the user's three morning skills in order. Owns no business logic of its own — every artifact is produced by the delegated skill. This skill exists so the user types one command instead of three.

## Quick start

```text
/good-morning                 → run all three phases for default team(s)
/good-morning Aurora          → pass team override down to sprint-* skills
/good-morning --skip-snapshot → skip phase 1 (e.g. snapshot already captured today)
```

Any extra args (team names, `--inc`, `--sprint`, `--days`, etc.) are forwarded to the sub-skills that recognise them. The wrapper itself only consumes `--skip-snapshot`, `--skip-calendar`, `--skip-standup`, `--skip-briefing`.

## Workflow

```text
- [ ] Phase 1:   sprint-snapshot --phase daily            (Jira → vault canvas + md + jsonl)
- [ ] Phase 1.5: team-calendar OOO digest                 (Confluence ICS → who's out this window)
- [ ] Phase 2:   daily-standup-prep --include-sprint-pulse (per-team standup report + burndown)
- [ ] Phase 3:   daily-briefing --no-focus-blocks         (Outlook 24h recap + today's schedule)
- [ ] Phase 4:   One-line summary of artifacts written
```

Run phases **sequentially, never in parallel**. Phase 2 depends on Phase 1's JSONL trend row (burndown forecast reads it). Phase 1.5 runs between them so its OOO digest can flow into both Phase 2 (capacity/coverage) and Phase 3 (FYIs). Phase 3 is independent but ordered last by design.

**Auto-continue between phases.** When a phase completes, immediately invoke the next one — do not pause for user acknowledgment, do not summarise mid-flight, do not ask "shall I continue?". The user invoked /good-morning expecting the whole chain to run unattended. The only legitimate stop points are:

1. A hard failure in Phase 1 (abort the wrapper).
2. A sub-skill raising a genuinely blocking `AskUserQuestion` that the wrapper cannot answer on the user's behalf (e.g. missing roster CSV with no source path). In that case, forward the prompt as-is.
3. **`daily-briefing`'s stale-carryover prompt or recently-closed re-surface prompt** — these are intentional user decisions about specific action items (still open vs done vs cancel vs defer; re-spawn vs false re-surface). Forward as-is; never auto-answer "Still open" on the user's behalf, since silently keeping a stale item is exactly the failure mode these prompts exist to prevent. See `daily-briefing/SKILL.md` § "Recently-closed dedup rule" and § "Stale carryover rule".

Overwrite prompts, "continue?" prompts, and any other yes/no the wrapper can answer for the user should be auto-answered with the continue/overwrite option — never paused for input.

### Phase 1 — Sprint snapshot (daily tag)

Invoke via `Skill`: `sprint-snapshot` with `--phase daily` (preserves literal `daily` in filenames per sprint-snapshot's phase rule). Forward any team / inc / sprint args the user supplied.

Tell the sub-skill the overwrite behavior up-front so it doesn't prompt: *"This run is from /good-morning — if `daily.canvas` or `daily.md` already exist for today, overwrite in place. Do not prompt."* The JSONL trend log is append-only — sprint-snapshot already handles that correctly.

If sprint-snapshot fails (Jira down, roster missing), surface the error and **abort the wrapper** — phases 2 and 3 expect the snapshot to exist.

### Phase 1.5 — Team-calendar OOO digest

Fetch the team's Confluence Team Calendar (PTO / OOO / Travel / Holiday) and build a short digest of who's out during the briefing window. This is the canonical out-of-office source — without it, vacations are only caught by chance from an email (e.g. John Doe's 6/23–6/29 PTO was missed by the schedule view and only surfaced via a manual status email). The digest flows into **both** Phase 2 (capacity/coverage) and Phase 3 (FYIs).

**Resolve the ICS URL** from memory: read `reference_team_calendar_ics.md` and take the `**Team Calendar ICS URL:**` line. If the memory is missing, skip this phase with a one-line warning in the Phase 4 summary (`⚠ Phase 1.5 skipped: no team-calendar ICS configured — add reference_team_calendar_ics.md`) — do **not** prompt or abort; the wrapper still produces all artifacts without it.

**Fetch + parse** with a single Bash call (read-only; never write the ICS into the vault). Save the feed to a scratch path under an allowed working dir (`C:/temp/teamcal.ics`), then parse with Python (`PYTHONIOENCODING=utf-8`). Parsing rules — these are load-bearing, get them wrong and the digest lies:

- **Unfold continuations first** — `re.sub(r'\n[ \t]', '', text)` after normalising `\r\n`→`\n`. Confluence wraps long lines.
- **All-day events; `DTEND` is EXCLUSIVE** (RFC5545). Subtract one day from `DTEND` for the human "last day out" — `DTSTART 20260629 / DTEND 20260704` = out **through 7/3**, back 7/4. Getting this wrong reports people back a day early.
- **Window filter:** keep events where `start <= horizon AND end_exclusive > today` (Pittsburgh local). Horizon = the current sprint's `end_date` from `_sprint.md` if available, else `today + 10 days`.
- **Attribute via the `CN=` field, NOT `SUMMARY`.** This is the load-bearing parsing fact: Confluence PTO events almost always have a bare `SUMMARY:PTO` and carry the person's name in the `ORGANIZER`/`ATTENDEE` line as `CN="Last, First"` (e.g. `ORGANIZER;X-CONFLUENCE-USER-KEY=...;CN="Doe, John";...`). Extract `CN="([^"]+)"` and match **last name AND first name** against the team roster CSV (`{{rosters_dir}}/<Team>.csv`). Matching only `SUMMARY` finds nobody — that was the bug that let John Doe's PTO go unsurfaced. Fall back to `SUMMARY` text only for the rare events that *do* name a person there (`Parrott - PTO`). Split into **attributed** (CN or summary matched a roster member) and **unattributed** (no roster match — counted as "N other platform-org entries", never invented into a roster name).
- **Merge adjacent same-person ranges.** One person often has several back-to-back PTO VEVENTs (e.g. 6/22–6/26 + 6/29–7/3). Collapse contiguous/overlapping ranges per person so the digest reads "John Doe PTO 6/22 → 7/3" rather than two fragments — otherwise the "last day out" understates the absence.
- **Category** comes from `CATEGORIES` (`PTO`, `Holiday`, `Travel`, `Conference`, `Appointment`, `Work Schedule`). Holidays apply to everyone; treat separately from individual PTO.

Build an **OOO digest** object: for each roster member out in-window → `{name, type, first_day, last_day_inclusive}` (after merging adjacent ranges per person); plus any team-wide Holiday; plus the unattributed count. Keep it to the roster + holidays — the calendar is the whole platform org (~960 events), so don't dump everything; the digest is just the handful of roster names plus holidays.

**Forward the digest** to the next two phases (pass it in the invocation text, since sub-skills don't read it themselves yet):

- **To Phase 2 (standup):** *"Team-calendar OOO this window: <digest>. Fold into the sprint-pulse capacity read and the scrum-master suggestions — a member who is OOO should not be flagged as 'no activity / confirm blocked', and their in-flight tickets are coverage risks. Reflect reduced effective capacity for OOO days."*
- **To Phase 3 (briefing):** *"Team-calendar OOO this window: <digest>. Render these as FYI bullets (`[[@Person]] OOO {{dates}}`) in the briefing, and factor team-wide Holidays into the schedule. Do not spawn action items from OOO (per daily-briefing § 3a — 'reach out to backups' is conditional, not a task)."*

If the fetch fails (TLS, network, non-200, empty body), **do not abort** — emit `⚠ Phase 1.5 skipped: team calendar unreachable (<reason>)` in the Phase 4 summary and continue. OOO data is high-value but not load-bearing for the other phases.

### Phase 2 — Standup report (with sprint burndown)

Invoke via `Skill`: `daily-standup-prep`. Pass `IncludeSprintPulse=true` explicitly so the clarity-council burndown chart + statistics-expert forecast + scrum-master suggestions run (this is the "sprint burndown" the user wants). Forward team / days args from the wrapper invocation.

Tell the sub-skill: *"Overwrite today's standup file(s) without prompting — /good-morning expects idempotent re-runs."*

If daily-standup-prep partially fails (e.g. GitLab unreachable), let it produce what it can — its degraded-mode output is still useful. Surface any warnings in Phase 4's summary.

### Phase 3 — Daily briefing (report + rollups, no focus blocks)

Invoke via `Skill`: `daily-briefing`. Phase 3 must produce **all three artifacts**:

1. **Daily report** — `{{vault_root}}\📅\YYYY\MM\YYYY-MM-DDD.md` (daily-briefing step 4)
2. **Weekly rollup** — `{{vault_root}}\📅\YYYY\MM\YYYY-W{NN}.md` (daily-briefing step 5)
3. **Monthly rollup** — `{{vault_root}}\📅\YYYY\YYYY-M{MM}.md` (daily-briefing step 6)

**Skip step 9 (focus-block proposal) entirely.** Tell the sub-skill: *"/good-morning is the caller — run steps 1 through 6 (daily report + weekly rollup + monthly rollup). Skip step 9 (focus blocks). Do NOT propose calendar blocks, do NOT create any meeting drafts. Stop after the monthly rollup is written."*

Same overwrite rule: if any of the three notes already exist for today, overwrite in place — no prompts.

### Phase 4 — Summary

Print one block summarising what was produced:

```text
=== Good morning — 2026-05-18 ===
✓ Snapshot:  [daily.canvas]  [daily.md]              (+1 trend row)
✓ OOO:       John Doe (PTO→6/29), Parrott (PTO→6/23)   (2 out, +1 unattributed)
✓ Standup:   [Aurora 2026-05-D18.md]                 (burndown ↘ on-track)
✓ Briefing:  [2026-05-D18.md]  [2026-W20.md]  [2026-M05.md]  (12 unread, 4 meetings today)
```

Use markdown links so the user can click straight into the vault. The `OOO:` line reflects the Phase 1.5 digest — if Phase 1.5 was skipped or the feed was unreachable, replace it with the `⚠ Phase 1.5 skipped: <reason>` line instead.

**Then run the daily-note section-completeness check (belt-and-suspenders).** `daily-briefing`'s § "Pre-save section checklist" should guarantee these, but the chart silently vanished from 4 consecutive June 2026 notes before the checklist existed — so the wrapper re-verifies. `Read` today's daily note (`{{vault_root}}/📅/YYYY/MM/YYYY-MM-DDD.md`) and confirm it contains all four mandatory surfaces (see [[feedback_daily_note_mandatory_sections]]):

1. A `#### Day at a glance` Mermaid `gantt` block.
2. A `#### Email triage at a glance` Mermaid `pie` block.
3. A `### 👥 Team Daily Notes` standup wikilink — unless `Glob("{{vault_root}}/Scrum Teams/**/YYYY-MM-DD.md")` returns zero matches for today.
4. A `### 📊 Cross-day context` block with the `![[Daily Notes Dashboard.base#Recent (cards)]]` + `![[TODO#By tag]]` embeds.

If any is missing, `Edit` it into the note in place (reconstruct the gantt from the schedule section, the pie from the email-triage counts, the standup link from the `Glob` result) and append a one-line note to the Phase 4 summary: `↻ Repaired missing daily-note sections: <list>`. Do NOT prompt — the sections are mandatory, so repair silently. If this check repairs sections on >1 run, that signals `daily-briefing`'s pre-save checklist is being skipped and needs tightening.

> ⛔ **When reconstructing the gantt, never put a colon (`:`) inside a task label.** Mermaid splits each task line on the first colon to find the `:HH-mm, NNmm` metadata, so a label like `Gap 09:00-10:00 :09-00, 60mm` fails with `Invalid date`. Write time ranges in labels with periods (`Gap 09.00-10.00`, `Lunch gap 12.00-15.00`) and keep the only colon in front of the metadata. See `daily-briefing/REPORT_TEMPLATE.md` § "Day at a glance" for the full rule.

**Then run the action-item post-filter pass (belt-and-suspenders).** `daily-briefing` should have applied the verb test and addressed-to-user classifier from its § 3a and the phantom-carryover guard from § 6, but those rules occasionally let an FYI slip through as a `- [ ]` item. Re-read today's daily note and audit the `## ✅ Open Action Items` section:

1. `Read` `{{vault_root}}/📅/YYYY/MM/YYYY-MM-DDD.md` and extract every `- [ ]` line from `### Carrying over (still open)` and `### New / from window`.
2. For each item, re-apply the heuristics from `daily-briefing/SKILL.md` § 3a:
   - **Verb test** — does the bullet name an actionable verb directed at the user (reply / review / approve / merge / sign / decide-with-deadline / schedule / draft / fix / investigate / RSVP)? Status verbs (is, was, has been) fail.
   - **Addressed-to-user test** — is the ask aimed at the user specifically, or is it broadcast / informational / conditional?
   - **OOO / FYI patterns** — "reach out to X backups" (X is OOO, no actual task), "read [meeting notes]" with no decision pending, "verify Y was sent" with no time-criticality, "decide: [topic]" with no external deadline or stakeholder waiting.
   - **Phantom test** — does the bullet contain a person/MR#/Jira ticket that does NOT appear anywhere in today's `📨 Email Triage` section or `🗓️ Today's Schedule`? If so, there's no fresh signal backing it and it's likely stale.
3. If any items fail the audit, `Edit` the daily note:
   - Remove the bullet from `## ✅ Open Action Items`.
   - Insert it as a plain bullet (no checkbox) under `## 📝 Notes & Follow-ups` if the item still has informational value (OOO notice, status awareness).
   - Drop it entirely if it's a phantom with no information content.
4. Append a one-line console note in the Phase 4 summary listing what was reclassified: `↻ Demoted N action items to Notes & Follow-ups: <one-line list with reason per item>`. Do NOT prompt — the heuristics are conservative enough to demote silently per user feedback. If the user wants to keep something demoted, they can manually restore it.

This is a sanity net, not the primary classifier. If this step is consistently demoting >2 items per run, that's a signal that `daily-briefing/SKILL.md` § 3a needs to be tightened further.

**Then run a personal-assistant pass.** Phase 3 (daily-briefing) was invoked with `--no-focus-blocks`, so the user never sees a focus-block proposal during `/good-morning`. To preserve the "what should I actually do next" signal without re-introducing calendar drafts, invoke via `Skill` agent: with `council-single-persona` agent, persona pinned to `personal-assistant`. Pass:

- **user_problem:** *"Given today's standup and briefing artifacts, what are the 3 most important next actions for the user?"*
- **context:** the executive-summary bullets from the daily briefing (read from `{{vault_root}}/📅/YYYY/MM/YYYY-MM-DDD.md`), the burndown verdict from the standup report, and any impediments/blockers surfaced in Phase 2.
- **desired_outcome:** *"A `## Next actions` block — 3 bullets max, each naming a concrete action, timing/deadline, and any dependency or follow-up. No calendar drafts (that's owned by `/daily-briefing` step 9 when invoked standalone)."*
- **depth:** `brief`

Append the persona's `## Next actions` block to the Phase 4 console output. This makes the wrapper output actionable in its own right rather than just an artifact-link manifest.

## Rules

- **Sequential, never parallel.** Phase 2 reads Phase 1's JSONL; Phase 3 is independent but ordered last by design.
- **Auto-continue between handoffs.** When one phase finishes, the next starts immediately. No pause, no acknowledgment prompt, no mid-flight summary. Only stop for a hard Phase-1 failure or a genuinely blocking sub-skill question the wrapper can't auto-answer.
- **Idempotent.** Re-running on the same day overwrites today's artifacts in place. Never prompt — the user has already opted in by invoking /good-morning. The JSONL trend log is the only exception (append-only, by design).
- **Phase 3 = three artifacts.** Daily report + weekly rollup + monthly rollup. All three. Skip only step 9 (focus blocks).
- **No focus blocks, no calendar drafts.** Phase 3 is report-only. Do not propose blocks, do not create meeting drafts.
- **Forward, don't reinterpret.** Team names, `--inc`, `--sprint`, `--days`, etc. are passed through verbatim to whichever sub-skill recognises them. The wrapper doesn't validate them.
- **Abort on Phase 1 failure.** A missing snapshot breaks Phase 2's burndown. Phases 2 and 3 may degrade gracefully; Phase 1 may not.
- **Phase 1.5 is best-effort, never blocking.** The team-calendar fetch/parse can fail (TLS, network, ICS schema drift) without affecting the other phases — skip with a `⚠` summary line, never abort. It reads the ICS read-only and never writes the feed into the vault.
- **DTEND is exclusive.** Whenever the wrapper renders an OOO date range (digest, summary, forwarded text), the "last day out" is `DTEND − 1 day`. Reporting someone back a day early is the failure mode to avoid.
- **No new memory writes.** All config (vault root, default team, Jira project, team-calendar ICS URL, etc.) is *read* from existing memory keys; the wrapper writes no memory files. (The ICS URL lives in `reference_team_calendar_ics.md`, created once out-of-band — the wrapper only reads it.)

## Edge cases

- **`--skip-snapshot`** — skip Phase 1 only if the user explicitly opts out (e.g. they already ran it from `/sprint-snapshot` earlier). Phase 2 will still attempt to read the most recent JSONL row.
- **`--skip-standup` / `--skip-briefing`** — same pattern; document in the Phase 4 summary which phases ran.
- **Sub-skill prompts despite the override instruction** — answer with the overwrite-equivalent option (`Overwrite`) on the user's behalf, since /good-morning already implies consent. Do not re-ask.
- **Today is a weekend or holiday** — still run all three phases; the artifacts are useful for catch-up. daily-briefing's window logic already handles gap days. If Phase 1.5 finds a team-wide `Holiday` event covering today, note it in the digest so the standup/briefing don't read an empty calendar as "everyone idle".
- **Team-calendar ICS missing or unreachable** — skip Phase 1.5 with a `⚠` line in the Phase 4 summary; the other phases run unchanged. Add/repair the URL in `reference_team_calendar_ics.md`.
- **`--skip-calendar`** — skip Phase 1.5 explicitly (e.g. the feed is down and the user doesn't want the warning noise). The wrapper consumes this flag alongside `--skip-snapshot` / `--skip-standup` / `--skip-briefing`.
- **OOO summary looks wrong by one day** — almost always the exclusive-`DTEND` rule; the last day out is `DTEND − 1`.

