Daily standup prep
Skill risadams/ink-and-agency/skills/scrum-sprint/daily-standup-prep
Generate a per-team standup markdown report by gathering activity over the last N days from Jira, GitLab, Confluence, and a local Git repo. Maps activity to team members from a roster CSV, renders a Mermaid kanban + randomized talking order, captures a `daily`-tagged sprint snapshot, and runs a clarity-council (infographics-expert burndown chart + statistics-expert forecast + scrum-master suggestions) for sprint pulse. Writes one file per team into the Obsidian vault. Use when the user says "daily standup prep", "standup report", "generate standup", "scrum prep", or invokes /daily-standup-prep.From its SKILL.md
npx -y skills add risadams/ink-and-agency --skill daily-standup-prepAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
32.0 KB, ~8.0k tokens by cl100k_base, as published. Nobody here has run it
Daily Standup Prep
Per-team standup report. Read-only across Jira, GitLab, Confluence, and Git. Writes one markdown file per team to the Obsidian vault.
This skill is a Claude Code port of D:\powershell-scripting\src\bin\Get-Standup-Report.ps1 — same inputs, same template placeholders, same output path layout, same identity-matching rules. See REFERENCE.md for the algorithm and per-section formatter contracts. See EXAMPLES.md for invocation parity with the PS script.
Quick start
/daily-standup-prep → Pyrite, last 2 days, INC 25 / Sprint 1
/daily-standup-prep Onyx → single team
/daily-standup-prep Pyrite,Onyx --days 3 --inc 26 → multi-team, custom window
/daily-standup-prep --no-gitlab --no-confluence → toggle sections off
Parameters
| Param | Default | Maps to PS |
|---|---|---|
Teams | ["Pyrite"] | -Teams (string[]) |
DaysToLookBack | 2 | -DaysToLookBack |
JQL | auto-built | -JQL |
GitRepoPath | D:\paas\ | -GitRepoPath |
Inc | 25 | -Inc |
Sprint | 1 | -Sprint |
IncludeJiraIssues / IncludeGitCommits / IncludeGitLabActivity / IncludeConfluenceActivity / IncludeKanbanDiagram / IncludeStandupOrder | all true | same |
IncludeSprintPulse | true | new — runs Phase 4.5 (daily snapshot + council burndown/forecast/suggestions). Toggle off with --no-sprint-pulse |
JIRA base, Confluence base, GitLab base + group, vault root, and template path are all resolved from memory — never accept them as args. See Config resolution below.
Config resolution (run once at the start of every invocation)
| Placeholder | Memory file | Default content / how to populate |
|---|---|---|
{{vault_root}} | reference_obsidian_vault.md (**Vault root:** line) | C:\Users\chris.adams\dev\gd-pkms\ — already populated |
{{jira_project}} | reference_jira_default_project.md (**Default Jira project key:** line) | PRJ — already populated |
{{confluence_space}} | reference_confluence_default_space.md | PP — already populated |
{{template_path}} | derived | {{vault_root}}\🗃Templates\Standup.md (no separate memory) |
{{rosters_dir}} | derived | {{vault_root}}\Scrum Teams\_rosters\ |
{{output_root}} | derived | {{vault_root}}\Scrum Teams\ (per-team subfolder appended later) |
If a memory file is missing or its value is <unset>, prompt once via AskUserQuestion with the PS-script default as the recommended option, then write the answer back so the next run skips the prompt. Same pattern as daily-briefing.
Roster bootstrap (first-run only)
The PS script reads D:\powershell-scripting\src\bin\Teams\<Team>.csv. We move ownership of those CSVs into the vault so the user maintains them in one place going forward.
For each team in the run:
- Look for
{{rosters_dir}}\<Team>.csv(case-insensitive). If present, use it. - If missing and
D:\powershell-scripting\src\bin\Teams\<Team>.csvexists, prompt viaAskUserQuestion: "No roster found at{{rosters_dir}}\<Team>.csv. Copy from the powershell-scripting repo?" — options:Copy (recommended),Use repo path this run only,Skip team. - On
Copy:mkdir -p "{{rosters_dir}}", then copy the file. Tell the user the new path is the canonical location going forward. - If neither path exists, skip the team and warn — do not invent a roster.
CSV format is the same headerless layout the PS Team-Helpers module uses: FullName,Email,Alias,Role,Flags. Lines starting with // and blanks are ignored. Alias and Flags accept ;-separated values. See REFERENCE.md.
Workflow (per team)
Loop the steps below for each team in Teams. Each iteration is independent — one file per team, matching the PS script's $teamsToProcess | ForEach-Object block.
Per-team progress:
- [ ] Phase 1: Resolve config + load roster
- [ ] Phase 2: Compute window + auto-build JQL
- [ ] Phase 3: Gather data (Jira, Git, GitLab, Confluence) — parallel
- [ ] Phase 4: Match activity to team members + flag HasActivity
- [ ] Phase 4.5: Sprint pulse — daily snapshot + council (burndown / forecast / suggestions)
- [ ] Phase 5: Render section markdown + Mermaid kanban + talking order
- [ ] Phase 6: Substitute template placeholders + write to vault
- [ ] Phase 7: Console summary
Phase 1 — Config + roster
Resolve every {{placeholder}} per the table above. Load the roster CSV into a list of {FullName, FirstName, LastName, JiraName, Email, Alias[], DisplayFirstName, Role, Flags[]} records — derive JiraName = "{Last}, {First}" and DisplayFirstName = Alias[0] ?? FirstName. Tag each member with HasActivity = false and empty Issues / Commits / GitLabActivities / ConfluenceActivities lists.
Phase 2 — Window + JQL
activity_since= midnight (Pittsburgh local)DaysToLookBackdays ago.since_iso=YYYY-MM-DDTHH:mm:ssZfor GitLab/Confluence cutoffs.since_date=YYYY-MM-DDforgit log --since.- If
JQLarg is empty: buildproject = {{jira_project}} AND updated >= startOfDay(-{N}d)whereN = DaysToLookBack.
State the chosen window in one short sentence: "Gathering Pyrite standup for window 2026-05-11 → 2026-05-13 (2-day lookback)."
Phase 3 — Gather data (parallel)
Issue these MCP/Bash calls in a single response when section toggles allow:
- Jira issues (
IncludeJiraIssuesorIncludeKanbanDiagram):jira_search(jql=<jql>, fields="summary,status,assignee,issuetype,updated")with pagination to a sane cap. For each result, follow up withjira_get_changelogandjira_get_commentsonly when the issue'supdatedis within the window — skip the fetch otherwise to save tokens. - Jira kanban (
IncludeKanbanDiagramonly): secondjira_searchwithproject = {{jira_project}} AND sprint in openSprints() AND labels in ({lowercase team names}). Capturekey, summary, status, assignee.displayName, issuetype.name. - GitLab MRs + discussions (
IncludeGitLabActivity):list_group_projects(group_id="{{gitlab.XXXX}}")to get all project IDs in the group (subgroups included).- For each project:
list_merge_requests(project_id, updated_after=<since_iso>, per_page=100). - For each MR:
mr_discussions(project_id, merge_request_iid)capped at 100 notes.
- GitLab pipelines (
IncludeGitLabActivity): for each project,list_pipelines(project_id, updated_after=<since_iso>, per_page=100). For each,get_pipeline(project_id, pipeline_id)to read the triggering user. - Confluence pages (
IncludeConfluenceActivity):confluence_search(cql="lastModified >= '{since_date}' AND space = {{confluence_space}}"). For each,confluence_get_page(page_id, expand="history.lastUpdated,version,space,body.view"). - Git commits (
IncludeGitCommits): Bash sequence — confirm{{git_repo}}\.gitexists;git -C "{{git_repo}}" fetch --all;git -C "{{git_repo}}" log --all --since="{since_date}" --pretty=format:"%H|%an|%ae|%aI|%s". Parse pipe-delimited lines.
If any external system fails to respond, log a warning and continue with empty results for that section — never fabricate. If IncludeKanbanDiagram is on but Jira fails, render _No kanban data — Jira unavailable._ rather than skipping the placeholder.
Phase 4 — Match activity to team members
Use the cascading 8-strategy matcher described in REFERENCE.md. For every activity record, attempt to match by (email, displayName, username) — Jira gives email + displayName; GitLab gives email + name + username; Confluence gives email + displayName; Git gives email + author name only.
Filter rules:
- Jira issue inclusion — keep an issue iff its assignee matches a team member OR any comment/changelog entry within the window was authored by a team member. (Direct port of the PS
$isAssignedToTeamMember -or $hasRecentActivityfilter.) - Git/GitLab/Confluence inclusion — keep an activity iff its author matches a team member.
- Set
member.HasActivity = truefor any matched member, and append the activity to the member'sIssues / Commits / GitLabActivities / ConfluenceActivitieslists for the talking-order/per-member sections.
Phase 4.5 — Sprint pulse (daily snapshot + council)
Skip entirely if IncludeSprintPulse is off — substitute empty strings for the three pulse placeholders in Phase 6.
Step 1 — Daily sprint snapshot. Invoke the sprint-snapshot skill via Skill for the current team with --phase "daily". This writes daily.canvas + daily.md to the team's current sprint folder (overwriting yesterday's daily.* is fine — the trend record lives in _snapshots.jsonl) and appends a row to that JSONL trend log. The JSONL is the burndown's data source. If sprint-snapshot fails (Jira down, sprint config missing, etc.), surface the failure in the console summary, render _Sprint pulse unavailable — daily snapshot failed._ for {{burndown_chart}}, leave the other two pulse placeholders empty, and continue. Never fabricate trend data.
Step 2 — Read trend data. Read the team's _snapshots.jsonl from {{output_root}}\<TeamTitleCase>\Scrum 📅\INC {Inc}\Sprint {Sprint}\_snapshots.jsonl. Parse the rows for the current sprint only (filter by inc + sprint). Extract (snapshot_at, remaining, totals.points, by_status.done.points) per row. Also pull capacity, velocity.last, and velocity.avg3 from the most recent row. If the JSONL has fewer than 2 rows for this sprint, the burndown will be a single point — note this in the rendered output, do not fake additional data points.
Step 3 — Run clarity-council. Invoke the clarity-council skill via Skill with three personas: infographics-expert, statistics-expert, scrum-master. Pass the parsed JSONL trend rows, sprint config (StartDate, EndDate, Capacity, LastSprintVelocity, AvgVelocityLast3), the day count (day X of 21), and the current matched-activity summary from Phase 4 as the council's shared context. Ask each persona for a tightly scoped artifact:
-
infographics-expert → render an Obsidian Charts plugin burn-up chart following the exact shape below. The chart uses a
chartcode fence (Chart.js backend via theobsidian-chartscommunity plugin, which must be installed in the vault). Burn-up (not burn-down) is the canonical shape because it visualizes scope changes explicitly — your sprints regularly take +20–40 pts of scope creep, which burn-down folds into a misleading "remaining" line.Burn-up chart contract (must be followed verbatim; no creative reinterpretation):
- Determine actual data points. From the JSONL trend rows, build a list
samples = [(day_n, scope_points, done_points), …]whereday_nis integer days sinceStartDate(Pittsburgh local,day_0 = StartDate),scope_points = totals.points,done_points = by_status.done.points. Sort byday_n. De-duplicate byday_nkeeping the latestsnapshot_atper row. - Build the x-axis. Full sprint range —
x_axis = list(range(0, sprint_length_days + 1))wheresprint_length_days = (EndDate - StartDate).days. The x-axis always spans day 0 → daysprint_length_days, regardless of how far into the sprint we are. - Build the actual scope series. For each
day_ninx_axis:- If
day_n ≤ today_nAND a sample exists forday_n: use the sample'sscope_points. - If
day_n ≤ today_nAND no sample exists (weekend/holiday/gap): carry forward the previous day'sscope_pointsvalue (last-known wins). - If
day_n > today_n(future): emitnull.
- If
- Build the actual done series. Same logic as scope, but using
done_points. Day 0 defaults to0if no sample exists. - Build the pinned scope series (visually distinct future segment). For each
day_ninx_axis:- If
day_n < today_n: emitnull. - If
day_n ≥ today_n: emit today's actualscope_points. (The series starts at today's point so the line connects visually from the actual series.)
- If
- Build the pinned done series. Same logic as pinned scope, but using
done_points. - Build the capacity reference series. Constant
Capacityacross alllen(x_axis)days. - Y-axis bounds.
y_min = 0,y_max = ceil(max(all actual scope values + [Capacity]) * 1.1)so the highest point isn't flush with the top edge. - Title. None at the chart level (the markdown heading carries it). Set the surrounding section heading to
### Burn-up. - Validate before emitting. Confirm all five series have length
len(x_axis); confirmnullis used (notNone/NaN/ empty string) for future-day gaps in the "actual" series and past-day gaps in the "pinned" series; confirm every non-null value is finite and inside[y_min, y_max]. If any check fails, do not emit the chart — emit a one-line markdown note explaining what failed, and surface it so the run can be debugged.
Reference template (substitute the computed values; this is the only acceptable shape):
### Burn-up ```chart type: line labels: ["0","1","2",...,"<sprint_length_days>"] series: - title: "Scope (actual)" data: [<scope values for days 0..today_n; null for days today_n+1..end>] - title: "Done (actual)" data: [<done values for days 0..today_n; null for days today_n+1..end>] - title: "Scope (pinned — no future data)" data: [<null for days 0..today_n-1; today's scope repeated for days today_n..end>] - title: "Done (pinned — no future data)" data: [<null for days 0..today_n-1; today's done repeated for days today_n..end>] - title: "Capacity (<Capacity> pts)" data: [<Capacity repeated len(x_axis) times>] tension: 0.2 width: 100% labelColors: false fill: false beginAtZero: true bestFit: false yTitle: "Points" xTitle: "Sprint day (0 = <StartDate>, <sprint_length_days> = <EndDate> close)" yMin: 0 yMax: <y_max> ``` **Reading the chart**: - **Solid lines (days 0–<today_n>)** = actual snapshot data. <One-sentence summary of scope trajectory: trim, creep, what drove changes>. Done climbs ~<observed pts/day> pts/day average. - **Past gaps** (if any: weekend/holiday days with no snapshot) are carry-forward — value held from the previous day. - **Pinned lines (days <today_n>–<sprint_length_days>)** = today's values held flat, NOT a forecast. The visible gap between Scope (<today_scope>) and Done (<today_done>) at day <sprint_length_days> = ~<gap> pts that would land in next sprint at observed cadence. - **Capacity (<Capacity> pts)** = horizontal reference; scope is currently <+/-N> vs capacity. - **What it would take to close the sprint**: done line would need to climb +<gap> pts in <days_remaining> days (~<required pts/day> pts/day) — <compare to historical velocity>. <Sprint N+1 carry-over recommendation if gap > observed-cadence × days-remaining>.Chart.js (via the
obsidian-chartsplugin) renders each series in a distinct color, so the "actual" and "pinned" segments are visually different even though they share the chart. Thenullvalues create true gaps in the line, so past/future are visually separate. Do not addchartjs:style overrides unless the user explicitly asks — the default rendering is the canonical shape.Plugin dependency. The
chartcode fence requires theobsidian-chartscommunity plugin enabled in the vault (folder:{{vault_root}}/.obsidian/plugins/obsidian-charts/). On first run, verify viaGlobthat the plugin folder exists. If missing, surface a one-line warning in the console summary and emit the chart anyway — Obsidian will render the raw YAML as a code block until the plugin is installed, which is recoverable. Do not fall back to Mermaidxychart-beta— the historical shape is inferior and the user has explicitly chosen Charts plugin going forward.Single-sample case. If only one trend row exists, day 0 acts as the implicit second point: Scope (actual) =
[<sample.scope>, ..., null after sample.day_n], Done (actual) =[0, ..., <sample.done>, ..., null after sample.day_n]. The pinned series start at sample.day_n. Caption appends:_First snapshot at day <N> — trend will fill in over coming days._ - Determine actual data points. From the JSONL trend rows, build a list
-
statistics-expert → produce a single-paragraph forecast: project end-of-sprint completed points using simple linear extrapolation from the trend, compare against
Capacityandvelocity.avg3, and state a confidence band (e.g. "tracking 5pts under commit, ±3pts based on 3-sprint variance"). Hard cap: ≤4 sentences. No charts — words only. Must explicitly call out if the trend data is too sparse to forecast (≤2 rows). -
scrum-master → 1–3 actionable suggestions for the team based on the day's matched activity (Phase 4) + the snapshot bucket counts. Examples: "two tickets in In Review for >3 days — chase reviewers", "Ada has no activity tracked in 2 days — confirm not blocked", "WIP at 8 vs limit 5 — pull from Ready before starting new". Bullet list. No vague platitudes — each suggestion names a ticket, person, or measurable signal.
Step 4 — Capture artifacts. Store the three council outputs as the strings {{burndown_chart}} (the Obsidian Charts chart code fence including its ```chart opening and closing ``` fences, followed by the "Reading the chart" markdown block — see infographics-expert contract above), {{forecast_note}} (the statistics-expert paragraph, plain markdown), and {{scrum_suggestions}} (the bullet list, plain markdown). These feed Phase 6. The placeholder name {{burndown_chart}} is retained for template-backward-compatibility — its content is a burn-up chart per the v2 contract. The vault-side Standup template at {{vault_root}}/🗃Templates/Standup.md does not need to change; the substitution drops the new chart shape into the existing slot.
Phase 5 — Render sections
Each placeholder in the template gets a string built from the matched data. Formatter contracts (exact markdown shape) live in REFERENCE.md:
{{talking_order}}— randomized active members (✅) first, inactive (🧽) last. Each line- [ ] [[@First Last]] - ✅(or🧽). Multi-team runs append(TeamName)after the wikilink. IfIncludeStandupOrderis off, leave the placeholder empty.{{jira_state}}— Mermaidflowchartwith five status columns (Blocked / Ready / In Progress / In Review / Done). Spec in REFERENCE.md. IfIncludeKanbanDiagramis off, empty.{{jira_issues}}— per-issue blocks with### KEY - Summary, type/status/assignee line, then a bulleted activity log of(date) WHO / WHAT / WHYplustextcode fences for details.{{git_updates}}— per-member blocks### [[@First Last]]then- (date)hash7- messagelines, sorted desc by date.{{gitlab_updates}}— three subsections (## Merge Request Activity,## Comment Activity,## Pipeline Activity) only if non-empty; otherwise the placeholder line_No GitLab activity found in the specified time period._.{{confluence_updates}}— per-page blocks with title + space tag, type/last-modified/author line, link, and a content snippet (first 200 chars of stripped HTML in atextcode fence).
Phase 6 — Substitute and write
Read{{template_path}}. The template is the user's master Standup template — never modify it.- Replace placeholders in this exact set (matches the PS
ConvertTo-TemplateMarkdownfunction plus the three Phase 4.5 additions):{{date}},{{team}},{{sprint}},{{increment}},{{talking_order}},{{jira_state}},{{jira_issues}},{{git_updates}},{{confluence_updates}},{{gitlab_updates}},{{burndown_chart}},{{forecast_note}},{{scrum_suggestions}}. Date formatYYYY-MM-DD(Pittsburgh local). Team name written TitleCase (first char upper, rest lower) to match the PS$formattedTeamline. The three pulse placeholders are no-ops if the user's template doesn't reference them — the substitution still runs cleanly. Note this once when a template lacks them so the user knows to add them if they want the pulse rendered (e.g. under a## Sprint Pulseheading containing{{burndown_chart}}, then### Forecast{{forecast_note}}, then### Suggestions{{scrum_suggestions}}). - Compute output path:
{{vault_root}}\Scrum Teams\<TeamTitleCase>\Scrum 📅\INC {Inc}\Sprint {Sprint}\YYYY-MM-DD.md— note the📅emoji is preserved literally; forward slashes work fine in bash on Windows. mkdir -pthe parent directory.- Never overwrite silently. If the file exists,
Readit, thenAskUserQuestion:Overwrite/Append as ## Re-run HH:MM section/Skip save. Writethe file. Print the saved path back as a clickable markdown link.
Sibling-skill delegation in Phase 4.5 / Phase 6:
-
The daily snapshot is always delegated to
sprint-snapshotviaSkillwith--phase "daily". Never reimplement Jira sprint fetching inline —sprint-snapshotowns the JQL, identity matching, capacity math, and the JSONL trend schema. If its phase derivation logic doesn't recognizedaily, pass it through as an explicit--phaseoverride (the skill already supports arbitrary phase strings via that flag). -
The pulse council is always delegated to
clarity-councilviaSkillwith personas pinned toinfographics-expert + statistics-expert + scrum-master. Do not author burndown SVG/Mermaid, forecast paragraphs, or suggestions inline — let each persona produce its own artifact under its own constraints. -
If extending the template with new Obsidian Flavored Markdown constructs (callouts, embeds, dataview, block IDs, frontmatter properties beyond
Increment/Sprint/Date), invokeobsidian-markdownviaSkillrather than authoring the syntax inline — it's the canonical reference and stays in sync with vault conventions. -
For wikilink target verification (the
[[@First Last]]rule in talking_order / git_updates), prefer delegating the lookup toobsidian-vaultif the run will need to verify many person notes — its search is more efficient than repeatedGlobcalls. For a single team run (≤15 lookups) the inlineGlobis fine. -
If the user later wants a dashboard
.basefile that aggregates team standups (e.g. "all standups for Pyrite this sprint"), invokeobsidian-basesrather than hand-authoring the YAML. -
For CLI-driven vault inspection (e.g. checking note frontmatter, running a vault-wide property query before deciding whether to overwrite), invoke
obsidian-cli— it has direct property/task access thatRead+Globcannot replicate cheaply.
Phase 7 — Console summary
Print a brief block per team (matches the PS === REPORT GENERATION SUMMARY === block):
=== <Team> standup report ===
Team members loaded: N
Jira issues included: N (of M scanned)
Git commits matched: N
GitLab activities matched: N
Confluence activities matched: N
Sprint pulse: snapshot=<ok|failed|skipped> · trend rows=N · council=<ok|failed|skipped>
Saved: <markdown link>
Rules
- Read-only across Jira, GitLab, Confluence. Never transition issues, post comments, approve MRs, edit pages, or modify any external state.
- Read-only on the Git repo.
fetchandlogonly — never checkout, pull, or merge. - No vault writes outside
Scrum Teams/. The template at🗃Templates/Standup.mdis read-only too. - Never overwrite an existing standup file silently. Always confirm via
AskUserQuestion. - Confirm before persisting to memory. When populating an
<unset>value (GitLab base URL, etc.), use the PS-script default as the recommended option but let the user override before writing. - Wikilinks for team members only when the vault person note exists. Use
Glob("{{vault_root}}/🤼 Team/**/@*.md")to verify before writing[[@First Last]]. If the vault has no canonical note for a roster member, render plain text — don't pollute the graph with broken links. - TitleCase team names in output.
pyrite→Pyrite(first char upper, rest lower) — matches the PS$formattedTeamline so vault paths and frontmatter stay consistent. - Pittsburgh local time. All
Datecolumns and the filename use America/New_York. Thegit log%aIand ISO timestamps from Jira/GitLab/Confluence are converted before display. - Section toggle semantics match the PS script. Skipping a section (e.g.,
--no-gitlab) means: don't gather, don't fetch, and substitute an empty string for the placeholder — the template still resolves cleanly. - No fabricated data. If an MCP fails, surface the failure in the console summary and put a
_<system> unavailable_note in the corresponding section. Do not invent. - Sprint pulse is delegated, never inlined. The
dailysnapshot must come fromsprint-snapshotand the burndown/forecast/suggestions must come fromclarity-council's three pinned personas. Do not author burndown charts, forecasts, or scrum-master suggestions inline — that bypasses the persona constraints (infographics-expert's chartjunk rules, statistics-expert's confidence-band requirement, etc.) that make these artifacts trustworthy. - Burndown trend data is read-only and append-only. Phase 4.5 reads
_snapshots.jsonlto feed the chart but never edits past rows. Pastdaily.canvas/daily.mdare allowed to be overwritten — the trend record lives in the JSONL.
Edge cases
- No team members loaded — warn and skip the team. Don't render an empty report (matches PS
if ($teamMembers.Count -eq 0)guard). - Roster CSV missing for one of N teams — process the others; warn for the missing one.
- Vault
🤼 Teamfolder doesn't have a@First Last.mdnote for a roster member — render plainFirst Lastinstead of a wikilink in{{talking_order}}and{{git_updates}}. - Git repo not present at
{{git_repo}}— skip git commit collection silently; render an empty{{git_updates}}section. - Jira sprint label missing for the team (kanban query returns 0) — render
_No <Team> items found in current sprint._for{{jira_state}}. - GitLab group returns 0 projects — surface a warning; the user may have lost group membership. Render
_No GitLab activity found in the specified time period._. - Re-run on the same day — same overwrite/append/skip prompt as
daily-briefing. - Multi-team run, partial failure — generate reports for the teams that succeeded; surface per-team status in the final summary block.
- Sprint pulse with no prior snapshots — first daily run of a sprint will only have one trend row after Step 1 writes it. Render the burndown as a single point with a one-line caption (e.g.
_First daily snapshot of Sprint N — trend will fill in over coming days._) and let the statistics-expert flag the data as too sparse to forecast. - Sprint config note missing —
sprint-snapshotwill prompt to create one. If the user declines, skip the entire pulse phase rather than half-rendering it.
Related skills
This skill writes into and reads from the user's Obsidian vault. When any vault-related extension or refinement is needed, delegate to the relevant obsidian-* skill via Skill rather than reimplementing — they are the canonical references for vault conventions and stay in sync with Obsidian feature changes.
| Skill | Use it for |
|---|---|
daily-briefing | Personal (Outlook-driven) morning prep. Sibling skill — both can run on the same morning. Same {{vault_root}} resolution pattern. |
sprint-snapshot | Owns the daily snapshot in Phase 4.5. Always invoke via Skill with --phase "daily"; never reimplement Jira sprint fetching, identity matching, or the JSONL trend schema inline. The _snapshots.jsonl it writes is the burndown's data source. |
clarity-council | Owns the pulse council in Phase 4.5. Always invoke with the three pinned personas (infographics-expert, statistics-expert, scrum-master). Lets each persona enforce its own constraints on the burndown chart, forecast, and suggestions. |
issue-suggest-component | Pattern reference for memory-driven default Jira project + bulk AskUserQuestion pacing if the multi-team loop ever grows beyond a handful of teams. |
issue-feature-breakdown | Pattern reference for Jira+Confluence context-gathering depth. |
obsidian-markdown | Canonical reference for Obsidian Flavored Markdown. Consult before extending the standup template with new constructs (callouts, embeds, dataview, block IDs, frontmatter properties). |
obsidian-vault | Use for batch wikilink target verification, vault-wide note discovery, and managing the standup index notes. Prefer over repeated Glob calls when the run needs >15 lookups. |
obsidian-cli | Use for property/task queries, frontmatter inspection, and any CLI-driven vault operation that Read + Glob cannot do cheaply. |
obsidian-bases | Use when the user wants a dashboard .base file (e.g. "show me all standups for Pyrite this sprint", "which team members had no activity in the last 5 standups"). Don't hand-author .base YAML. |
obsidian-canvas | Use if the user wants a visual board summarizing the standup (e.g. a sprint-overview canvas with linked standup notes as nodes). |
Quality Loop
Before returning the artifact, evaluate it and refine if it falls short.
- Generate the artifact via the workflow above.
- Self-evaluate against these criteria:
- Activity is mapped to the correct roster member from the CSV
- The Mermaid kanban renders and reflects the gathered Jira/GitLab state
- Sources cover the requested N-day window across Jira, GitLab, Confluence, and Git
- One file per team is written to the vault
- Loop — if two or more criteria fail, revise and re-check.
- Exit when all criteria pass, or after two refinement passes (then note which criteria still fall short).
<!-- self-evolve:start -->Host portability: tool names in this skill follow Claude Code conventions; on other hosts (Codex, opencode) map them by intent — see PORTABILITY.md.
Self-Evolve Loop
This skill learns across invocations — the full contract is
SELF-EVOLVE.md. Start: read the learnings
journal — ~/.ink-and-agency/learnings/daily-standup-prep.md and/or the workspace-local
.ink-and-agency/learnings/daily-standup-prep.md — if present, and apply its guidance.
End: self-evaluate the results; optionally ask the user for feedback (never
block on it); append signal-bearing learnings to the journal (user-global when
the sandbox allows writing there, workspace-local otherwise); route
skill-improvement ideas per the contract's tiers — edit the canonical source
when one is present, never the plugin cache.
What ships with it: 4 files
24.3 KB alongside SKILL.md
agents/
- openai.yaml271 B
- EXAMPLES.md2.4 KB
- README.md11.2 KB
- REFERENCE.md10.4 KB