feat(agenda): forward-looking schedule layer (agenda + dash UPCOMING)#463
Conversation
Working artifact for feature/agenda. Implement in a fresh session from this worktree. See docs/specs/SPEC-agenda-schedule-2026-06-13.md. Delete this file during dev merge cleanup. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add lib/schedule.zsh, the shared engine for the new agenda layer. It aggregates dated activity from two greenfield sources and normalizes them to a pipe-delimited record stream (date|label|type|project| recurrence|source) so every surface renders identically: - .STATUS `## Schedule:` blocks (ZSH-parseable, no yq) - .flow/teach-config.yml teaching dates (via _date_load_config, yq-guarded) Functions: _schedule_classify, _schedule_relative_days, _schedule_parse_status, _schedule_teach_items, _schedule_expand_recurring (weekly:<dow>, crosses month/year boundaries), _schedule_collect (session cached), _schedule_filter_window, _schedule_sort, _schedule_render_line, and _flow_schedule_to_atlas (opportunistic, capability-detected, silent no-op when atlas/schedule absent). Reuses date-parser + atlas-bridge + dash helpers + FLOW_COLORS; pure ZSH, works without atlas and without yq. Wire date-parser.zsh then schedule.zsh into the core library block (after atlas-bridge.zsh). tests/test-schedule.zsh (32 cases) covers classification boundaries, relative-day labels, .STATUS parsing (empty/malformed safe), recurrence expansion across month+year boundaries, window filtering, the no-yq fallback, and the atlas-absent no-op. Adds tests/fixtures/ teach-config-scheduled.yml (weeks[].start_date, which _date_load_config requires; the demo fixture uses weeks[].date and yields no week_N). Verified: test-schedule (32/32), source flow.plugin.zsh clean. Full run-all.sh has 17 pre-existing environmental failures (atlas/git/chezmoi/ himalaya/network-dependent), unchanged by this commit (confirmed against a stashed baseline). https://claude.ai/code/session_014YpEdtSqYMRdcuoMQFgLXW
Add commands/agenda.zsh — a top-level command (not a dispatcher) that
renders the schedule engine's records grouped into OVERDUE / TODAY /
THIS WEEK / LATER buckets with a calm empty state ("Nothing scheduled —
clear runway").
Flags: default/`-w`/`--week` (7d), `today` (0d + overdue), `-m`/`--month`
(30d, adds LATER), `--all` (no cap, includes holidays), `--overdue`
(overdue only), `<category>` (dev|r|research|teach|teaching|quarto|apps),
`-h`/`--help`. Pipeline: _schedule_collect | _schedule_filter_window |
_schedule_sort, then an async _flow_schedule_to_atlas push. Holidays are
filtered unless `--all`. Aliases agt/agw/agm (avoiding `ag`, which
collides with the silver-searcher binary). _agenda_help follows the
dash-style _C_* convention.
Also polish _schedule_render_line: drop the trailing 🔁 marker when the
type icon is already 🔁 (recurring type), keeping it for recurrence on
other types (e.g. a research weekly block).
tests/test-agenda.zsh (17 cases): help, windows, --overdue, category
filter, empty state, unknown option, and a run_isolated case with atlas
disabled + a temp FLOW_PROJECTS_ROOT. All green (agenda 17/17,
schedule 32/32); source flow.plugin.zsh clean.
https://claude.ai/code/session_014YpEdtSqYMRdcuoMQFgLXW
Insert _dash_upcoming in dash() after QUICK WINS and before QUICK ACCESS. It renders the next few dated items (7-day window + overdue) via the shared engine (_schedule_collect | _schedule_filter_window | _schedule_sort), caps at 4 with a "+N more — run 'agenda'" hint, filters holidays, and self-suppresses when nothing is due. Reuses _schedule_collect's session cache so it shares work with `agenda` and the morning cadence. Degrades silently if the engine isn't loaded. Extend tests/test-dash.zsh: a scheduled item in the fixture, asserts the UPCOMING header + item render, and that the section self-suppresses on an empty schedule. Also isolate FLOW_DATA_DIR in setup so the dashboard never reads the real worklog (leaked session counts otherwise hit a latent _dash_right_now arithmetic path that truncates output before UPCOMING). Verified: test-dash 29/29. https://claude.ai/code/session_014YpEdtSqYMRdcuoMQFgLXW
Wire the schedule engine into the daily/weekly cadence: - morning: _flow_morning_agenda shows the next 5 dated items (7d + overdue) between the projects summary and yesterday's wins; self-suppresses when nothing is due. morning -q gains a "📅 N due soon" one-liner (_flow_agenda_count). - today: _flow_today_agenda shows a "📅 Due today" block (window 0 → today + overdue). - week: _flow_week_agenda shows "📅 This week's deadlines" (7d), grouped by weekday with an Overdue group first. All blocks reuse _schedule_render_line for visual consistency and the _schedule_collect session cache. Register test-schedule, test-agenda, and the new test-cadence-agenda (6/6) in run-all.sh. Verified: test-cadence-agenda 6/6; source flow.plugin.zsh clean. https://claude.ai/code/session_014YpEdtSqYMRdcuoMQFgLXW
- completions/_agenda: completes windows (today/-w/-m/--all/--overdue/-h) and project categories; also covers the agt/agw/agm aliases. - man/man1/agenda.1: full page (modeled on g.1); .TH product/version is "flow-cli 7.9.0" to match FLOW_VERSION (the version-sync guard enforces this). - tests/test-manpage-version-sync.zsh: skip `agenda` in the orphan check (next to the `flow` skip) — agenda is a top-level command, not a dispatcher, so it has no dispatcher function for the coverage scan to match. REQUIRED or the guard flags agenda.1 as an orphan page. Verified: test-manpage-version-sync 12/12; zsh -n completions/_agenda clean; man -l agenda.1 renders without warnings. https://claude.ai/code/session_014YpEdtSqYMRdcuoMQFgLXW
- New docs/guides/AGENDA-SCHEDULE-GUIDE.md (command, .STATUS `## Schedule:` grammar, teaching-config source, icons, dash/morning/today/week surfaces, atlas integration) + mkdocs.yml nav entry. - docs/help/QUICK-REFERENCE.md: Agenda subsection (windows, aliases, schedule snippet), dash UPCOMING tip, TOC update. - docs/reference/MASTER-DISPATCHER-GUIDE.md: top-level commands note covering agenda, dash UPCOMING, and cadence enrichment. - docs/ATLAS-CONTRACT.md: opportunistic, capability-detected `atlas schedule push --format=json` contract (payload schema, probe, upsert semantics). - CLAUDE.md: add agenda to Core Commands; note dash UPCOMING. Verified: all internal doc links resolve; nav target exists. Full `mkdocs build --strict` could not be run in this sandbox (mkdocs-exclude won't compile here and the available mkdocs-material/pymdownx versions differ from the project's pinned toolchain) — validated links + nav by hand instead. https://claude.ai/code/session_014YpEdtSqYMRdcuoMQFgLXW
Implementation complete (all ORCHESTRATE steps) ✅Pushed 6 implementation commits on top of the spec/ORCHESTRATE commit (head Done
Tested
Not verified here (sandbox limits)
🤖 Generated with Claude Code Generated by Claude Code |
Independent verification pass (fresh sandbox) ✅Re-checked head
Closes the one "not verified" gap (docs build)I got
Net: nothing actionable found — branch is complete and green modulo the documented environmental gaps. Still draft; not marking ready / not merging. 🤖 Generated with Claude Code Generated by Claude Code |
`agenda <filter>` previously matched only the project's detected category (_dash_detect_category), so a `- … | research` item kept in a project that flow-cli classifies as `dev`/`r`/`quarto` was silently dropped from `agenda research` — contradicting the `## Schedule:` data model, which invites tagging each item with a type. The filter now matches a record's own TYPE (research|teaching|general|recurring) OR the project's category (dev|r|teach|quarto|apps), via a new _schedule_category_match helper. `teach`/`teaching` are treated as synonyms. Filtering moved from per-project to per-record in _schedule_collect. The command arg parser now also accepts `general`/`recurring` (were rejected as "unknown"). Tests: +3 engine (test-schedule 32→35), +2 command (test-agenda 17→19) covering cross-category type match, type precision, and the teach/teaching synonym. Docs/man/completions/help updated to describe type-or-category filtering. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- CLAUDE.md: lib 74→77, command files 31→32 (schedule.zsh + agenda.zsh added this branch); note agenda in the command-list hint; clarify the agenda filter accepts <type|cat>. - README.md: list `agenda` in the "Stay Oriented" core commands. - AGENDA-SCHEDULE-GUIDE.md: add `text` language to two terminal-output fences (MD040); markdownlint clean, mkdocs --strict passes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…view) Addresses adversarial-review findings A–F on this branch: A. Test integrity (test-agenda.zsh): `local out=$(agenda…); assert_exit_code $?` always read the `local` builtin's status (0), so the exit-code assertions were inert. Split declaration/assignment to capture the real rc. B. _schedule_parse_status corrupted records when a label contained `|` (label truncated, type mis-assigned). Now the last field is treated as the type only when it is a known token; the middle is the label; any leftover `|` in the label is collapsed to `/` so the pipe-delimited record stays well-formed (6 fields, correct type). teach→teaching normalized. C. Holiday filtering used `grep -v '|holiday|'` (substring) across 6 call sites — a general item literally labeled "holiday" was wrongly dropped. New `_schedule_drop_holidays` filters the TYPE field via awk; all 6 sites (agenda/dash/morning×4) use it. D. _schedule_collect cache key omitted FLOW_PROJECTS_ROOT → stale cross-root results when the cache persisted. Root added to the key. E. Added command-path coverage for holiday hide/--all-show and pipe-in-label rendering (were untested). F. _flow_schedule_to_atlas built JSON via raw printf interpolation → invalid JSON when a label contained `"`/`\`. New _schedule_json_escape + _schedule_records_to_json (pure ZSH, no jq) emit valid escaped JSON. Tests: test-schedule 35→39, test-agenda 19→22; full suite 60/60 (2 pre-existing environmental fails, 1 expected timeout — unchanged). source clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The agenda feature surfaced that the schedule-aware top-level commands lacked man pages (only dispatchers + flow + agenda had them). Add full troff pages modeled on agenda.1 / g.1: - dash.1 — dashboard + UPCOMING section, all flags (-a/-i/-w/-f/--inventory) - morning.1 — daily briefing + Upcoming block; documents the `am` alias - today.1 — "Due today" + overdue snapshot - week.1 — "This week's deadlines" grouped by weekday All .TH = "flow-cli 7.9.0" (== FLOW_VERSION); each cross-references the others and agenda(1). These are top-level commands, not dispatchers, so the manpage-version-sync orphan check gains a skip case for them (same pattern as the existing flow/agenda skips). Verified: manpage guard 12/12, mandoc lint clean (only the project-wide verbatim-date warning shared by every flow-cli page), full suite unchanged (60 pass / 2 pre-existing env fails / 1 expected timeout). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds the missing per-feature test pair (every major feature has both): - tests/e2e-agenda.zsh (19 tests) — drives the real commands against a seeded multi-project root: windows, --overdue, today/-m, type+category filters, holiday hide/--all, recurring expansion, pipe-in-label sanitization, the dash UPCOMING section, today/week/morning cadence, the empty state, and the yq-gated teaching path. - tests/dogfood-agenda.zsh (15 tests) — structural wiring: engine/command/alias functions defined, source order, dash/cadence call the engine, holiday filter uses the helper (no raw grep left), no local path=/status= footguns, no var leakage, and packaging (man pages, completion, docs, .TH == FLOW_VERSION). - Registered both in tests/run-all.sh. Fix surfaced by the e2e: commands/dash.zsh used `$(( cond ? 's' : '' ))` (arithmetic ternary with STRING operands) in _dash_right_now. Interactive shells tolerate the resulting "bad math expression"; non-interactive shells (command substitution, pipes, tests) treat it as fatal, so `dash` aborted before reaching _dash_upcoming — i.e. the new UPCOMING section never rendered when dash output was captured/piped. Pre-existing on dev (3 sites); replaced with the codebase's safe idiom `$( (( cond )) && echo s )`. Suite: 62 passed / 2 pre-existing env fails / 1 expected timeout. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Forward-looking schedule layer (agenda command + dash UPCOMING + cadence enrichment) ships as a minor bump. Updates FLOW_VERSION, package.json, CLAUDE.md, and all man-page .TH lines; adds mirrored CHANGELOG entries (root + docs); refreshes suite/test-file counts (64/64, 213 files). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Review polish (non-blocking nits from the PR #463 review): - Add _schedule_window_records (collect | filter-window | sort | drop-holidays) so dash UPCOMING, morning/today/week, and the quick count no longer each repeat the four-stage pipeline. - Add _schedule_render_capped (renders a stream capped at N with a "+M more" footer), counting via an array split instead of `grep -c .`. - Route _dash_upcoming, _flow_morning_agenda, _flow_today_agenda, _flow_week_agenda, and _flow_agenda_count through the helpers. `agenda` itself keeps its own pipeline (category-aware + conditional holiday drop for --all), so it is intentionally not folded in. No behavior change. Verified: test-schedule 39/39, test-agenda 22/22, test-cadence-agenda 6/6, test-dash 29/29, e2e-agenda 19/19, dogfood-agenda 15/15. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ORCHESTRATE-*.md files are feature-branch working artifacts and should not land on dev (per CLAUDE.md merge-cleanup convention). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Update worktree table + status footer: agenda/schedule layer merged (#463), v7.10.0, 64/64 suites / 213 files; release dev→main pending. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
What
Adds a forward-looking schedule layer so flow-cli surfaces dated activity (deadlines, lectures, exams, manuscript/grant milestones, recurring blocks) — not just present/backward-looking status.
agendacommand — buckets into OVERDUE / TODAY / THIS WEEK / LATER; windows-w/-m/today/--all/--overdue; type-or-category filter; aliasesagt/agw/agmdash(after QUICK WINS, self-suppressing)morning/today/weeklib/schedule.zsh(pure ZSH, noyq/atlas required)Data sources
_date_load_configreading.flow/teach-config.yml(yq-guarded)yq## Schedule:section in.STATUS(- <when> | <label> [| <type>], where<when>is an ISO date orweekly:<dow>)Done
lib/schedule.zshengine +test-schedule(39 cases)commands/agenda.zsh+test-agenda(22 cases)morning/today/weekenrichment +test-cadence-agenda(6) /test-dashagenda/dash/morning/today/week) + manpage-guard patchAGENDA-SCHEDULE-GUIDE, QUICK-REFERENCE, MASTER-DISPATCHER-GUIDE, ATLAS-CONTRACT) + fixturee2e-agenda(19) +dogfood-agenda(15) per-feature pair./tests/run-all.shgreen (64/64 suites; 2 pre-existing env-only fails, 1 expected interactive timeout)Notes
_dash_right_nowused$(( cond ? 's' : '' ))(arithmetic ternary on string operands) — fatal in non-interactive shells, sodashaborted before UPCOMING rendered when output was captured/piped. Pre-existing ondev(3 sites); replaced with$( (( cond )) && echo s ).docs/specs/SPEC-agenda-schedule-2026-06-13.md.ORCHESTRATE-agenda.mdis a working artifact — delete during dev-merge cleanup.🤖 Generated with Claude Code