good-morning — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited good-morning (Agent Skill) and scored it 79/100 (yellow). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 5 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 5 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
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.
/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.
- [ ] 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 writtenRun 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:
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.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.
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.
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:
re.sub(r'\n[ \t]', '', text) after normalising \r\n→\n. Confluence wraps long lines.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.start <= horizon AND end_exclusive > today (Pittsburgh local). Horizon = the current sprint's end_date from _sprint.md if available, else today + 10 days.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).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):
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.
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.
Invoke via Skill: daily-briefing. Phase 3 must produce all three artifacts:
{{vault_root}}\📅\YYYY\MM\YYYY-MM-DDD.md (daily-briefing step 4){{vault_root}}\📅\YYYY\MM\YYYY-W{NN}.md (daily-briefing step 5){{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.
Print one block summarising what was produced:
=== 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]]):
#### Day at a glance Mermaid gantt block.#### Email triage at a glance Mermaid pie block.### 👥 Team Daily Notes standup wikilink — unless Glob("{{vault_root}}/Scrum Teams/**/YYYY-MM-DD.md") returns zero matches for today.### 📊 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, NNmmmetadata, so a label likeGap 09:00-10:00 :09-00, 60mmfails withInvalid 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. Seedaily-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:
Read {{vault_root}}/📅/YYYY/MM/YYYY-MM-DDD.md and extract every - [ ] line from ### Carrying over (still open) and ### New / from window.daily-briefing/SKILL.md § 3a:📨 Email Triage section or 🗓️ Today's Schedule? If so, there's no fresh signal backing it and it's likely stale.Edit the daily note:## ✅ Open Action Items.## 📝 Notes & Follow-ups if the item still has informational value (OOO notice, status awareness).↻ 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:
{{vault_root}}/📅/YYYY/MM/YYYY-MM-DDD.md), the burndown verdict from the standup report, and any impediments/blockers surfaced in Phase 2.briefAppend 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.
--inc, --sprint, --days, etc. are passed through verbatim to whichever sub-skill recognises them. The wrapper doesn't validate them.⚠ summary line, never abort. It reads the ICS read-only and never writes the feed into the vault.DTEND − 1 day. Reporting someone back a day early is the failure mode to avoid.reference_team_calendar_ics.md, created once out-of-band — the wrapper only reads it.)/sprint-snapshot earlier). Phase 2 will still attempt to read the most recent JSONL row.Overwrite) on the user's behalf, since /good-morning already implies consent. Do not re-ask.Holiday event covering today, note it in the digest so the standup/briefing don't read an empty calendar as "everyone idle".⚠ line in the Phase 4 summary; the other phases run unchanged. Add/repair the URL in reference_team_calendar_ics.md.--skip-snapshot / --skip-standup / --skip-briefing.DTEND rule; the last day out is DTEND − 1.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.