diff --git a/.STATUS b/.STATUS index 030108485..695bdeb6d 100644 --- a/.STATUS +++ b/.STATUS @@ -1709,6 +1709,6 @@ E) **Quarto Workflow Enhancements** (Additional validators) **Last Updated:** 2026-02-02 **Status:** v6.2.0 Released ✅ | Docs deployed ✅ | Website reorganized (14→7 sections) ✅ | 0 build warnings ✅ -## wins: --category fix squashed the bug (2026-02-02), fixed the bug (2026-02-02), --category fix squashed the bug (2026-01-31), fixed the bug (2026-01-31), --category fix squashed the bug (2026-01-31) +## wins: --category fix squashed the bug (2026-02-03), fixed the bug (2026-02-03), --category fix squashed the bug (2026-02-03), fixed the bug (2026-02-03), --category fix squashed the bug (2026-02-03) ## streak: 0 -## last_active: 2026-02-02 17:08 +## last_active: 2026-02-03 22:01 diff --git a/.archive/BRAINSTORM-teach-deploy-v2-2026-02-03.md b/.archive/BRAINSTORM-teach-deploy-v2-2026-02-03.md new file mode 100644 index 000000000..9b1c907ff --- /dev/null +++ b/.archive/BRAINSTORM-teach-deploy-v2-2026-02-03.md @@ -0,0 +1,79 @@ +# teach deploy v2 — Brainstorm + +**Generated:** 2026-02-03 +**Context:** flow-cli v6.3.0 (`teach deploy` enhancement) +**Mode:** deep + feature + save +**Duration:** ~10 min + +## Overview + +Port STAT-545's battle-tested deploy patterns into flow-cli's `teach deploy` command, plus add dry-run, history, rollback, and CI mode. Consolidate the two deploy code paths into one. + +## Current Pain Points + +1. **Slow PR workflow** (45-90s) — STAT-545 has a 8-15s direct merge script +2. **Generic commit messages** — `"Update: 2026-02-03"` vs smart `content: week-05 lecture, assignment 5` +3. **No deployment tracking** — No history of what was deployed when +4. **No rollback** — Manual `git revert` only +5. **No dry-run** — Can't preview without executing +6. **Two code paths** — `_teach_deploy()` and `_teach_deploy_enhanced()` with inconsistent behavior +7. **No CI support** — Prompts block non-interactive use +8. **No .STATUS integration** — Teaching week / progress not tracked on deploy + +## Quick Wins (Phase 0-1) + +1. Delete old `_teach_deploy()` dead code (~340 lines removed) +2. Add `--ci` flag + TTY auto-detection +3. Extract shared `_deploy_preflight_checks()` + +## Medium Effort (Phase 2-4) + +4. Smart commit messages from changed file categories +5. `--direct` flag for direct merge mode (8-15s) +6. Branch divergence detection + recovery +7. `--dry-run` preview mode + +## Larger Effort (Phase 5-6) + +8. Deploy history log (`.flow/deploy-history.yml`) +9. `--rollback [N]` with interactive picker +10. `.STATUS` auto-updates (teaching week, progress, deploy count) + +## Options Considered + +### Option A: Phase It (rejected by user) +Port STAT-545 first, then add new features. +**Pros:** Smaller PRs, faster feedback +**Cons:** More branches, more merge overhead + +### Option B: All at Once (selected) +Single feature branch with everything. +**Pros:** One implementation pass, coherent design +**Cons:** Larger PR, more risk + +### Option C: Skip Rollback (not selected) +Defer rollback + history to a future release. +**Pros:** Simpler scope +**Cons:** History is needed for rollback, and both are useful independently + +## Recommended Path + +All 8 features + 1 refactor in a single `feature/teach-deploy-v2` branch. Implementation follows a dependency-ordered 7-phase plan where CI mode comes first (every feature needs it), then smart commits, direct merge, dry-run, history+rollback, .STATUS updates, and finally help/docs. + +## Key Design Decisions + +1. **`--direct` replaces `--direct-push`** — shorter name, `--direct-push` kept as alias +2. **deploy-history.yml uses append-only writes** — no yq rewrite of entire file +3. **Rollback uses `git revert`** — forward rollback, not destructive reset +4. **CI mode auto-detects from TTY** — plus explicit `--ci` flag override +5. **Smart commit messages are overridable** — `--message "text"` takes precedence +6. **.STATUS updates are non-destructive** — skip if file doesn't exist + +## Next Steps + +1. [ ] Create worktree: `feature/teach-deploy-v2` +2. [ ] Implement Phase 0: Consolidation +3. [ ] Implement Phase 1-6 sequentially +4. [ ] Run full test suite +5. [ ] Update help + CLAUDE.md +6. [ ] PR to dev diff --git a/CLAUDE.md b/CLAUDE.md index 9cb168640..a396a597b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,8 +8,8 @@ This file provides guidance to Claude Code when working with code in this reposi - **Architecture:** Pure ZSH plugin (no Node.js runtime required) - **Dependencies:** **ZERO** - No dependencies on Oh-My-Zsh, antidote, or any framework -- **Current Version:** v6.2.1 -- **Latest Release:** v6.2.1 (2026-02-03) +- **Current Version:** v6.4.0 +- **Latest Release:** v6.4.0 (2026-02-03) - **Install:** Homebrew (recommended), or any plugin manager (antidote, zinit, oh-my-zsh, manual) - **Optional:** Atlas integration for enhanced state management - **Health Check:** `flow doctor` for dependency verification @@ -276,14 +276,14 @@ cc [cmd] # Claude Code launcher (cc, cc pick, cc yolo) tm # Terminal manager (tm title, tm profile, tm ghost) wt # Worktree management (wt create, wt status, wt prune) dot # Dotfile management (dot edit, dot sync, dot secret) -teach # Teaching workflow (teach analyze, teach init, teach deploy, teach exam, teach macros, teach plan) +teach # Teaching workflow (teach analyze, teach init, teach deploy, teach exam, teach macros, teach plan, teach style) prompt # Prompt engine switcher (prompt status, prompt toggle) v # Vibe coding mode (v on, v off, v status) ``` **Get help:** ` help` (e.g., `r help`, `cc help`, `teach help`) -### Template Management (v6.1.0) ✨ +### Template Management (v6.4.0) ✨ **Project-local templates at `.flow/templates/`** @@ -297,7 +297,7 @@ teach templates sync # Update from plugin defaults teach init --with-templates # Initialize with templates ``` -### LaTeX Macro Management (v6.1.0) ✨ +### LaTeX Macro Management (v6.4.0) ✨ **Consistent notation for AI-generated content** @@ -328,7 +328,7 @@ teach macros export --format json # Export as JSON **Documentation:** `docs/reference/REFCARD-TEMPLATES.md` -### Lesson Plan Management (v6.1.0) ✨ +### Lesson Plan Management (v6.4.0) ✨ **CRUD management of lesson plan weeks** @@ -351,7 +351,7 @@ teach plan delete 3 --force # Remove week **Documentation:** `docs/reference/REFCARD-TEACH-PLAN.md` -### Token Management (v6.1.0 Phase 1) ✨ +### Token Management (v6.4.0 Phase 1) ✨ **Isolated Token Checks & Smart Caching** @@ -427,7 +427,9 @@ flow-cli/ │ ├── inventory.zsh # Tool inventory generator │ ├── keychain-helpers.zsh # macOS Keychain secrets │ ├── config-validator.zsh # Config validation -│ ├── git-helpers.zsh # Git integration utilities +│ ├── git-helpers.zsh # Git integration + smart commits +│ ├── deploy-history-helpers.zsh # Deploy history (append-only YAML) +│ ├── deploy-rollback-helpers.zsh # Forward rollback (git revert) │ └── dispatchers/ # Smart command dispatchers (12) │ ├── cc-dispatcher.zsh # Claude Code │ ├── dot-dispatcher.zsh # Dotfiles + Secrets @@ -453,11 +455,14 @@ flow-cli/ ├── completions/ # ZSH completions ├── hooks/ # ZSH hooks ├── docs/ # Documentation (MkDocs) -├── tests/ # Test suite (462 tests) +├── tests/ # Test suite (543+ tests) │ ├── fixtures/ # Test fixtures │ │ └── demo-course/ # STAT-101 demo course for E2E -│ ├── e2e-teach-analyze.zsh # E2E: 29 tests -│ └── interactive-dog-teaching.zsh # Interactive: 10 tasks +│ ├── test-teach-deploy-v2-unit.zsh # Deploy v2 unit: 36 tests +│ ├── test-teach-deploy-v2-integration.zsh # Deploy v2 integration: 22 tests +│ ├── e2e-teach-deploy-v2.zsh # Deploy v2 E2E: 23 tests +│ ├── e2e-teach-analyze.zsh # E2E: 29 tests +│ └── interactive-dog-teaching.zsh # Interactive: 10 tasks └── .archive/ # Archived Node.js CLI ``` @@ -472,7 +477,9 @@ flow-cli/ | `lib/atlas-bridge.zsh` | Atlas integration | Optional state engine | | `lib/keychain-helpers.zsh` | macOS Keychain secrets | Touch ID support | | `lib/config-validator.zsh` | Config validation | Schema + hash validation | -| `lib/git-helpers.zsh` | Git integration | Teaching workflow | +| `lib/git-helpers.zsh` | Git integration | Smart commits, teaching | +| `lib/deploy-history-helpers.zsh` | Deploy history | Append-only YAML | +| `lib/deploy-rollback-helpers.zsh` | Deploy rollback | Forward rollback | | `lib/dispatchers/*.zsh` | Smart dispatchers | 12 active dispatchers | | `commands/*.zsh` | Core commands | work, dash, finish, etc. | | `docs/reference/MASTER-DISPATCHER-GUIDE.md` | Complete dispatcher docs | All 12 dispatchers | @@ -595,22 +602,27 @@ teach exam "Topic" # Generate exam via Scholar ### Test Suite Overview -**Status:** ✅ 462 tests total +**Status:** ✅ 543+ tests total **Documentation:** [Complete Testing Guide](docs/guides/TESTING.md) ```bash # Core test suites tests/test-pick-command.zsh # Pick: 39 tests tests/test-cc-dispatcher.zsh # CC: 37 tests -tests/test-dot-v6.1.0-unit.zsh # DOT: 112+ tests +tests/test-dot-v6.4.0-unit.zsh # DOT: 112+ tests tests/test-teach-dates-unit.zsh # Teaching dates: 33 tests tests/test-teach-dates-integration.zsh # Integration: 16 tests -# Teach plan tests (v6.1.0) +# Teach plan tests (v6.4.0) tests/test-teach-plan.zsh # Unit: 32 tests tests/test-teach-plan-security.zsh # Security: 24 tests (YAML injection, edge cases) tests/e2e-teach-plan.zsh # E2E: 15 tests (CRUD workflows) +# Teach deploy v2 tests (v6.4.0) +tests/test-teach-deploy-v2-unit.zsh # Unit: 34 tests +tests/test-teach-deploy-v2-integration.zsh # Integration: 22 tests +tests/e2e-teach-deploy-v2.zsh # E2E: 20 tests + # E2E tests (teach analyze) tests/e2e-teach-analyze.zsh # E2E: 29 tests (8 sections) @@ -707,34 +719,133 @@ export FLOW_DEBUG=1 ## Current Status -**Version:** v6.2.1 -**Latest Release:** v6.2.1 (2026-02-03) +**Version:** v6.4.0 +**Latest Release:** v6.4.0 (2026-02-03) **Status:** Production **Branch:** `dev` -**Release (latest):** https://github.com/Data-Wise/flow-cli/releases/tag/v6.2.1 +**Release (latest):** https://github.com/Data-Wise/flow-cli/releases/tag/v6.4.0 **Performance:** Sub-10ms for core commands, 3-10x speedup from optimization **Documentation:** https://Data-Wise.github.io/flow-cli/ -**Tests:** 107 tests (62 unit + 33 E2E + 12 interactive) for teach prompt + 462 total tests +**Tests:** 543+ total tests (462 existing + 81 new teach deploy v2 tests) **Recent Improvements:** -- ✅ Website reorganization - 14 sections reduced to 7 (ADHD-friendly) -- ✅ 11 new teaching docs - REFCARDs, guides, tutorials, schema reference -- ✅ teach help system - 100% standards compliance -- ✅ MkDocs tags plugin - 14 topic tags across ~39 pages -- ✅ Section landing pages - Grid card navigation for all 4 main sections -- ✅ Build warnings eliminated - 28 → 0 (broken links to excluded files) -- ✅ Grid card emoji rendering - `pymdownx.emoji` + `attr_list` spacing fix +- ✅ teach deploy v2 - Direct merge (8-15s), smart commits, history, rollback, dry-run, CI mode +- ✅ Deploy history tracking - Append-only `.flow/deploy-history.yml` +- ✅ Forward rollback - `teach deploy --rollback` via git revert +- ✅ Legacy dead code removed - ~400 lines of old `_teach_deploy()` + `_teach_deploy_help()` +- ✅ 76 new tests - 34 unit + 22 integration + 20 E2E --- ## Recent Releases -### v6.2.1 - Help Compliance System (2026-02-03) +### v6.4.0 - Teach Deploy v2: Direct Merge, History, Rollback (2026-02-03) + +**Released:** 2026-02-03 +**Branch:** `feature/teach-deploy-v2` +**Changes:** 8 features, 1 refactor, 76 new tests + +**Major Features:** + +- **Direct Merge Mode** (`--direct/-d`) — 8-15s deploys vs 45-90s PR workflow + - `_deploy_direct_merge()` — merge draft→production, push, no PR + - `--direct-push` kept as backward-compatible alias + - Smart commit messages auto-generated from changed file categories + +- **Smart Commit Messages** — Auto-categorized from file paths + - `_generate_smart_commit_message()` in `lib/git-helpers.zsh` + - Categories: content (lectures, assignments), config (\_quarto.yml), style (CSS), data, deploy + - Overridable with `--message "text"` + +- **Deploy History Tracking** — Append-only `.flow/deploy-history.yml` + - `lib/deploy-history-helpers.zsh` — 4 functions (append, list, get, count) + - `cat >>` for writes (fast), `yq` for reads only + - Records: mode, commit hash, branch, file count, message, duration + - `teach deploy --history [N]` — show last N deploys + +- **Forward Rollback** (`--rollback [N]`) — Revert via `git revert` + - `lib/deploy-rollback-helpers.zsh` — 2 functions (rollback, perform_rollback) + - Interactive picker or explicit index + - Records rollback in history with mode "rollback" + - CI mode requires explicit index (no interactive picker) + +- **Dry-Run Preview** (`--dry-run`/`--preview`) — Preview without mutation + - `_deploy_dry_run_report()` — shows files, commit message, merge direction + - Works with both direct merge and PR mode + +- **CI Mode** (`--ci`) — Non-interactive deployment + - Auto-detect from TTY (`[[ ! -t 0 ]]`) + - 18 CI guards on all `read -r` prompts + - Explicit `--ci` flag override + +- **Shared Preflight** — Extracted `_deploy_preflight_checks()` + - Git repo check, config validation, branch detection + - Sets `DEPLOY_*` exported variables for all deploy modes + - `[ok]`/`[!!]` marker format + +- **.STATUS Auto-Updates** — `_deploy_update_status_file()` + - Updates `last_deploy`, `deploy_count`, `teaching_week` + - Teaching week calculated from `semester_info.start_date` + - Non-destructive: skips if `.STATUS` absent + +**Refactoring:** + +- Deleted legacy `_teach_deploy()` (~313 lines) from `teach-dispatcher.zsh` +- Deleted legacy `_teach_deploy_help()` (~85 lines) from `teach-dispatcher.zsh` +- All deploy routing uses `_teach_deploy_enhanced()` exclusively + +**New Files:** + +- `lib/deploy-history-helpers.zsh` (185 lines) +- `lib/deploy-rollback-helpers.zsh` (214 lines) +- `tests/test-teach-deploy-v2-unit.zsh` (34 tests) +- `tests/test-teach-deploy-v2-integration.zsh` (22 tests) +- `tests/e2e-teach-deploy-v2.zsh` (20 tests) + +**Testing:** 76 new tests (34 unit + 22 integration + 20 E2E), all passing + +**Stats:** 9 files changed, +2,687 / -581 lines + +--- + +### v6.4.0 - Teaching Style Consolidation + Help Compliance (2026-02-03) + +**Released:** 2026-02-03 +**PR #334:** https://github.com/Data-Wise/flow-cli/pull/334 (MERGED) +**Release:** https://github.com/Data-Wise/flow-cli/releases/tag/v6.4.0 +**Changes:** Teaching style consolidation, help compliance system, docs overhaul, test fixes + +**Major Features:** + +- **Teaching Style Consolidation (#298)** — Read `teaching_style:` and `command_overrides:` from `.flow/teach-config.yml` + - `lib/teach-style-helpers.zsh` — 4 helper functions (`_teach_find_style_source`, `_teach_get_style`, `_teach_get_command_override`, `_teach_style_is_redirect`) + - `teach style` / `teach style show` — display current teaching style config + - `teach style check` — validate teaching style configuration + - `teach doctor` — new "Teaching Style" section reports source and config status + - Schema: `teaching_style` + `command_overrides` definitions added to `teach-config.schema.json` + - Resolution order: `.flow/teach-config.yml` (preferred) → `.claude/teaching-style.local.md` (legacy fallback) + - Redirect shim detection (`_redirect: true` in legacy frontmatter) + +- **Help Compliance System (#328)** — 9-rule automated validator for all 12 dispatchers + - `flow doctor --help-check` validates against CONVENTIONS.md standards + - All 12 dispatchers brought to full compliance (box header, MOST COMMON, QUICK EXAMPLES, TIP, See Also) + +- **Documentation Overhaul** — Website reorganized (14→7 sections), 11 new teaching docs, section landing pages, MkDocs tags + +- **Test Fixes** — Repaired 3 pre-existing failures (cc-dispatcher scope bug, obs-dispatcher stale assertions, e2e-dot-safety sandbox guard) + +**Testing:** 21 passed in run-all.sh, 34-test dogfooding suite for teach style helpers + +**Stats:** 82 files changed, +3,628 / -4,251 lines + +--- + +### v6.4.0 - Help Compliance System (2026-02-03) **Released:** 2026-02-03 **PR #332:** https://github.com/Data-Wise/flow-cli/pull/332 (MERGED) -**Release:** https://github.com/Data-Wise/flow-cli/releases/tag/v6.2.1 +**Release:** https://github.com/Data-Wise/flow-cli/releases/tag/v6.4.0 **Changes:** Help compliance system, dispatcher standardization, infrastructure fixes **Major Features:** @@ -761,11 +872,11 @@ export FLOW_DEBUG=1 --- -### v6.2.0 - Docs Overhaul + Website Reorganization (2026-02-02) +### v6.4.0 - Docs Overhaul + Website Reorganization (2026-02-02) **Released:** 2026-02-02 **PR #326:** https://github.com/Data-Wise/flow-cli/pull/326 (MERGED) -**Release:** https://github.com/Data-Wise/flow-cli/releases/tag/v6.2.0 +**Release:** https://github.com/Data-Wise/flow-cli/releases/tag/v6.4.0 **Changes:** Documentation overhaul, no code changes **Major Features:** @@ -797,16 +908,16 @@ export FLOW_DEBUG=1 - Triage: deleted 7 orphaned files, expanded exclude globs, fixed broken links - `pymdownx.emoji` extension for grid card emoji rendering - `attr_list` spacing fix (`:emoji:{ .lg .middle }` — no space) -- Version badges and What's New updated to v6.2.0 +- Version badges and What's New updated to v6.4.0 - Resolved all 28 MkDocs build warnings (0 remaining) --- -### v6.1.0 - Comprehensive Chezmoi Safety Features (2026-01-31) +### v6.4.0 - Comprehensive Chezmoi Safety Features (2026-01-31) **Released:** 2026-01-31 **PR #316:** https://github.com/Data-Wise/flow-cli/pull/316 (MERGED) -**Release:** https://github.com/Data-Wise/flow-cli/releases/tag/v6.1.0 +**Release:** https://github.com/Data-Wise/flow-cli/releases/tag/v6.4.0 **Changes:** Major safety enhancements to dot dispatcher **Major Features:** @@ -868,12 +979,12 @@ export FLOW_DEBUG=1 --- -### v6.1.0 - AI Prompt Management + GIF Quality Enhancement (2026-01-29) +### v6.4.0 - AI Prompt Management + GIF Quality Enhancement (2026-01-29) **Released:** 2026-01-29 **PR #313:** https://github.com/Data-Wise/flow-cli/pull/313 (MERGED - AI Prompts) **PR #315:** https://github.com/Data-Wise/flow-cli/pull/315 (MERGED - GIF Quality) -**Release:** https://github.com/Data-Wise/flow-cli/releases/tag/v6.1.0 +**Release:** https://github.com/Data-Wise/flow-cli/releases/tag/v6.4.0 **Changes:** 67 files changed, +9,012 / -44 lines **Major Features:** @@ -911,13 +1022,13 @@ export FLOW_DEBUG=1 - Tutorial 28: teach-prompt.md (step-by-step guide) - REFCARD-PROMPTS.md (quick reference) - VHS-TAPE-STYLE-GUIDE.md (complete standards) - - Demo course v6.1.0 with prompts + lesson plans + macros + - Demo course v6.4.0 with prompts + lesson plans + macros **Combined Stats:** 67 files, +9,012 / -44 lines --- -### v6.1.0 - Template Management & Lesson Plan Migration (2026-01-28) +### v6.4.0 - Template Management & Lesson Plan Migration (2026-01-28) **Released:** 2026-01-28 **Changes:** 88 commits, 51 files changed, +12,341 / -1,977 lines @@ -960,7 +1071,7 @@ export FLOW_DEBUG=1 --- -### v6.1.0 - Token Automation Phase 1 (2026-01-23) +### v6.4.0 - Token Automation Phase 1 (2026-01-23) **Released:** 2026-01-23 **PR #292:** https://github.com/Data-Wise/flow-cli/pull/292 (MERGED) @@ -984,7 +1095,7 @@ export FLOW_DEBUG=1 --- -### v6.1.0 - Intelligent Content Analysis (2026-01-22) +### v6.4.0 - Intelligent Content Analysis (2026-01-22) **Released:** 2026-01-22 **PR #291:** https://github.com/Data-Wise/flow-cli/pull/291 @@ -1033,10 +1144,10 @@ git diff # Commit and tag git add -A && git commit -m "chore: bump version to 5.23.0" -git tag -a v6.2.0 -m "v6.2.0" +git tag -a v6.4.0 -m "v6.4.0" # Push (requires PR for protected branch) -git push origin main && git push origin v6.2.0 +git push origin main && git push origin v6.4.0 ``` **Files updated by release script:** @@ -1056,5 +1167,5 @@ git push origin main && git push origin v6.2.0 --- -**Last Updated:** 2026-02-03 -**Status:** Production Ready (v6.2.1) +**Last Updated:** 2026-02-03 (v6.4.0) +**Status:** Production Ready (v6.4.0) diff --git a/README.md b/README.md index a3802bd0b..1015a2f19 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # flow-cli -[![Version](https://img.shields.io/badge/version-6.2.0-blue.svg)](https://github.com/Data-Wise/flow-cli/releases/tag/v6.2.0) +[![Version](https://img.shields.io/badge/version-6.4.0-blue.svg)](https://github.com/Data-Wise/flow-cli/releases/tag/v6.4.0) [![Tests](https://github.com/Data-Wise/flow-cli/actions/workflows/test.yml/badge.svg)](https://github.com/Data-Wise/flow-cli/actions) [![Docs](https://img.shields.io/badge/docs-online-brightgreen.svg)](https://data-wise.github.io/flow-cli/) @@ -72,7 +72,7 @@ teach validate --quick-checks - 📚 [Complete Guide](https://data-wise.github.io/flow-cli/guides/LINT-GUIDE/) - 🔄 [Workflow Integration](https://data-wise.github.io/flow-cli/workflows/WORKFLOW-LINT/) -[GitHub Release →](https://github.com/Data-Wise/flow-cli/releases/tag/v6.1.0) +[GitHub Release →](https://github.com/Data-Wise/flow-cli/releases/tag/v6.4.0) --- diff --git a/SPEC-REVIEW-intelligent-content-analysis.md b/SPEC-REVIEW-intelligent-content-analysis.md deleted file mode 100644 index 103c5de92..000000000 --- a/SPEC-REVIEW-intelligent-content-analysis.md +++ /dev/null @@ -1,770 +0,0 @@ -# Specification Review: Intelligent Content Analysis - -**Reviewing:** `SPEC-intelligent-content-analysis-2026-01-20.md` -**Reviewer:** Claude Sonnet 4.5 -**Review Date:** 2026-01-20 -**Status:** Critical review for implementation readiness - ---- - -## 🎯 Executive Summary - -**Overall Assessment:** 🟢 **STRONG FOUNDATION** - Spec is comprehensive and well-structured, but needs refinements in 5 key areas before implementation. - -**Key Strengths:** - -1. ✅ Clear user stories with measurable acceptance criteria -2. ✅ Well-designed architecture with Mermaid diagram -3. ✅ Comprehensive data models with JSON schemas -4. ✅ ADHD-friendly UX patterns -5. ✅ Phased implementation approach (66 hours, 6 phases) - -**Critical Issues to Address (5):** - -1. ⚠️ **Open Question #1** - AI service integration decision blocks Phase 2+ -2. ⚠️ **Cache invalidation** - Strategy not fully defined -3. ⚠️ **Performance targets** - Some targets may be unrealistic -4. ⚠️ **Phase 1 scope** - Too large for MVP (should be split) -5. ⚠️ **Frontmatter schema** - Missing from spec - ---- - -## 📋 Section-by-Section Review - -### 1. Overview & User Stories - -**Rating:** 🟢 **EXCELLENT** - -**Strengths:** - -- Clear primary user story with measurable acceptance criteria -- Secondary stories cover all major use cases -- Acceptance criteria are testable - -**Refinements:** - -#### Acceptance Criteria Adjustment - -**Original:** "✅ `teach analyze` completes in < 30s for single lecture" - -**Issue:** 30s is slow for ADHD-friendly CLI. User will lose focus. - -**Refined:** - -``` -Performance Targets (Tiered): -- Cached analysis: < 100ms ✅ (ADHD-friendly) -- Heuristic-only: < 5s ✅ (acceptable) -- AI-powered analysis: < 30s ⚠️ (requires progress indicator) -- Batch analysis: async background ✅ (non-blocking) -``` - -**Rationale:** 30s is only acceptable if: - -1. User sees continuous progress (not silent) -2. Results are heavily cached (85%+ hit rate) -3. Async option available for batch analysis - ---- - -### 2. Architecture - -**Rating:** 🟡 **GOOD - Needs Clarification** - -**Strengths:** - -- Clean separation of concerns (5 services) -- File-based storage (no external DB) -- Integration points well-defined - -**Refinements:** - -#### Missing: Cache Invalidation Strategy - -**Issue:** Mermaid diagram shows `CacheInvalidator` service but no spec for it. - -**Add to Spec:** - -```markdown -### Cache Invalidation Service - -**Triggers:** - -1. Content hash changes (file modified) -2. lesson-plan.yml updated (affects prerequisites) -3. teach-config.yml updated (affects analysis settings) -4. Manual: teach analyze --force - -**Strategy:** - -- Content-hash based (SHA-256 of file content) -- Cascade invalidation (if Week 3 changes, invalidate Week 4-15 prerequisite checks) -- Partial invalidation (only affected sections, not entire file) - -**Performance:** - -- < 10ms to check if cache valid -- < 50ms to rebuild cache index after invalidation -- Target 85-90% cache hit rate in typical workflow -``` - -#### Missing: Error Handling Architecture - -**Add to Spec:** - -```markdown -### Error Handling Strategy - -**Graceful Degradation:** - -1. Scholar API unavailable → Fall back to heuristic-only analysis -2. Cache corrupted → Rebuild cache, continue analysis -3. lesson-plan.yml malformed → Use frontmatter-only prerequisites -4. Memory limit exceeded → Switch to file-based processing - -**User Notification:** - -- Silent fallback for non-critical (cached → uncached) -- Visible warning for degraded functionality (AI → heuristic) -- Blocking error only for critical failures (file not found, permissions) -``` - ---- - -### 3. Data Models - -**Rating:** 🟢 **EXCELLENT** - -**Strengths:** - -- Comprehensive JSON schemas -- Clear versioning strategy -- Practical field choices - -**Refinements:** - -#### Missing: Frontmatter Schema - -**Issue:** Spec references `concepts:` field in frontmatter but doesn't provide schema. - -**Add to Spec:** - -```yaml ---- -# Standard Quarto frontmatter -title: 'Linear Regression Assumptions' -week: 5 -date: 2026-02-10 - -# Analysis-specific fields (optional) -concepts: - introduces: - - id: regression-assumptions - title: 'Regression Assumptions' # Optional: defaults to id - difficulty: medium # easy|medium|hard - - - id: homoscedasticity - title: 'Homoscedasticity Testing' - difficulty: hard - - requires: - - correlation # From Week 3 - - variance # From Week 1 - - related: # Optional: for concept graph - - residual-analysis - - diagnostic-plots - -learning_objectives: - - id: fit-model - title: 'Fit and interpret regression model in R' - bloom: apply # remember|understand|apply|analyze|evaluate|create - min_examples: 3 - - - id: check-assumptions - title: 'Validate regression assumptions using diagnostic tests' - bloom: evaluate - min_examples: 2 ---- -``` - -**Validation Rules:** - -1. `concepts.introduces` - Array of concepts introduced in this file -2. `concepts.requires` - Array of concept IDs (must exist in earlier weeks) -3. `learning_objectives` - Array of objectives with Bloom taxonomy levels -4. All fields are optional (graceful defaults if missing) - -#### Concept Graph: Add Bloom Taxonomy Levels - -**Enhancement:** Track cognitive complexity for better analysis. - -**Add to `concepts` object:** - -```json -{ - "concepts": { - "regression-assumptions": { - // ... existing fields ... - "bloom_level": "evaluate", // NEW - "cognitive_load": 0.7, // NEW: 0.0 (low) to 1.0 (high) - "teaching_time_minutes": 45 // NEW: estimated teaching time - } - } -} -``` - -**Benefit:** Can detect if Week 5 requires "evaluate" level but prerequisites only teach "remember" level (pedagogical gap). - ---- - -### 4. API Design - -**Rating:** 🟡 **GOOD - Simplification Needed** - -**Strengths:** - -- Comprehensive flag coverage -- Good examples -- Clear integration points - -**Refinements:** - -#### teach analyze - Too Many Flags - -**Issue:** 16 flags is overwhelming (ADHD unfriendly). - -**Simplify:** - -```bash -teach analyze [files...] [options] - -# Core flags (always visible in --help) - --week N, -w N Analyze specific week - --all Analyze all content (default if no files) - --interactive, -i Step through suggestions - --summary, -s Compact summary only - -# Output flags - --format json|text Output format (default: text) - --report [FILE] Generate HTML report (optional filename) - --quiet, -q No progress indicators - -# Analysis flags (advanced - shown in --help-advanced) - --mode strict|moderate|relaxed Strictness (default: from config) - --force Ignore cache - --extract-concepts AI concept extraction (Phase 3+) - --slide-breaks Analyze slide structure (Phase 4+) - --costs Show API costs - -# Examples remain the same -``` - -**Rationale:** - -- 7 primary flags (ADHD-friendly) -- 6 advanced flags (opt-in via `--help-advanced`) -- Progressive disclosure - -#### teach validate --quality → teach validate --deep - -**Issue:** "Quality" is vague. "Deep" better conveys comprehensive analysis. - -**Change:** - -```bash -# OLD -teach validate --quality - -# NEW -teach validate --deep -# Runs Layers 1-6: YAML, syntax, render, chunks, images, + content analysis -``` - -**Benefit:** Matches existing `--deep` pattern in tech industry (deep scan, deep copy, deep equals). - ---- - -### 5. Configuration - -**Rating:** 🟡 **GOOD - Simplification Needed** - -**Strengths:** - -- Tiered strictness modes -- Configurable severities -- Per-week concept definitions - -**Refinements:** - -#### lesson-plan.yml - Simplify Analysis Config - -**Issue:** Config has 3 nested levels (analysis → modes → mode_name). Too complex. - -**Simplify:** - -```yaml -# Simplified analysis config -analysis: - enabled: true - mode: moderate # strict|moderate|relaxed - cache_ttl_hours: 168 # 7 days - - # Thresholds (can override per mode) - min_examples_per_objective: 2 - require_all_prerequisites: true - warn_missing_concepts: true - - # Advanced (optional) - background_analysis: false # Auto-analyze on file save - save_reports: true - report_dir: .teach/analysis - -# Per-week concept definitions remain unchanged -weeks: - - number: 5 - concepts: - - id: slr-basics - required: true - # ... -``` - -**Benefit:** Flatter structure, easier to read and modify. - -#### Add Defaults for Missing Config - -**Add to Spec:** - -```markdown -### Configuration Defaults - -If `analysis:` section missing from lesson-plan.yml: - -- `enabled: false` (opt-in only) -- `mode: moderate` -- `cache_ttl_hours: 168` -- `min_examples_per_objective: 2` - -If `weeks[N].concepts` missing: - -- Extract concepts from frontmatter only -- No prerequisite validation for that week -``` - ---- - -### 6. Open Questions - Resolution Required - -**Rating:** 🔴 **CRITICAL - Blocks Implementation** - -#### Q1: AI Service Integration (MUST RESOLVE) - -**Current Recommendation:** B (Scholar service) - -**Issue:** Scholar service may not have semantic analysis capabilities. - -**Deep Dive:** - -- **Scholar Service:** Designed for content generation (lectures, exams) -- **Semantic Analysis:** Requires NLP, concept extraction, prerequisite inference -- Scholar may not expose these APIs - -**UPDATED Recommendation:** **Option D (NEW)** - -**Option D: Hybrid with Fallback** - -1. **Phase 1 (MVP):** Heuristic-only (no AI) - - Extract concepts from frontmatter - - Validate prerequisites (user-defined only) - - Readability scores (textstat-style algorithms) - - **Benefit:** Zero API dependency, instant results - -2. **Phase 2:** Scholar integration (if available) - - Check if Scholar exposes concept extraction API - - If yes: integrate - - If no: continue with heuristics - -3. **Phase 3+:** Claude API direct (if needed) - - Only if Scholar limitations found - - Add API key management - - Cost tracking - -**Decision Required Before Phase 1:** - -- [ ] Test Scholar API for semantic analysis capabilities -- [ ] If unavailable, commit to heuristic-only MVP -- [ ] Document API key management if Claude API needed - ---- - -#### Q2: Analysis Caching Strategy (RESOLVED ✅) - -**Current Recommendation:** A (JSON files) - -**Enhancement:** Add specific cache file structure. - -**Add to Spec:** - -``` -.teach/ -├── analysis-cache/ -│ ├── lectures/ -│ │ ├── week-01-lecture.json # Mirrors source structure -│ │ ├── week-02-lecture.json -│ │ └── week-03-lecture.json -│ ├── assignments/ -│ │ └── hw-01.json -│ └── cache-index.json # Metadata for all cached files -├── concepts.json -└── prerequisites.json -``` - -**cache-index.json schema:** - -```json -{ - "version": "1.0", - "last_updated": "2026-01-20T15:00:00Z", - "cache_stats": { - "total_files": 15, - "cached_files": 12, - "cache_hit_rate": 0.87, - "total_size_bytes": 245678 - }, - "files": { - "lectures/week-03-lecture.qmd": { - "cache_file": ".teach/analysis-cache/lectures/week-03-lecture.json", - "content_hash": "sha256:abc123...", - "cached_at": "2026-01-20T14:30:00Z", - "ttl_expires": "2026-01-27T14:30:00Z", - "status": "valid" - } - } -} -``` - ---- - -#### Q3: teach validate Integration (NEEDS REVISION) - -**Current Recommendation:** B (`--deep` flag) - -**Issue:** Name changed from `--deep` to `--quality` in spec, but `--deep` is better. - -**Final Decision:** Use `--deep` flag (matches Q4 refinement above). - -```bash -# Validation layers -teach validate [file] # Layers 1-5 (syntax, render, etc) -teach validate --deep [file] # Layers 1-6 (adds content analysis) -``` - ---- - -#### Q4: Performance vs Accuracy (RESOLVED ✅) - -**Current Recommendation:** C (hybrid: heuristics first, AI on demand) - -**Enhancement:** Make this explicit in command design. - -**Add to `teach analyze` API:** - -```bash -# Fast heuristics only (< 5s) -teach analyze --fast - -# AI-powered analysis (30s+) -teach analyze --ai - -# Default: heuristics only in Phase 1, hybrid in Phase 3+ -teach analyze # Uses config default -``` - ---- - -#### Q5: teach slides --optimize Output (RESOLVED ✅) - -**Current Recommendation:** C (terminal) + optional B (`--save-report`) - -**No changes needed.** This is good. - ---- - -### 7. Implementation Phasing - -**Rating:** 🟡 **NEEDS ADJUSTMENT** - -**Issue:** Phase 1 scope too large (12 hours is not MVP). - -**Recommended Re-phasing:** - -#### NEW Phase 0: Ultra-MVP (4-5 hours) ← START HERE - -**Goal:** Prove the concept with minimal features. - -**Scope:** - -1. Create `lib/concept-extraction.zsh` (heuristic-only) - - Extract concepts from frontmatter `concepts:` field - - Build simple concept graph (ID + prerequisites) - - Save to `.teach/concepts.json` - -2. Create `commands/teach-analyze.zsh` (basic) - - `teach analyze [file]` - single file only - - Display concepts found - - Check prerequisites (user-defined only) - - Text output only - -3. Integration - - Source libraries in `flow.plugin.zsh` - - Add routing in `teach-dispatcher.zsh` - - Add basic help text - -4. Tests - - 15 unit tests (concept extraction) - - 5 integration tests (full workflow) - -**Deliverable:** Working `teach analyze` command that validates user-defined prerequisites. No AI, no caching, no advanced features. - -**Success Criteria:** - -- User can add `concepts:` to frontmatter -- `teach analyze lectures/week-03.qmd` validates prerequisites -- Warning if Week 5 requires concept from Week 7 -- < 2s analysis time - ---- - -#### NEW Phase 1: Foundation (6-8 hours) - -**Goal:** Add caching and batch analysis. - -**Scope:** - -1. Add caching (content-hash based) -2. Add `--all` and `--week N` flags -3. Add `--format json` output -4. Improve help text with examples -5. Add 20 more tests - -**Move to Later Phases:** - -- Learning objective validation → Phase 3 -- Interactive mode → Phase 4 -- AI extraction → Phase 5 -- Slide optimization → Phase 6 - ---- - -### 8. Missing Sections - -**Add to Spec:** - -#### Security Considerations - -```markdown -## 🔒 Security Considerations - -### API Key Management (if using Claude API) - -- Store in macOS Keychain (via `security` command) -- Never log API keys -- Rotate keys quarterly -- Rate limiting to prevent abuse - -### File Access - -- Analysis reads files in project directory only -- No network access except AI APIs -- Cache files in `.teach/` (git-ignored) - -### User Privacy - -- No telemetry by default -- Opt-in analytics (crash reports only) -- No content sent to external services without user consent -``` - ---- - -#### Backward Compatibility - -```markdown -## ♻️ Backward Compatibility - -### Zero Breaking Changes Required - -- All analysis features are opt-in -- Existing commands work unchanged -- New frontmatter fields are optional -- lesson-plan.yml analysis section is optional - -### Migration Path - -1. Users can adopt gradually (no flag day) -2. Start with Phase 0 (basic prerequisite checking) -3. Add frontmatter fields incrementally -4. Enable caching when comfortable -5. Add AI analysis in Phase 5+ - -### Versioning - -- Features versioned by phase (not semver) -- Cache schema versioned (v1, v2, etc.) -- Graceful handling of old cache files (rebuild if schema mismatch) -``` - ---- - -## 🎯 Revised Recommendations - -### Immediate Actions (Before Implementation) - -1. **Resolve Q1 (AI Service)** ✅ CRITICAL - - Test Scholar API semantic analysis capabilities - - If unavailable, commit to heuristic-only MVP (Phase 0) - - Document decision in spec - -2. **Simplify Phase 0 Scope** ✅ REQUIRED - - Extract 4-5 hour MVP from current Phase 1 - - Defer advanced features to later phases - - Update `PLAN-teach-analyze-phase1.md` - -3. **Add Missing Sections** ⚠️ IMPORTANT - - Security considerations - - Backward compatibility - - Frontmatter schema - - Cache invalidation strategy - -4. **Simplify Configuration** ⚠️ NICE-TO-HAVE - - Flatten lesson-plan.yml analysis config - - Document defaults for missing config - -5. **Refine Performance Targets** ⚠️ IMPORTANT - - Tier targets (cached/heuristic/AI) - - Add progress indicators requirement - - Document async option for batch - ---- - -### Long-term Considerations - -1. **Extension Points** - - Design API for third-party validators - - Allow custom concept extractors - - Plugin system for analysis modes - -2. **Observability** - - Add `teach analyze --stats` for cache metrics - - Log analysis performance to `.teach/performance-log.json` - - Dashboard for analysis history - -3. **Teaching Assistants** - - Multi-instructor support (different prerequisite definitions) - - Shared concept graph across course sections - - Merge conflicts in lesson-plan.yml - ---- - -## 📊 Spec Maturity Assessment - -| Section | Status | Confidence | Blockers | -| ----------------------- | ------------------- | ---------- | -------------------------- | -| Overview & User Stories | 🟢 Ready | 95% | None | -| Architecture | 🟡 Needs refinement | 85% | Cache invalidation spec | -| Data Models | 🟢 Ready | 90% | Add frontmatter schema | -| API Design | 🟡 Simplify | 80% | Too many flags | -| Configuration | 🟡 Simplify | 80% | Flatten structure | -| Open Questions | 🔴 Blocking | 60% | Q1 must resolve | -| Implementation Plan | 🟡 Revise | 75% | Split Phase 0 from Phase 1 | -| Testing Strategy | 🟢 Ready | 90% | None | -| Documentation Plan | 🟢 Ready | 90% | None | - -**Overall Readiness:** 🟡 **82% - REFINE BEFORE IMPLEMENTATION** - ---- - -## ✅ Approval Checklist - -**Before creating feature branch:** - -- [ ] Q1 (AI Service) resolved with implementation path -- [ ] Phase 0 scope extracted and documented -- [ ] Frontmatter schema added to spec -- [ ] Cache invalidation strategy documented -- [ ] Security considerations added -- [ ] Backward compatibility section added -- [ ] `teach analyze` flags simplified to 7 primary + 6 advanced -- [ ] lesson-plan.yml config flattened -- [ ] Performance targets tiered (cached/heuristic/AI) -- [ ] All refinements incorporated into spec - -**Implementation can proceed when:** - -- [ ] Spec updated status → `ready` -- [ ] Phase 0 plan created (separate from Phase 1) -- [ ] User approves Phase 0 scope -- [ ] Feature branch created: `feature/teach-analyze` - ---- - -## 📝 Recommended Spec Updates - -### 1. Update Open Question #1 - -```diff -- **Recommendation:** Start with B (Scholar), migrate to C if limitations found -+ **RESOLVED:** Option D (Hybrid with Fallback) -+ Phase 0-1: Heuristic-only (no AI dependency) -+ Phase 2: Test Scholar API, integrate if available -+ Phase 3+: Claude API direct if needed -+ Decision: Proceed with heuristic-only MVP -``` - -### 2. Add Section: Frontmatter Schema - -Insert after "Data Models" section (line 257). - -### 3. Update teach analyze Flags - -Simplify from 16 flags to 7 primary + 6 advanced. - -### 4. Add Section: Cache Invalidation Strategy - -Insert in Architecture section (line 151). - -### 5. Add Section: Security Considerations - -Insert before "Review Checklist" (line 580). - -### 6. Add Section: Backward Compatibility - -Insert after "Security Considerations". - -### 7. Split Phase 1 into Phase 0 + Phase 1 - -Update "Implementation Notes" section (line 634). - ---- - -## 🎬 Next Steps - -### Option A: Update Spec Immediately - -I can update the spec file with all refinements right now. - -### Option B: Review Refinements First - -Discuss specific refinements before updating spec. - -### Option C: Create Phase 0 Plan - -Skip spec updates, create minimal Phase 0 implementation plan. - -### Option D: All of the Above - -Update spec, then create Phase 0 plan in sequence. - -**Recommended:** Option D (update spec, then plan Phase 0) - -**Your choice?** diff --git a/SPEC-intelligent-content-analysis-2026-01-20.md b/SPEC-intelligent-content-analysis-2026-01-20.md deleted file mode 100644 index 349de1c84..000000000 --- a/SPEC-intelligent-content-analysis-2026-01-20.md +++ /dev/null @@ -1,1358 +0,0 @@ -# SPEC: Intelligent Content Analysis for Teaching Workflows - -**Status:** ready ✅ -**Created:** 2026-01-20 -**Updated:** 2026-01-20 (post-review) -**From Brainstorm:** BRAINSTORM-intelligent-content-analysis-2026-01-20.md -**Mode:** Deep + Feature + Agents (Backend Architect + UX Designer) -**Review:** SPEC-REVIEW-intelligent-content-analysis.md - ---- - -## 📋 Overview - -Transform flow-cli's teaching workflow from basic content creation to **intelligent content engineering** with semantic content analysis. The system validates not just syntax (YAML, Quarto) but semantic quality: learning objectives coverage, prerequisite sequencing, concept coverage, and pedagogical structure. - -**Key Innovation:** Hybrid prerequisite system combining user-defined high-level concepts with AI-extracted detailed relationships (future), enabling both instructor control and intelligent automation. - -**Implementation Strategy:** Start with heuristic-only analysis (Phase 0-1), add AI enhancement in later phases only if needed. - ---- - -## 🎯 Primary User Story - -**As an instructor creating course content,** -**I want automated content quality analysis before deployment,** -**So that I can catch pedagogical issues (missing concepts, broken prerequisite chains, insufficient examples) without manual review.** - -**Acceptance Criteria (Revised):** - -1. ✅ `teach analyze` performance targets: - - **Cached:** < 100ms (ADHD-friendly, instant feedback) - - **Heuristic-only:** < 5s (acceptable for interactive use) - - **AI-powered:** < 30s (with continuous progress indicator) [Phase 3+] - - **Batch analysis:** Async background (non-blocking) [Phase 2+] - -2. ✅ Provides actionable suggestions (not just problems) -3. ✅ Integrates with existing workflow: validate → analyze → deploy -4. ✅ Configurable strictness (strict/moderate/relaxed) -5. ✅ Visual progress indicators (no silent processing) -6. ✅ Cached results (content-hash invalidation, 85%+ hit rate target) - ---- - -## 🔄 Secondary User Stories - -### Story 2: Prerequisite Tracking - -**As an instructor,** -**I want to ensure concepts are introduced before they're used in examples,** -**So that students aren't confused by unfamiliar terms.** - -**Acceptance Criteria:** - -1. User defines high-level prerequisites in lesson-plan.yml or frontmatter -2. System validates prerequisites are met across weeks -3. Reports violations with line numbers and suggestions -4. _(Phase 3+)_ AI extracts detailed concept relationships from content - -### Story 3: Learning Objective Validation [Phase 3+] - -**As an instructor,** -**I want to verify that each learning objective has sufficient examples,** -**So that students have multiple chances to practice the skill.** - -**Acceptance Criteria:** - -1. Parse objectives from YAML frontmatter -2. Count examples and code chunks matching each objective -3. Flag objectives with < N examples (configurable) -4. Suggest where to add more examples - -### Story 4: Slide Structure Optimization [Phase 4+] - -**As an instructor using teach slides,** -**I want AI suggestions for optimal slide breaks and key concepts to emphasize,** -**So that my presentations are better structured for comprehension.** - -**Acceptance Criteria:** - -1. Analyze lecture structure during conversion -2. Suggest slide breaks at concept boundaries (beyond H2/H3) -3. Identify key concepts for callout boxes -4. Estimate presentation time -5. Optional: Apply suggestions automatically with --optimize flag - ---- - -## 🏗️ Architecture - -### Component Diagram (Mermaid) - -```mermaid -graph TB - subgraph "User Interface Layer" - CLI[teach CLI Commands] - Hooks[Git Pre-commit Hooks] - end - - subgraph "Service Layer - Content Analysis" - Analyzer[Content Analysis Service] - ConceptExtractor[Concept Extraction Service] - PrereqChecker[Prerequisite Checker Service] - SlideOptimizer[Slide Optimizer Service - Phase 4+] - QualityScorer[Quality Scorer Service - Phase 3+] - end - - subgraph "Data Layer" - ConceptGraph[Concept Graph Store
.teach/concepts.json] - PrereqDB[Prerequisites DB
.teach/prerequisites.json] - AnalysisCache[Analysis Cache
.teach/analysis-cache/] - CacheIndex[Cache Index
.teach/analysis-cache/cache-index.json] - PerfLog[Performance Log
.teach/performance-log.json] - ValidationStatus[Validation Status
.teach/validation-status.json] - end - - subgraph "Integration Layer" - ScholarAPI[Scholar AI API - Phase 3+] - ValidatorFramework[Custom Validator Framework] - PerformanceMonitor[Performance Monitor] - end - - subgraph "Background Processing - Phase 2+" - AnalysisQueue[Analysis Queue
async worker pool] - CacheInvalidator[Cache Invalidation Service] - end - - CLI -->|teach analyze| Analyzer - CLI -->|teach lecture| ConceptExtractor - CLI -->|teach slides --optimize| SlideOptimizer - CLI -->|teach validate --deep| QualityScorer - CLI -->|teach deploy --check-prereqs| PrereqChecker - Hooks -->|pre-commit| QualityScorer - - Analyzer --> ConceptExtractor - Analyzer --> PrereqChecker - Analyzer --> QualityScorer - - ConceptExtractor --> ScholarAPI - ConceptExtractor --> ConceptGraph - - PrereqChecker --> PrereqDB - PrereqChecker --> ConceptGraph - - SlideOptimizer --> ConceptGraph - SlideOptimizer --> ScholarAPI - - QualityScorer --> ScholarAPI - QualityScorer --> AnalysisCache - - Analyzer --> AnalysisQueue - AnalysisQueue --> AnalysisCache - - QualityScorer --> PerformanceMonitor - QualityScorer --> ValidationStatus - - ConceptExtractor --> ValidatorFramework - - AnalysisCache --> CacheInvalidator - AnalysisCache --> CacheIndex - ConceptGraph --> CacheInvalidator - - style Analyzer fill:#e1f5ff - style ConceptGraph fill:#fff4e6 - style AnalysisCache fill:#fff4e6 - style ScholarAPI fill:#f3e5f5 - style AnalysisQueue fill:#e8f5e9 - style CacheInvalidator fill:#e8f5e9 -``` - -### Cache Invalidation Strategy - -**Triggers:** - -1. **Content hash changes** (file modified) - - Invalidate: single file cache only -2. **lesson-plan.yml updated** (affects prerequisites/concepts) - - Invalidate: all concept graph caches + prerequisite caches -3. **teach-config.yml updated** (affects analysis settings) - - Invalidate: all caches -4. **Manual:** `teach analyze --force` - - Invalidate: specified files or all - -**Strategy:** - -- **Content-hash based:** SHA-256 of file content (ignoring whitespace) -- **Cascade invalidation:** If Week 3 changes, invalidate Week 4-15 prerequisite checks -- **Partial invalidation:** Only affected sections, not entire analysis -- **TTL-based expiration:** Cache entries expire after N hours (configurable, default 168h = 7 days) - -**Performance Targets:** - -- < 10ms to check if cache valid -- < 50ms to rebuild cache index after invalidation -- Target 85-90% cache hit rate in typical workflow - -**Cache Directory Structure:** - -``` -.teach/ -├── analysis-cache/ -│ ├── cache-index.json # Metadata for all cached files -│ ├── lectures/ -│ │ ├── week-01-lecture.json # Mirrors source structure -│ │ ├── week-02-lecture.json -│ │ └── week-03-lecture.json -│ └── assignments/ -│ └── hw-01.json -├── concepts.json # Global concept graph -└── prerequisites.json # Prerequisites database -``` - -**cache-index.json schema:** - -```json -{ - "version": "1.0", - "last_updated": "2026-01-20T15:00:00Z", - "cache_stats": { - "total_files": 15, - "cached_files": 12, - "cache_hit_rate": 0.87, - "total_size_bytes": 245678, - "avg_analysis_time_ms": 1234 - }, - "files": { - "lectures/week-03-lecture.qmd": { - "cache_file": ".teach/analysis-cache/lectures/week-03-lecture.json", - "content_hash": "sha256:abc123...", - "cached_at": "2026-01-20T14:30:00Z", - "ttl_expires": "2026-01-27T14:30:00Z", - "status": "valid", - "analysis_time_ms": 1150 - } - } -} -``` - -### Error Handling Strategy - -**Graceful Degradation:** - -1. **Scholar API unavailable** → Continue with heuristic-only analysis (Phase 0-2 always work) -2. **Cache corrupted** → Rebuild cache, continue analysis (log warning) -3. **lesson-plan.yml malformed** → Use frontmatter-only prerequisites (warn user) -4. **Memory limit exceeded** → Switch to file-based processing (slower but functional) -5. **Frontmatter missing** → Skip concept extraction for that file (info message) - -**User Notification:** - -- **Silent fallback:** Cached → uncached (performance degradation only) -- **Visible warning:** AI → heuristic (feature degradation) -- **Blocking error:** File not found, permissions denied, invalid YAML syntax - -**Recovery:** - -- All errors logged to `.teach/analysis-errors.log` -- `teach doctor` can diagnose and fix common issues -- `teach analyze --rebuild-cache` forces cache rebuild - ---- - -## 🔌 API Design - -### teach analyze Command - -```bash -teach analyze [files...] [options] - -# CORE FLAGS (always visible) - --week N, -w N Analyze specific week's content - --all Analyze all lectures and slides - --interactive, -i Step through suggestions one-by-one - --summary, -s Show compact summary only - -# OUTPUT FLAGS - --format json|text Output format (default: text) - --report [FILE] Generate HTML report (optional filename) - --quiet, -q Suppress progress indicators - -# ADVANCED FLAGS (shown in --help-advanced) - --mode strict|moderate|relaxed Strictness level (default: from config) - --force Ignore cache, re-analyze everything - --fast Heuristics only (< 5s) [default Phase 0-2] - --ai AI-powered analysis (30s+) [Phase 3+] - --slide-breaks Analyze for optimal slide structure [Phase 4+] - --costs Show API usage costs [Phase 3+] - -Examples: - # Basic usage (Phase 0+) - teach analyze lectures/week-05-regression.qmd - teach analyze --week 5 - teach analyze --all --summary - - # Interactive mode (Phase 2+) - teach analyze --interactive lectures/week-05-regression.qmd - - # Generate report (Phase 2+) - teach analyze --mode strict --report - - # AI-powered (Phase 3+) - teach analyze --ai --week 5 -``` - -**Flag Count:** 7 primary + 6 advanced = 13 total (reduced from 16) - -### teach slides --optimize [Phase 4+] - -```bash -teach slides [options] --optimize - -New Flags: - --optimize Enable AI-powered slide structure analysis - --preview-breaks Show suggested slide breaks before generation - --apply-suggestions Auto-apply slide break suggestions - --key-concepts Emphasize key concepts with callouts - -Integration with teach analyze: - 1. Load cached analysis for source lecture - 2. Extract slide breaks from analysis.slide_breaks - 3. Identify key concepts from analysis.key_concepts_for_emphasis - 4. Generate slides with enhanced structure - -Examples: - teach slides --week 3 --optimize - teach slides --from-lecture week-03.qmd --optimize --preview-breaks - teach slides --week 3 --optimize --apply-suggestions -``` - -### teach validate --deep [Phase 2+] - -```bash -teach validate [files...] --deep - -New Flag: - --deep Run comprehensive validation (Layers 1-6) - -Layer 6: Content Quality Analysis - - Check concept coverage - - Verify prerequisites - - Validate learning objectives (Phase 3+) - - Analyze readability (Phase 3+) - -Integration: - 1. Run standard 5-layer validation (YAML, syntax, render, chunks, images) - 2. Run Layer 6: Content Quality Analysis - 3. Update .teach/validation-status.json with quality scores - 4. Cache analysis results - -Examples: - teach validate --deep lectures/week-05-regression.qmd - teach validate --deep --changed -``` - -### teach deploy --check-prereqs [Phase 2+] - -```bash -teach deploy [options] --check-prereqs - -New Flags: - --check-prereqs Validate prerequisites before deployment - --skip-analysis Skip prerequisite checks (urgent deploys) - -Behavior: - 1. Get changed files since last deploy - 2. Run teach analyze on changed files - 3. Check prerequisite satisfaction across all weeks - 4. Generate quality report - 5. Block deploy if critical issues found (strict mode only) - 6. Warn and ask confirmation (moderate/relaxed modes) - -Examples: - teach deploy --check-prereqs --preview - teach deploy --check-prereqs --mode strict - teach deploy --skip-analysis # Emergency bypass -``` - ---- - -## 📊 Data Models - -### Frontmatter Schema - -**Add to lecture/assignment `.qmd` files:** - -```yaml ---- -# Standard Quarto frontmatter -title: 'Linear Regression Assumptions' -week: 5 -date: 2026-02-10 - -# Analysis-specific fields (all optional) -concepts: - introduces: - - id: regression-assumptions - title: 'Regression Assumptions' # Optional: defaults to id - difficulty: medium # easy|medium|hard - - - id: homoscedasticity - title: 'Homoscedasticity Testing' - difficulty: hard - - requires: - - correlation # Concept ID from earlier week - - variance # Concept ID from earlier week - - related: # Optional: for concept graph visualization - - residual-analysis - - diagnostic-plots - -learning_objectives: # Phase 3+ - - id: fit-model - title: 'Fit and interpret regression model in R' - bloom: apply # remember|understand|apply|analyze|evaluate|create - min_examples: 3 - - - id: check-assumptions - title: 'Validate regression assumptions using diagnostic tests' - bloom: evaluate - min_examples: 2 ---- -``` - -**Validation Rules:** - -1. `concepts.introduces` - Array of concepts introduced in this file -2. `concepts.requires` - Array of concept IDs (must exist in earlier weeks) -3. `learning_objectives` - Array of objectives with Bloom taxonomy levels (Phase 3+) -4. **All fields are optional** - Graceful defaults if missing - -**Defaults if missing:** - -- No `concepts:` field → Extract nothing, skip prerequisite checks for this file -- No `concepts.requires` → Assume no prerequisites -- No `learning_objectives` → Skip objective validation - -### Concept Graph Schema (.teach/concepts.json) - -```json -{ - "version": "1.0", - "schema_version": "concept-graph-v1", - "metadata": { - "last_updated": "2026-01-20T12:00:00Z", - "course_hash": "abc123...", - "total_concepts": 342, - "weeks": 15, - "extraction_method": "frontmatter" - }, - "concepts": { - "regression-assumptions": { - "id": "regression-assumptions", - "name": "Linear Regression Assumptions", - "type": "fundamental|applied|advanced", - "difficulty": "medium", - "prerequisites": ["correlation", "variance"], - "introduced_in": { - "week": 5, - "lecture": "lectures/week-05-lecture.qmd", - "line_number": 12 - }, - "reinforced_in": [ - { "week": 6, "file": "lectures/week-06-lecture.qmd" }, - { "week": 7, "file": "assignments/hw-05.qmd" } - ], - "learning_objectives": [ - "Identify violations of regression assumptions", - "Apply diagnostic tests for assumptions" - ], - "related_concepts": ["residuals", "homoscedasticity", "normality"], - "keywords": ["linearity", "independence", "homoscedasticity", "normality"], - "bloom_level": "evaluate", - "cognitive_load": 0.7, - "teaching_time_minutes": 45, - "ai_confidence": 0.89 - } - }, - "concept_graph": { - "edges": [ - { - "from": "correlation", - "to": "regression-assumptions", - "type": "prerequisite", - "weight": 0.9 - } - ] - } -} -``` - -**Phase 0-1:** Only `id`, `name`, `prerequisites`, `introduced_in` populated (from frontmatter) - -**Phase 3+:** AI extracts `related_concepts`, `keywords`, `bloom_level`, `cognitive_load` - -### Analysis Cache Schema (.teach/analysis-cache/.json) - -```json -{ - "file": "lectures/week-05-lecture.qmd", - "content_hash": "sha256:abc123...", - "analyzed_at": "2026-01-20T12:00:00Z", - "ttl_hours": 168, - "phase": "phase0", - "analysis": { - "concepts_extracted": [ - { - "id": "regression-assumptions", - "introduced": true, - "prerequisites_satisfied": true - } - ], - "prerequisite_violations": [], - "readability_score": 0.78, - "missing_concepts": [], - "weak_examples": [], - "slide_breaks": [], - "key_concepts_for_emphasis": [] - }, - "performance": { - "api_calls": 0, - "total_duration_ms": 1150, - "tokens_used": 0 - } -} -``` - ---- - -## 📐 Dependencies - -### Existing Infrastructure (Leverage) - -- **Custom Validator Framework** (Phase 1) - Extensible validation -- **Performance Monitor** (Phase 2) - Metrics tracking -- **lesson-plan.yml** - Structured semester data -- **yq** - YAML parsing -- **Quarto** - Document rendering - -### New Dependencies - -- **jq** - JSON processing (already used in flow-cli) -- **flock** - File locking for cache writes (Phase 2+) -- **fswatch** or **inotifywait** - Watch mode (optional, Phase 3+) - -### No External Dependencies (Phase 0-2) - -- Pure ZSH for all core logic -- File-based storage (no database) -- No AI APIs required - -### Future Dependencies (Phase 3+) - -- **Scholar AI API** (if semantic analysis supported) -- **Claude API** (fallback if Scholar unavailable) -- Async processing via ZSH background jobs - ---- - -## 🎨 UI/UX Specifications - -### User Flow Diagram - -``` -Instructor creates content - │ - ├─ teach lecture "Topic" (via Scholar) - ├─ teach slides "Topic" (via Scholar) - └─ teach exam "Topic" (via Scholar) - │ - ▼ -┌───────────────────────────────────────────────────────────────┐ -│ teach validate [file] │ -│ ├─ YAML frontmatter │ -│ ├─ Syntax checking │ -│ ├─ Render validation │ -│ └─ Image references │ -└───────────────────────────────────────────────────────────────┘ - │ - ▼ (Pass) -┌───────────────────────────────────────────────────────────────┐ -│ teach analyze [file] [--mode strict|moderate|relaxed] │ -│ ├─ Concept coverage │ -│ ├─ Prerequisites check │ -│ └─ (Phase 3+) Learning objectives, structure suggestions │ -└───────────────────────────────────────────────────────────────┘ - │ - ├─ WARNINGS (> 0) ──┐ - │ │ - │ ▼ - │ ┌────────────────────────────┐ - │ │ teach analyze --interactive │ - │ │ Review suggestions 1-by-1 │ - │ └────────────────────────────┘ - │ │ - ▼ (All INFO or user accepts) -┌───────────────────────────────────────────────────────────────┐ -│ teach deploy [--check-prereqs] │ -│ Deploys to GitHub Pages │ -└───────────────────────────────────────────────────────────────┘ -``` - -### CLI Output Example (Phase 0) - -```bash -$ teach analyze lectures/week-05-regression.qmd - -Analyzing: lectures/week-05-regression.qmd -[████████████████████████████████] 100% Complete (1.2s) - -╭─────────────────────────────────────────────────────────────────╮ -│ Content Analysis Report - Week 5 │ -│ Mode: moderate | Phase: 0 (heuristic-only) │ -╰─────────────────────────────────────────────────────────────────╯ - -📊 CONCEPT COVERAGE -┌────────────────────────────────────────────────────────────────┐ -│ Concept | Status │ -├────────────────────────────┼────────────────────────────────────┤ -│ Simple Linear Regression │ ✓ Introduced (Week 5) │ -│ Residual Analysis │ ✓ Introduced (Week 5) │ -│ R-squared Interpretation │ ✓ Introduced (Week 5) │ -└────────────────────────────────────────────────────────────────┘ - -🔗 PREREQUISITES -┌────────────────────────────────────────────────────────────────┐ -│ Prerequisite | Reference | Status │ -├────────────────────────────┼───────────┼───────────────────────┤ -│ Correlation (Week 3) │ ✓ │ ✓ Satisfied │ -│ Variance (Week 1) │ ✓ │ ✓ Satisfied │ -└────────────────────────────────────────────────────────────────┘ - -╭─────────────────────────────────────────────────────────────────╮ -│ SUMMARY │ -╰─────────────────────────────────────────────────────────────────╯ - - Status: ✓ READY TO DEPLOY (0 errors, 0 warnings) - - ✓ All prerequisites satisfied - ✓ All concepts properly defined - -Next steps: - 1. Deploy content: teach deploy --preview - 2. Or continue editing: quarto preview lectures/week-05-regression.qmd -``` - -### Configuration Schema (lesson-plan.yml) - -```yaml -# Analysis settings (all optional) -analysis: - enabled: true - mode: moderate # strict|moderate|relaxed - cache_ttl_hours: 168 # 7 days - - # Thresholds (can override per mode) - min_examples_per_objective: 2 - require_all_prerequisites: true - warn_missing_concepts: true - - # Advanced (optional) - background_analysis: false # Auto-analyze on file save (Phase 2+) - save_reports: true - report_dir: .teach/analysis - -# Per-week concept definitions (optional, alternative to frontmatter) -weeks: - - number: 5 - title: 'Simple Linear Regression' - concepts: - - id: slr-basics - title: 'Simple Linear Regression' - required: true - subtopics: - - 'Model formulation' - - 'Parameter interpretation' - prerequisites: - - correlation - - variance - learning_objectives: # Phase 3+ - - id: fit-model - title: 'Fit regression model in R' - bloom_level: 'apply' - min_examples: 3 -``` - -**Configuration Defaults (if sections missing):** - -- `analysis.enabled: false` (opt-in only) -- `analysis.mode: moderate` -- `analysis.cache_ttl_hours: 168` -- `analysis.min_examples_per_objective: 2` - -**Precedence:** Frontmatter overrides lesson-plan.yml for per-file settings - ---- - -## 🔒 Security Considerations - -### API Key Management (Phase 3+) - -- **Storage:** macOS Keychain via `security` command -- **Access:** Never log API keys, mask in error messages -- **Rotation:** Quarterly key rotation recommended -- **Rate limiting:** Prevent abuse with request limits -- **Fallback:** Graceful degradation to heuristic-only if API unavailable - -### File Access - -- **Read scope:** Project directory only (no parent directory traversal) -- **Write scope:** `.teach/` directory only -- **Network access:** None (Phase 0-2), Scholar/Claude API only (Phase 3+) -- **Cache files:** All in `.teach/` (git-ignored by default) - -### User Privacy - -- **No telemetry:** Zero tracking by default -- **Opt-in analytics:** Crash reports only (if user enables) -- **Content privacy:** No content sent to external services without explicit consent -- **Local-first:** All analysis done locally (Phase 0-2) - -### Permissions - -- **File permissions:** Respect existing file permissions -- **Cache files:** Created with `0600` (user read/write only) -- **Sensitive data:** Never cache API keys or secrets - ---- - -## ♻️ Backward Compatibility - -### Zero Breaking Changes Required - -- All analysis features are **opt-in** -- Existing commands work unchanged -- New frontmatter fields are **optional** -- `lesson-plan.yml` analysis section is **optional** -- Cache is transparent (users don't need to manage it) - -### Migration Path - -1. **Phase 0:** Users can adopt gradually (no flag day) -2. **Add frontmatter:** Start with one lecture, expand incrementally -3. **Enable caching:** Automatic (no user action required) -4. **Add lesson-plan config:** Optional configuration when needed -5. **Enable AI analysis:** Phase 3+ feature (opt-in with `--ai` flag) - -### Versioning Strategy - -- **Features:** Versioned by phase (Phase 0, Phase 1, etc.) -- **Cache schema:** Versioned independently (`concept-graph-v1`, `concept-graph-v2`) -- **Graceful handling:** Old cache files rebuilt if schema mismatch -- **No migrations:** Cache is disposable (rebuild on schema change) - -### Deprecation Policy - -- **No features deprecated** (additive only) -- **Cache format:** Old formats supported for 2 versions -- **Configuration:** Old config keys supported indefinitely - ---- - -## ⚠️ Open Questions - RESOLVED ✅ - -### Q1: AI Service Integration → RESOLVED - -**Question:** Use Claude API directly or Scholar service? - -**RESOLUTION:** Option D (Hybrid with Fallback) - -**Implementation Plan:** - -- **Phase 0-1:** Heuristic-only (no AI dependency) - - Extract concepts from frontmatter - - Validate prerequisites (user-defined only) - - Readability scores (textstat-style algorithms) - - **Benefit:** Zero API dependency, instant results, reliable - -- **Phase 2:** Test Scholar API semantic analysis - - Check if Scholar exposes concept extraction API - - If yes: integrate for enhanced analysis - - If no: continue with heuristics (perfectly functional) - -- **Phase 3+:** Claude API direct (if needed) - - Only if Scholar limitations found - - Add API key management (Keychain) - - Cost tracking for transparency - -**Decision:** Proceed with heuristic-only MVP (Phase 0-1) - ---- - -### Q2: Analysis Caching Strategy → RESOLVED - -**Question:** Where and how to cache analysis results? - -**RESOLUTION:** Option A (JSON files) - Enhanced - -**Cache Structure:** - -``` -.teach/ -├── analysis-cache/ -│ ├── cache-index.json # Global cache metadata -│ ├── lectures/ -│ │ ├── week-01-lecture.json # Mirrors source structure -│ │ └── week-02-lecture.json -│ └── assignments/ -│ └── hw-01.json -├── concepts.json # Global concept graph -└── prerequisites.json # Prerequisites database -``` - -**Benefits:** - -- Simple, git-ignorable, inspectable -- No database setup required -- Parallel-safe with file locking (Phase 2+) -- Easy debugging (just cat the JSON) - ---- - -### Q3: teach validate integration → RESOLVED - -**Question:** Should teach validate auto-run teach analyze? - -**RESOLUTION:** Option B with name change - -**Implementation:** - -```bash -teach validate [file] # Layers 1-5 (syntax, render, etc) -teach validate --deep [file] # Layers 1-6 (adds content analysis) -``` - -**Rationale:** `--deep` better conveys comprehensive analysis than `--quality` - ---- - -### Q4: Performance vs Accuracy Trade-off → RESOLVED - -**Question:** Fast heuristics or slow AI analysis? - -**RESOLUTION:** Option C (hybrid approach) with explicit flags - -**Implementation:** - -```bash -# Fast heuristics only (< 5s) - DEFAULT Phase 0-2 -teach analyze --fast - -# AI-powered analysis (30s+) - Phase 3+ -teach analyze --ai - -# Default behavior depends on phase -teach analyze # Phase 0-2: heuristic only - # Phase 3+: hybrid (heuristics + AI) -``` - ---- - -### Q5: teach slides --optimize output format → RESOLVED - -**Question:** How to present slide suggestions? - -**RESOLUTION:** Option C (terminal) + optional B (`--save-report`) - -**Implementation:** - -- Interactive terminal output (default) -- Optional: `teach slides --optimize --save-report analysis.md` - ---- - -## ✅ Review Checklist - -### Functional Requirements - -**Phase 0 (Ultra-MVP):** - -- [ ] teach analyze command (basic) -- [ ] Concept extraction from frontmatter -- [ ] Prerequisite checking (user-defined) -- [ ] Text output format -- [ ] Help text with examples - -**Phase 1 (Foundation):** - -- [ ] Caching system (content-hash based) -- [ ] Batch analysis (`--all`, `--week N`) -- [ ] JSON output format -- [ ] teach status integration - -**Phase 2 (Integration):** - -- [ ] Interactive review mode (`--interactive`) -- [ ] teach validate --deep integration -- [ ] teach deploy --check-prereqs integration -- [ ] Background processing (async queue) -- [ ] HTML report generation - -**Phase 3+ (Enhancement):** - -- [ ] Learning objective validation -- [ ] AI-powered concept extraction -- [ ] Readability analysis -- [ ] Custom validators - -**Phase 4+ (Advanced):** - -- [ ] Slide optimization integration -- [ ] Key concept emphasis -- [ ] Slide break suggestions - -### Performance Requirements - -**Phase 0:** - -- [ ] Analysis completes in < 5s for single lecture -- [ ] No caching (rebuild each time) - -**Phase 1+:** - -- [ ] Cache hit < 100ms -- [ ] Cache miss < 5s (heuristic) -- [ ] Cache hit rate > 85% -- [ ] Memory usage < 50MB - -**Phase 3+:** - -- [ ] AI analysis < 30s with progress indicator -- [ ] Background processing non-blocking - -### UX Requirements - -**Phase 0:** - -- [ ] Progress indicators (simple spinner) -- [ ] Actionable error messages -- [ ] ADHD-friendly colors and layout - -**Phase 1+:** - -- [ ] Visual progress bars -- [ ] Clear next-step recommendations -- [ ] Configurable strictness - -**Phase 2+:** - -- [ ] Keyboard shortcuts in interactive mode -- [ ] HTML reports with styling - -### Integration Requirements - -**All Phases:** - -- [ ] No breaking changes to existing commands -- [ ] Backward compatible with existing configs -- [ ] Graceful degradation if features unavailable - -**Phase 1+:** - -- [ ] teach status shows analysis summary -- [ ] Cache transparent to user - -**Phase 2+:** - -- [ ] teach validate --deep integration -- [ ] teach deploy --check-prereqs integration -- [ ] Git hooks integration (optional) - -### Testing Requirements - -**Phase 0:** - -- [ ] 20 unit tests (concept extraction, prerequisite checking) -- [ ] 5 integration tests (full workflow) - -**Phase 1:** - -- [ ] 40 unit tests (add caching tests) -- [ ] 10 integration tests - -**Phase 2:** - -- [ ] 60 unit tests -- [ ] 20 integration tests -- [ ] Performance benchmarks - -**Phase 3+:** - -- [ ] 100+ unit tests -- [ ] 50+ integration tests -- [ ] AI integration tests (mocked) - -### Documentation Requirements - -**Phase 0:** - -- [ ] Basic help text (`teach analyze --help`) -- [ ] Quick start in README - -**Phase 1:** - -- [ ] Comprehensive help text -- [ ] Updated TEACHING-WORKFLOW-V3-GUIDE.md -- [ ] Example frontmatter snippets - -**Phase 2:** - -- [ ] Quick reference card -- [ ] Full integration guide -- [ ] Troubleshooting section - -**Phase 3+:** - -- [ ] AI integration guide -- [ ] API reference for analysis functions -- [ ] Best practices guide - ---- - -## 📝 Implementation Roadmap (REVISED) - -### Phase 0: Ultra-MVP (4-5 hours) ← START HERE - -**Goal:** Prove the concept with minimal features. - -**Deliverables:** - -1. `lib/concept-extraction.zsh` (250 lines) - - Extract concepts from frontmatter `concepts:` field - - Build simple concept graph (ID + prerequisites) - - Save to `.teach/concepts.json` - -2. `lib/prerequisite-checker.zsh` (200 lines) - - Check if prerequisites satisfied (week ordering) - - Report violations with suggestions - -3. `commands/teach-analyze.zsh` (150 lines) - - `teach analyze [file]` - single file only - - Display concepts found - - Check prerequisites (user-defined only) - - Text output only (no JSON, no reports) - -4. Integration (50 lines) - - Source libraries in `flow.plugin.zsh` - - Add routing in `teach-dispatcher.zsh` - - Add basic help text - -5. Tests (600 lines) - - 20 unit tests (concept extraction, prerequisite checking) - - 5 integration tests (full workflow) - -**Scope Limitations (Phase 0):** - -- ❌ No caching (rebuild each time) -- ❌ No AI (heuristic-only) -- ❌ No interactive mode -- ❌ No batch analysis (`--all`, `--week N`) -- ❌ No JSON output -- ❌ No HTML reports -- ❌ No learning objectives -- ❌ No readability scores - -**Success Criteria:** - -- User can add `concepts:` to frontmatter -- `teach analyze lectures/week-05.qmd` validates prerequisites -- Warning if Week 5 requires concept from Week 7 -- < 2s analysis time (no caching) -- 25 tests pass (20 unit + 5 integration) - -**Time Estimate:** 4-5 hours - ---- - -### Phase 1: Foundation (6-8 hours) - -**Goal:** Add caching and batch analysis. - -**Deliverables:** - -1. Caching system (400 lines) - - Content-hash based cache - - `.teach/analysis-cache/` structure - - Cache index with metadata - - Cache invalidation logic - -2. Batch analysis (200 lines) - - `--all` flag (analyze all files) - - `--week N` flag (analyze week N) - - Progress indicators for batch - -3. JSON output (100 lines) - - `--format json` flag - - Structured JSON schema - -4. teach status integration (100 lines) - - Show analysis summary - - Last analysis time - - Concept count - -5. Enhanced help (150 lines) - - Comprehensive examples - - Flag documentation - - Troubleshooting tips - -6. Tests (1000 lines) - - 40 unit tests (add caching tests) - - 10 integration tests - -**Success Criteria:** - -- Cache hit < 100ms -- Cache hit rate > 85% in typical workflow -- Batch analysis non-blocking (spinner) -- JSON output validates against schema -- 50 total tests pass (40 unit + 10 integration) - -**Time Estimate:** 6-8 hours - ---- - -### Phase 2: Integration (8-10 hours) - -**Goal:** Full integration with existing commands. - -**Deliverables:** - -1. Interactive mode (300 lines) - - `--interactive` flag - - Step through suggestions one-by-one - - Accept/reject/skip actions - -2. teach validate --deep (200 lines) - - Layer 6: Content analysis - - Integration with existing 5 layers - - Update validation status - -3. teach deploy --check-prereqs (200 lines) - - Pre-deployment gate - - Check changed files - - Block/warn based on mode - -4. Background processing (400 lines) - - Async analysis queue - - Worker pool (ZSH background jobs) - - Job status tracking - -5. HTML reports (300 lines) - - `--report` flag - - Styled HTML output - - Concept graph visualization - -6. Git hooks integration (150 lines) - - Optional pre-commit hook - - Run analysis on changed files - - Fast-fail on violations - -7. Tests (1400 lines) - - 60 unit tests - - 20 integration tests - - Performance benchmarks - -**Success Criteria:** - -- Interactive mode keyboard shortcuts work -- teach validate --deep integrates cleanly -- Background processing non-blocking -- HTML reports render correctly -- 80 total tests pass (60 unit + 20 integration) - -**Time Estimate:** 8-10 hours - ---- - -### Phase 3: AI Enhancement (10-12 hours) - -**Goal:** Add AI-powered semantic analysis. - -**Deliverables:** - -1. Scholar API integration (400 lines) - - Test semantic analysis capabilities - - Concept extraction from content (not just frontmatter) - - Prerequisite inference - -2. Learning objective validation (350 lines) - - Parse objectives from frontmatter - - Count examples per objective - - Flag insufficient examples - -3. Readability analysis (250 lines) - - Flesch reading ease - - Sentence complexity - - Vocabulary difficulty - -4. Quality scorer service (400 lines) - - Aggregate quality metrics - - Scoring algorithm - - Recommendations - -5. AI cost tracking (200 lines) - - Track API calls - - Estimate costs - - `--costs` flag - -6. Tests (1600 lines) - - 100 unit tests (add AI tests) - - 30 integration tests (mocked AI) - -**Success Criteria:** - -- Scholar API integration working (or graceful fallback) -- Learning objectives validated correctly -- Readability scores accurate -- AI analysis < 30s with progress -- 130 total tests pass - -**Time Estimate:** 10-12 hours - ---- - -### Phase 4: Slide Optimization (8-10 hours) - -**Goal:** AI-suggested slide breaks and key concept emphasis. - -**Deliverables:** - -1. Slide optimizer service (400 lines) - - Analyze lecture structure - - Suggest slide breaks (beyond H2/H3) - - Identify key concepts for emphasis - -2. teach slides --optimize integration (300 lines) - - Load cached analysis - - Apply slide break suggestions - - Add key concept callouts - -3. Slide break preview (200 lines) - - `--preview-breaks` flag - - Show suggestions before generation - - Accept/reject interface - -4. Tests (1200 lines) - - 40 unit tests (slide optimizer) - - 10 integration tests (teach slides) - -**Success Criteria:** - -- Slide breaks suggested accurately -- Key concepts identified correctly -- Integration with PR #280 seamless -- 180 total tests pass - -**Time Estimate:** 8-10 hours - ---- - -### Phase 5: Polish & Documentation (6-8 hours) - -**Goal:** Production-ready release. - -**Deliverables:** - -1. Comprehensive documentation (2000 lines) - - User guide (INTELLIGENT-CONTENT-ANALYSIS.md) - - Quick reference card (REFCARD-CONTENT-ANALYSIS.md) - - Update TEACHING-WORKFLOW-V3-GUIDE.md - - Update TEACH-DISPATCHER-REFERENCE.md - -2. Error handling improvements (300 lines) - - Better error messages - - Recovery suggestions - - Diagnostic commands - -3. Performance optimizations (200 lines) - - Parallel file parsing - - Optimize cache reads - - Reduce memory usage - -4. Final testing (400 lines) - - 50+ integration tests - - Performance benchmarks - - Manual QA walkthrough - -**Success Criteria:** - -- Documentation complete and accurate -- All edge cases handled -- Performance targets met -- 230+ total tests pass - -**Time Estimate:** 6-8 hours - ---- - -## 📊 Total Effort Summary - -| Phase | Scope | Effort | Cumulative | -| --------- | ---------------------------------------- | ---------- | ---------- | -| Phase 0 | Ultra-MVP (prerequisite checking) | 4-5h | 5h | -| Phase 1 | Foundation (caching, batch) | 6-8h | 13h | -| Phase 2 | Integration (validate, deploy) | 8-10h | 23h | -| Phase 3 | AI Enhancement (objectives, readability) | 10-12h | 35h | -| Phase 4 | Slide Optimization (PR #280) | 8-10h | 45h | -| Phase 5 | Polish & Documentation | 6-8h | 53h | -| **Total** | **All phases** | **42-53h** | **53h** | - -**Orchestrated:** With parallel agent execution, total time ~40-50h (20% savings) - -**Phase 0 Standalone:** Can stop after Phase 0 (5h) and still have valuable prerequisite checking feature. - ---- - -## 📜 History - -| Date | Event | Status | -| ---------- | ------------------------------------------ | -------- | -| 2026-01-20 | Initial brainstorm with deep mode + agents | draft | -| 2026-01-20 | Backend architecture analysis (agent) | complete | -| 2026-01-20 | UX design analysis (agent) | complete | -| 2026-01-20 | Spec captured from brainstorm | draft | -| 2026-01-20 | Spec reviewed and refined | ready ✅ | - ---- - -## 🔗 Related Work - -### Integration Points - -- **PR #280** - teach slides (lecture-to-slides conversion) - Phase 4 integration -- **Phase 1 (Quarto)** - Custom Validator Framework - Phase 2 integration -- **Phase 2 (Quarto)** - Performance Monitor - All phases -- **Teaching Workflow v3.0** - Git hooks, backup system, deploy enhancements - -### Similar Tools (Inspiration) - -- **Grammarly** - Real-time content suggestions (UX inspiration) -- **Vale** - Prose linter for docs (heuristic validation inspiration) -- **textstat** - Readability analysis (algorithm inspiration) -- **Bloom's taxonomy validators** - Academic content validation (pedagogical inspiration) - ---- - -## 🎬 Next Steps - -1. ✅ **Spec approved** (status: ready) -2. **Create Phase 0 implementation plan** (detailed task breakdown) -3. **Get user approval** on Phase 0 scope -4. **Create feature branch:** `feature/teach-analyze` -5. **Implement Phase 0** (4-5 hours) -6. **Test and iterate** -7. **Decide:** Continue to Phase 1 or ship Phase 0 standalone? - ---- - -**End of Specification** ✅ diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 88b36b628..574d2669d 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -6,6 +6,55 @@ The format follows [Keep a Changelog](https://keepachangelog.com/), and this pro --- +## [6.4.0] - 2026-02-03 + +### Added + +- **Teach Deploy v2** (`teach deploy --direct`) - Direct merge deployment with 8-15s cycle time + - `--direct` / `-d` - Direct merge deploy (draft → main, push) + - `--dry-run` / `--dry` - Preview deploy without side effects + - `--rollback [N]` / `--rb [N]` - Forward rollback via `git revert` (N=display index) + - `--history` / `--hist` - View deploy history table + - `--ci` - Non-interactive CI mode (auto-detected when no TTY) + - `-m "message"` - Custom commit message + - Smart commit messages - auto-categorize by file type (content, config, infra, deploy) + - `.STATUS` auto-update - Sets `last_deploy`, `deploy_count`, `teaching_week` + +- **Deploy History Tracking** (`.flow/deploy-history.yml`) - Append-only YAML history + - Records timestamp, mode, commit hashes, branch, file count, user, duration + - `yq`-based reading for list/get/count operations + - YAML injection prevention via single-quote escaping + +- **Deploy Rollback** - Forward rollback via `git revert` + - Interactive picker or explicit index (`--rollback 1`) + - Merge commit detection with `-m 1` parent specification + - Rollback recorded in history with `mode: "rollback"` + - CI mode requires explicit index (no interactive picker) + +### Fixed + +- Merge commit rollback now detects parent count and uses `git revert -m 1` +- `commit_before` captures target branch HEAD (not current branch) +- Dirty worktree guard prevents deploy with uncommitted changes +- Stderr capture pattern prevents silent failures (replaced `2>/dev/null` with `$(cmd 2>&1)`) +- Dead dry-run code path fixed (was unreachable due to wrong condition) +- `_deploy_cleanup_globals` prevents variable leakage between calls + +### Documentation + +- `docs/guides/TEACH-DEPLOY-GUIDE.md` (1,092 lines) - Complete user guide +- `docs/reference/REFCARD-DEPLOY-V2.md` (216 lines) - Quick reference card +- `docs/tutorials/31-teach-deploy-v2.md` (336 lines) - Step-by-step tutorial +- `docs/reference/MASTER-API-REFERENCE.md` - 14 new function entries + +### Tests + +- 81 new tests: 36 unit + 22 integration + 23 E2E +- 51-test dogfooding suite against demo-course fixture +- Merge commit rollback regression tests (diverged branches, parent detection) + +--- + ## [5.20.0] - 2026-01-28 ### Added diff --git a/docs/demos/tutorials/tutorial-teach-deploy.gif b/docs/demos/tutorials/tutorial-teach-deploy.gif index 77ce5e1b3..19d1c86ca 100644 Binary files a/docs/demos/tutorials/tutorial-teach-deploy.gif and b/docs/demos/tutorials/tutorial-teach-deploy.gif differ diff --git a/docs/demos/tutorials/tutorial-teach-deploy.tape b/docs/demos/tutorials/tutorial-teach-deploy.tape index 7108678fe..19d629847 100644 --- a/docs/demos/tutorials/tutorial-teach-deploy.tape +++ b/docs/demos/tutorials/tutorial-teach-deploy.tape @@ -1,6 +1,6 @@ -# VHS Demo: teach deploy - Preview Deployment -# Part of flow-cli Teaching Workflow v3.0 -# Tutorial: Deploy with preview workflow and status checking +# VHS Demo: teach deploy v2 - Direct Merge, History, Rollback +# Part of flow-cli v6.4.0 +# Tutorial 31: Fast Deployments with teach deploy v2 Output tutorial-teach-deploy.gif @@ -23,48 +23,47 @@ Type "clear" Enter Show # Title -Type "echo 'Teaching Workflow v3.0: teach deploy'" Enter -Sleep 1s -Type "echo 'Preview Deployment Workflow'" Enter +Type "echo '=== teach deploy v2 (flow-cli v6.4.0) ==='" Enter Sleep 2s -Type "" Enter -Sleep 1s - -# Show help -Type "echo 'Available options:'" Enter +# Step 1: Dry-run preview +Type "echo ''" Enter +Type "echo '1. Preview before deploying (dry-run):'" Enter Sleep 1s -Type "teach deploy --help | head -20" Enter +Type "teach deploy --dry-run --direct" Enter Sleep 4s -Type "" Enter +Type "echo ''" Enter Type "clear" Enter Sleep 1s -# Check status before deploy -Type "echo 'Check deployment status:'" Enter +# Step 2: Direct deploy +Type "echo '2. Fast direct deploy (8-15s):'" Enter Sleep 1s -Type "teach status" Enter -Sleep 3s +Type "teach deploy --direct -m 'content: week-05 lecture'" Enter +Sleep 5s -Type "" Enter +Type "echo ''" Enter +Type "clear" Enter Sleep 1s -# Preview deployment -Type "echo 'Deploy to preview branch:'" Enter +# Step 3: Check history +Type "echo '3. View deployment history:'" Enter Sleep 1s -Type "teach deploy --preview" Enter -Sleep 5s +Type "teach deploy --history" Enter +Sleep 4s -Type "" Enter +Type "echo ''" Enter +Type "clear" Enter Sleep 1s -# Show what happened -Type "echo 'Preview deployed to gh-pages-preview branch'" Enter -Sleep 2s -Type "echo 'Review at: https://username.github.io/course/preview/'" Enter -Sleep 2s +# Step 4: Rollback +Type "echo '4. Rollback if something goes wrong:'" Enter +Sleep 1s +Type "teach deploy --rollback 1 --ci" Enter +Sleep 4s -Type "" Enter -Type "echo '✓ Preview deployment complete!'" Enter -Sleep 2s +Type "echo ''" Enter +Sleep 1s +Type "echo 'Done! Deploy, track, rollback — all in seconds.'" Enter +Sleep 3s diff --git a/docs/getting-started/index.md b/docs/getting-started/index.md index 8cd9c1e84..aecc2ec64 100644 --- a/docs/getting-started/index.md +++ b/docs/getting-started/index.md @@ -75,4 +75,4 @@ tags: --- -**v6.2.0** | [Home](../index.md) +**v6.4.0** | [Home](../index.md) diff --git a/docs/guides/TEACH-DEPLOY-GUIDE.md b/docs/guides/TEACH-DEPLOY-GUIDE.md index 7220eb9d0..65ddc33e9 100644 --- a/docs/guides/TEACH-DEPLOY-GUIDE.md +++ b/docs/guides/TEACH-DEPLOY-GUIDE.md @@ -8,7 +8,9 @@ tags: > Deploy your course website from local preview to production with confidence. > -> **Version:** v6.1.0+ | **Command:** `teach deploy` +> **Version:** v6.4.0+ | **Command:** `teach deploy` + +![teach deploy v2 Demo](../demos/tutorials/tutorial-teach-deploy.gif) --- @@ -36,6 +38,12 @@ The `teach deploy` command provides a safe, validated deployment workflow that: - **Manages indexes** - Updates navigation links (ADD/UPDATE/REMOVE) - **Creates PRs** - Uses draft → production branch workflow (no direct pushes) - **Validates prerequisites** - Ensures concepts are introduced before use (optional) +- **Direct merge mode** - 8-15s deploys without PR overhead (`--direct`) +- **Smart commit messages** - Auto-categorized from changed file paths +- **Deploy history** - Tracks every deployment in `.flow/deploy-history.yml` +- **Rollback** - Forward rollback via `git revert` (`--rollback`) +- **Dry-run preview** - Preview without mutation (`--dry-run`) +- **CI mode** - Non-interactive for automated pipelines (`--ci`) ### Design Philosophy @@ -115,12 +123,14 @@ git checkout draft ## Deployment Modes -`teach deploy` supports two modes: +`teach deploy` supports multiple modes: -| Mode | Trigger | Use Case | -|------|---------|----------| -| **Full Site** | `teach deploy` | Deploy all changes from draft → production | -| **Partial** | `teach deploy ` | Deploy specific files/directories | +| Mode | Trigger | Speed | Use Case | +|------|---------|-------|----------| +| **Full Site (PR)** | `teach deploy` | 45-90s | Review before production | +| **Direct Merge** | `teach deploy -d` | 8-15s | Quick fixes, solo instructor | +| **Partial** | `teach deploy ` | Varies | Deploy specific files/directories | +| **Dry-Run** | `teach deploy --dry-run` | <1s | Preview all operations | ### Full Site Deployment @@ -163,6 +173,126 @@ teach deploy lectures/ --- +## Direct Merge Mode (v6.4.0) + +Skip the PR workflow for fast, direct deployment: + +```bash +# Basic direct deploy +teach deploy --direct + +# With custom commit message +teach deploy -d -m "Week 5 lecture updates" + +# CI-friendly direct deploy +teach deploy --ci -d +``` + +**Process:** +1. Run preflight checks +2. Generate smart commit message (or use `--message`) +3. Merge draft → production directly +4. Push to remote +5. Log in deploy history +6. Update .STATUS + +**When to use:** +- Solo instructor (no review needed) +- Quick fixes (typos, minor updates) +- CI/CD pipelines +- When 45-90s PR workflow is too slow + +--- + +## Deploy History & Rollback (v6.4.0) + +### Viewing History + +```bash +# Show last 10 deployments +teach deploy --history + +# Show last 20 +teach deploy --history 20 +``` + +**Output:** +``` +Recent deployments: + +# When Mode Files Message +1 2026-02-03 14:30 direct 3 content: week-05 lecture +2 2026-02-02 09:15 pr 15 deploy: full site update +3 2026-02-01 16:45 partial 2 content: assignment 3 +``` + +### Rollback + +Revert any deployment safely using forward rollback (`git revert`): + +```bash +# Interactive picker +teach deploy --rollback + +# Rollback most recent deployment +teach deploy --rollback 1 + +# Rollback 2nd most recent (CI mode) +teach deploy --rollback 2 --ci +``` + +**Safety:** Rollback uses `git revert` (not `git reset`), preserving full history. The rollback itself is recorded in deploy history with mode "rollback". + +### History File + +Stored at `.flow/deploy-history.yml` (git-tracked, append-only): + +```yaml +deploys: + - timestamp: '2026-02-03T14:30:22-06:00' + mode: 'direct' + commit_hash: 'a1b2c3d4' + commit_before: 'e5f6g7h8' + branch_from: 'draft' + branch_to: 'main' + file_count: 15 + commit_message: 'content: week-05 lecture' +``` + +--- + +## Dry-Run Preview (v6.4.0) + +Preview deployment without making any changes: + +```bash +# Preview full site deploy +teach deploy --dry-run + +# Preview direct merge +teach deploy --dry-run --direct + +# Preview with custom message +teach deploy --preview -m "Week 5" +``` + +**Output:** +``` +DRY RUN — No changes will be made + +Would deploy 3 files: + lectures/week-05.qmd + scripts/analysis.R (dependency) + home_lectures.qmd (index update) + +Would commit: "content: week-05 lecture, analysis script" +Would merge: draft -> production (direct mode) +Would log: deploy #12 to .flow/deploy-history.yml +Would update: .STATUS (teaching_week: 5) +``` + +--- + ## Full Site Deployment ### Step-by-Step Workflow @@ -927,34 +1057,25 @@ https://deploy-preview-42--course-site.netlify.app ### Rollback Procedure -If deployment breaks production: - -**1. Revert merge commit:** +**Using teach deploy (v6.4.0+):** ```bash -# Find merge commit -git log --oneline main | head -5 - -# Revert -git revert -m 1 -git push origin main -``` +# View recent deployments +teach deploy --history -**2. Wait for GitHub Pages redeploy** +# Rollback most recent +teach deploy --rollback 1 -**3. Fix issue on draft branch:** - -```bash -git checkout draft -# Fix broken content -git add . -git commit -m "Fix deployment issue" +# Verify site +open https://username.github.io/course-repo/ ``` -**4. Re-deploy:** +**Manual rollback (if needed):** ```bash -teach deploy +git log --oneline main | head -5 +git revert -m 1 +git push origin main ``` --- @@ -969,5 +1090,5 @@ teach deploy --- -**Last Updated:** 2026-02-02 -**Version:** v6.1.0 +**Last Updated:** 2026-02-03 +**Version:** v6.4.0 diff --git a/docs/guides/TEACHING-COMMANDS-DETAILED.md b/docs/guides/TEACHING-COMMANDS-DETAILED.md index eb5e032c4..56dbdc514 100644 --- a/docs/guides/TEACHING-COMMANDS-DETAILED.md +++ b/docs/guides/TEACHING-COMMANDS-DETAILED.md @@ -1008,7 +1008,7 @@ teach deploy # → Validates branch # → Merges to production # → Deploys to students -# → Takes < 2 minutes +# → Takes ~15 seconds (direct) or < 2 min (PR) # 5. Verify live open https://yoursite.github.io/stat-545 diff --git a/docs/guides/TEACHING-DATES-GUIDE.md b/docs/guides/TEACHING-DATES-GUIDE.md index 02ecbd518..acaea59be 100644 --- a/docs/guides/TEACHING-DATES-GUIDE.md +++ b/docs/guides/TEACHING-DATES-GUIDE.md @@ -776,7 +776,7 @@ git commit -m "chore: extend hw3 deadline by 2 days" teach deploy ``` -**Time:** < 2 minutes from edit to live +**Time:** ~15 seconds with `teach deploy -d` (or < 2 min via PR) ### Semester Rollover: New Semester Setup diff --git a/docs/guides/TEACHING-DEMO-GUIDE.md b/docs/guides/TEACHING-DEMO-GUIDE.md index 1a63b6167..36290434e 100644 --- a/docs/guides/TEACHING-DEMO-GUIDE.md +++ b/docs/guides/TEACHING-DEMO-GUIDE.md @@ -347,7 +347,7 @@ sleep 1 teach deploy echo "" -echo "✓ Materials live in < 2 minutes!" +echo "✓ Materials live in seconds with teach deploy -d!" ``` --- diff --git a/docs/guides/TEACHING-WORKFLOW-V3-GUIDE.md b/docs/guides/TEACHING-WORKFLOW-V3-GUIDE.md index 6a55fa2ed..489a8aa4f 100644 --- a/docs/guides/TEACHING-WORKFLOW-V3-GUIDE.md +++ b/docs/guides/TEACHING-WORKFLOW-V3-GUIDE.md @@ -2096,7 +2096,7 @@ git commit -m "Restore introduction section with #sec-introduction label" teach deploy # Output: -# ✓ Fix deployed in < 2 minutes +# ✓ Fix deployed in ~15 seconds (direct mode) # ✓ Student can access corrected content ``` diff --git a/docs/guides/TEACHING-WORKFLOW-VISUAL.md b/docs/guides/TEACHING-WORKFLOW-VISUAL.md index 23e559a10..1c993dc66 100644 --- a/docs/guides/TEACHING-WORKFLOW-VISUAL.md +++ b/docs/guides/TEACHING-WORKFLOW-VISUAL.md @@ -252,7 +252,7 @@ Insertions: 45 ✅ Deployed in 12 seconds 🌐 Site: https://yourname.github.io/stat-545 -⏳ GitHub Actions deploying... (usually < 2 minutes) +⏳ Direct mode: live in ~15 seconds | PR mode: GitHub Actions (< 2 minutes) 💡 Tip: Check Actions tab to monitor build progress ``` diff --git a/docs/guides/TEACHING-WORKFLOW.md b/docs/guides/TEACHING-WORKFLOW.md index d8805bf22..572d65e43 100644 --- a/docs/guides/TEACHING-WORKFLOW.md +++ b/docs/guides/TEACHING-WORKFLOW.md @@ -10,7 +10,7 @@ The **Teaching Workflow** is a deployment-focused workflow system designed to solve the 5-15 minute deployment pain point for course websites. It provides: -✅ **Fast Deployment** - Typo to live in < 2 minutes +✅ **Fast Deployment** - Typo to live in 8-15 seconds (`teach deploy -d`) ✅ **Branch Safety** - Prevents accidental edits to production branch ✅ **Course Context** - Teaching-aware work sessions ✅ **Automation Scripts** - One-command deployment and archival @@ -58,7 +58,7 @@ work stat-545 - Merges `draft` → `production` - Pushes to GitHub - GitHub Actions deploys to GitHub Pages -- **Completes in < 2 minutes** +- **Completes in 8-15 seconds** (direct mode) or < 2 minutes (PR mode) --- @@ -554,7 +554,7 @@ s545d # 5. Verify live # Visit course website -# Total time: < 2 minutes from typo discovery to live fix +# Total time: ~15 seconds with teach deploy -d (or < 2 min via PR) ``` --- diff --git a/docs/guides/index.md b/docs/guides/index.md index b7e54a976..f82d81902 100644 --- a/docs/guides/index.md +++ b/docs/guides/index.md @@ -85,4 +85,4 @@ tags: --- -**v6.2.0** | [Home](../index.md) +**v6.4.0** | [Home](../index.md) diff --git a/docs/help/QUICK-REFERENCE.md b/docs/help/QUICK-REFERENCE.md index c65b7836d..3132be3f3 100644 --- a/docs/help/QUICK-REFERENCE.md +++ b/docs/help/QUICK-REFERENCE.md @@ -8,7 +8,7 @@ tags: **Purpose:** Single-page command lookup for all flow-cli features **Format:** Copy-paste ready with expected outputs -**Version:** v6.2.0 +**Version:** v6.4.0 **Last Updated:** 2026-02-02 --- @@ -949,8 +949,11 @@ teach scholar status teach analyze lectures/ # Analyze content teach exam "Midterm Topics" # Generate exam -# Deployment -teach deploy # Publish site +# Deployment (v6.4.0) +teach deploy --direct # Direct merge deploy +teach deploy --dry-run # Preview deploy +teach deploy --rollback 1 # Undo last deploy +teach deploy --history # Show deploy log teach status # Verify ``` @@ -1022,6 +1025,6 @@ mcp help --- -**Version:** v6.2.0 +**Version:** v6.4.0 **Last Updated:** 2026-02-02 **Contributors:** See [CHANGELOG.md](../CHANGELOG.md) diff --git a/docs/index.md b/docs/index.md index 5f62b89b4..45d9573cf 100644 --- a/docs/index.md +++ b/docs/index.md @@ -7,7 +7,7 @@ tags: # Flow CLI -[![Version](https://img.shields.io/badge/version-v6.2.0-blue)](https://github.com/Data-Wise/flow-cli/releases/tag/v6.2.0) +[![Version](https://img.shields.io/badge/version-v6.4.0-blue)](https://github.com/Data-Wise/flow-cli/releases/tag/v6.4.0) [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE) [![Tests](https://img.shields.io/github/actions/workflow/status/Data-Wise/flow-cli/test.yml?label=tests&branch=main)](https://github.com/Data-Wise/flow-cli/actions/workflows/test.yml) [![Docs](https://img.shields.io/github/actions/workflow/status/Data-Wise/flow-cli/docs.yml?label=docs&branch=main)](https://github.com/Data-Wise/flow-cli/actions/workflows/docs.yml) @@ -26,9 +26,9 @@ tags: ``` **That's it!** No configuration required. -!!! success "🎉 What's New: v6.2.0 - Docs Overhaul + Website Reorganization" - Reduced navigation from 14 to 7 sections. Landing pages with grid cards. Tags for cross-cutting discovery. - [→ See what's new](RELEASES.md){ .md-button } +!!! success "🎉 What's New: v6.4.0 - teach deploy v2" + Direct deploy in 8-15 seconds. Smart commit messages. Deploy history tracking. Safe rollback via `git revert`. + [→ See what's new](CHANGELOG.md){ .md-button } [→ All releases](RELEASES.md){ .md-button } --- @@ -185,7 +185,7 @@ Choose your path based on what you need right now: --- - Deploy courses in < 2 minutes + Deploy courses in 8-15 seconds [→ Teaching Guide](guides/TEACHING-SYSTEM-ARCHITECTURE.md) @@ -266,4 +266,4 @@ catch "idea" # Quick capture --- -**v6.2.0** · Pure ZSH · Zero Dependencies · MIT License +**v6.4.0** · Pure ZSH · Zero Dependencies · MIT License diff --git a/docs/reference/MASTER-API-REFERENCE.md b/docs/reference/MASTER-API-REFERENCE.md index d3b518d32..eeff77263 100644 --- a/docs/reference/MASTER-API-REFERENCE.md +++ b/docs/reference/MASTER-API-REFERENCE.md @@ -6778,6 +6778,54 @@ _teach_plan_help --- +### teach-deploy v2 Helpers + +**Source:** `lib/deploy-history-helpers.zsh`, `lib/deploy-rollback-helpers.zsh`, `lib/dispatchers/teach-deploy-enhanced.zsh` +**Added:** v6.4.0 + +#### Deploy History (`lib/deploy-history-helpers.zsh`) + +Append-only YAML deploy history tracking at `.flow/deploy-history.yml`. + +| Function | Signature | Purpose | +|----------|-----------|---------| +| `_deploy_history_append` | ` [pr_number] [tag] [duration]` | Append deploy entry (never rewrites file) | +| `_deploy_history_list` | `[count]` | Display recent deploys as formatted table (default: 5) | +| `_deploy_history_get` | `` | Retrieve entry by display index (1=most recent). Sets `DEPLOY_HIST_*` variables | +| `_deploy_history_count` | (none) | Print total number of recorded deploys | + +**Exported variables** (from `_deploy_history_get`): + +`DEPLOY_HIST_TIMESTAMP`, `DEPLOY_HIST_MODE`, `DEPLOY_HIST_COMMIT`, `DEPLOY_HIST_COMMIT_BEFORE`, `DEPLOY_HIST_BRANCH_FROM`, `DEPLOY_HIST_BRANCH_TO`, `DEPLOY_HIST_FILE_COUNT`, `DEPLOY_HIST_MESSAGE`, `DEPLOY_HIST_PR`, `DEPLOY_HIST_TAG`, `DEPLOY_HIST_USER`, `DEPLOY_HIST_DURATION` + +#### Deploy Rollback (`lib/deploy-rollback-helpers.zsh`) + +Forward rollback via `git revert` with history tracking. + +| Function | Signature | Purpose | +|----------|-----------|---------| +| `_deploy_rollback` | `[N] [--ci]` | Main rollback with interactive picker. N=display index (1=most recent) | +| `_deploy_perform_rollback` | ` ` | Execute forward rollback. Detects merge commits (parent count > 1) and uses `-m 1` | + +#### Deploy Orchestration (`lib/dispatchers/teach-deploy-enhanced.zsh`) + +| Function | Signature | Purpose | +|----------|-----------|---------| +| `_deploy_preflight_checks` | `` | Validate git state, config, branches. Sets `DEPLOY_*` variables | +| `_deploy_direct_merge` | ` ` | Direct merge deploy (push draft, checkout prod, merge, push) | +| `_deploy_dry_run_report` | (reads `DEPLOY_*` globals) | Preview deploy without side effects | +| `_deploy_update_status_file` | (reads `.STATUS` + history) | Update `.STATUS` with `last_deploy`, `deploy_count`, `teaching_week` | +| `_teach_deploy_enhanced` | `[flags...]` | Main entry point. Parses flags, dispatches to deploy/rollback/history/dry-run | +| `_deploy_cleanup_globals` | (none) | Unset `DEPLOY_COMMIT_BEFORE`, `DEPLOY_COMMIT_AFTER`, `DEPLOY_DURATION`, `DEPLOY_MODE` | +| `_teach_deploy_enhanced_help` | (none) | Print formatted help output | +| `_check_prerequisites_for_deploy` | (none) | Verify `git` and optional `yq` are available | + +**Exported variables** (from `_deploy_direct_merge` and `_deploy_perform_rollback`): + +`DEPLOY_COMMIT_BEFORE`, `DEPLOY_COMMIT_AFTER`, `DEPLOY_DURATION`, `DEPLOY_MODE` + +--- + ## See Also - [MASTER-DISPATCHER-GUIDE.md](MASTER-DISPATCHER-GUIDE.md) - Complete dispatcher reference diff --git a/docs/reference/MASTER-DISPATCHER-GUIDE.md b/docs/reference/MASTER-DISPATCHER-GUIDE.md index cad270873..40df92321 100644 --- a/docs/reference/MASTER-DISPATCHER-GUIDE.md +++ b/docs/reference/MASTER-DISPATCHER-GUIDE.md @@ -1641,28 +1641,30 @@ Creates comprehensive course analysis. #### Deployment Workflows +**Quick direct deploy (v6.4.0):** +```bash +teach deploy --direct # Merge draft → main, push +teach deploy -d -m "week 5" # Direct with custom message +teach deploy --dry-run # Preview first +``` + +**Deploy with rollback safety:** +```bash +teach deploy --direct # Deploy +teach deploy --history # Check history +teach deploy --rollback 1 # Undo most recent deploy +``` + **Preview before deploy:** ```bash qu preview # Review site locally # Fix any issues -teach deploy +teach deploy --direct # Deploy to production ``` -**Deploy with validation:** -```bash -# Check for broken links -markdown-link-check lectures/**/*.md - -# Deploy -teach deploy - -# Verify -curl -I https://username.github.io/stat-440/ -``` - --- #### Configuration Migration (v5.20.0) @@ -1981,8 +1983,14 @@ qu preview - `teach exam --template ` - Use template - `teach quiz ` - Generate quiz -**Deployment:** -- `teach deploy` - Deploy to GitHub Pages +**Deployment (v6.4.0):** +- `teach deploy` - Deploy via PR (default) +- `teach deploy --direct` / `teach dep -d` - Direct merge deploy +- `teach deploy --dry-run` / `teach dep --dry` - Preview without deploying +- `teach deploy --rollback [N]` / `teach dep --rb [N]` - Rollback deployment N (1=most recent) +- `teach deploy --history` / `teach dep --hist` - Show deploy history +- `teach deploy --ci` - CI/non-interactive mode +- `teach deploy -m "msg"` - Custom commit message **Migration (v5.20.0):** - `teach migrate-config` - Extract lesson plans from config diff --git a/docs/reference/REFCARD-DEPLOY-V2.md b/docs/reference/REFCARD-DEPLOY-V2.md new file mode 100644 index 000000000..1bb66c868 --- /dev/null +++ b/docs/reference/REFCARD-DEPLOY-V2.md @@ -0,0 +1,216 @@ +--- +tags: [reference, teaching, deploy] +--- + +# Quick Reference: teach deploy v2 + +> Enhanced Git-based Course Deployment with PR Workflow & Rollback + +## Commands + +| Command | Alias | Description | +|---------|-------|-------------| +| `teach deploy` | `teach dep` | Full site deploy via PR (draft → production) | +| `teach deploy -d` | `teach dep -d` | Direct merge, no PR (8-15s) | +| `teach deploy --dry-run` | `teach dep --preview` | Preview without executing | +| `teach deploy --rollback [N]` | `teach dep --rb [N]` | Rollback deployment N (1=most recent) | +| `teach deploy --history [N]` | `teach dep --hist [N]` | Show last N deploys (default: 10) | +| `teach deploy ` | `teach dep ` | Partial deploy (specific files/dirs) | + +## Deploy Modes + +| Mode | Command | Speed | Use Case | +|------|---------|-------|----------| +| PR (default) | `teach deploy` | 45-90s | Review before production | +| Direct merge | `teach deploy -d` | 8-15s | Quick fixes, solo instructor | +| Partial | `teach deploy file.qmd` | Varies | Single file updates | +| Dry-run | `teach deploy --dry-run` | <1s | Preview before executing | + +## Flags + +| Flag | Short | Description | +|------|-------|-------------| +| `--direct` | `-d` | Direct merge mode (no PR) | +| `--dry-run` | `--preview` | Preview without executing | +| `--rollback [N]` | `--rb [N]` | Revert deployment N from history | +| `--history [N]` | `--hist [N]` | Show recent deployments | +| `--ci` | | Force non-interactive mode | +| `--message "text"` | `-m` | Custom commit message | +| `--auto-commit` | | Auto-commit dirty files | +| `--auto-tag` | | Tag with timestamp | +| `--skip-index` | | Skip index management | +| `--check-prereqs` | | Validate prerequisites | +| `--direct-push` | | Alias for `--direct` (backward compat) | + +## Smart Commit Messages + +Auto-generated from changed file paths: + +``` +lectures/*.qmd → "content: week-05 lecture" +assignments/*.qmd → "content: assignment 3" +_quarto.yml → "config: quarto settings" +styles/*.css → "style: theme update" +Mixed files → "deploy: STAT-101 update" +``` + +Override with custom message: + +```bash +teach deploy -d -m "Week 5 lecture + lab" +``` + +## Deploy History + +Stored in `.flow/deploy-history.yml` (git-tracked): + +```yaml +deploys: + - timestamp: '2026-02-03T14:30:22-06:00' + mode: 'direct' + commit_hash: 'a1b2c3d4' + branch_from: 'draft' + branch_to: 'main' + file_count: 15 + commit_message: 'content: week-05 lecture' +``` + +View history: + +```bash +teach deploy --history # Last 10 deploys +teach deploy --history 20 # Last 20 deploys +``` + +## Rollback + +```bash +teach deploy --rollback # Interactive picker +teach deploy --rollback 1 # Most recent deploy +teach deploy --rollback 2 --ci # 2nd most recent, non-interactive +``` + +Uses `git revert` (forward rollback, not destructive reset). Merge commits are detected automatically and reverted with `-m 1` (parent specification). Rollback is recorded in history with `mode: "rollback"`. + +## CI Mode + +Auto-detected when no TTY (`[[ ! -t 0 ]]`), or forced with `--ci`: + +```bash +teach deploy --ci -d # Direct merge, no prompts +echo | teach deploy # Auto-detected (piped input) +``` + +## .STATUS Auto-Updates + +After successful deploy, non-destructively updates `.STATUS`: + +- `last_deploy:` → today's date +- `deploy_count:` → total deploys from history +- `teaching_week:` → calculated from `semester_info.start_date` + +Skips if `.STATUS` absent. + +## Output Format (Direct Mode) + +``` + Pre-flight Checks +───────────────────────────────────────────────── + [ok] Git repository + [ok] Config file found + [ok] On draft branch + [ok] Working tree clean + [ok] No production conflicts + + Smart commit: content: week-05 lecture + + Direct merge: draft -> production + [ok] Merged successfully + [ok] Pushed to origin/production + + History logged: #12 (2026-02-03 14:30) + .STATUS updated + + Direct deployment complete + Site: https://example.github.io/stat-545/ +``` + +## Configuration + +In `.flow/teach-config.yml`: + +```yaml +git: + draft_branch: draft # Default: "draft" + production_branch: main # Default: "main" + auto_pr: true # Default: true + require_clean: true # Default: true +``` + +## Workflows + +### Quick deploy (direct merge) + +```bash +# Make changes on draft branch +teach deploy -d +# 8-15 seconds → live +``` + +### Deploy via PR (default) + +```bash +# Make changes on draft branch +teach deploy +# Opens PR → review → merge → live +``` + +### Partial deploy (specific files) + +```bash +teach deploy lectures/week-05.qmd +teach deploy lectures/ assignments/hw-03.qmd +``` + +### Preview before deploying + +```bash +teach deploy --dry-run +# Shows what would happen without executing +``` + +### Rollback to previous version + +```bash +teach deploy --rollback +# Interactive picker shows recent deploys +# Select deployment to revert +``` + +### Non-interactive (CI/CD) + +```bash +teach deploy --ci -d -m "Auto-deploy from GitHub Actions" +``` + +## Files + +| File | Purpose | +|------|---------| +| `lib/dispatchers/teach-deploy-enhanced.zsh` | Main deploy implementation | +| `lib/git-helpers.zsh` | Smart commit messages | +| `lib/deploy-history-helpers.zsh` | History tracking | +| `lib/deploy-rollback-helpers.zsh` | Rollback via git revert | +| `.flow/deploy-history.yml` | Deploy history (git-tracked) | +| `.STATUS` | Auto-updated after deploy | + +## Related + +- `teach doctor` - Health check with deploy validation +- `teach init` - Initialize teaching project +- Guide: `docs/guides/TEACH-DEPLOY-GUIDE.md` +- Spec: `docs/specs/SPEC-teach-deploy-v2-2026-02-03.md` + +--- + +*v6.4.0 - teach deploy v2 command* diff --git a/docs/specs/BRAINSTORM-config-concept-graph-integration-2026-01-22.md b/docs/specs/BRAINSTORM-config-concept-graph-integration-2026-01-22.md deleted file mode 100644 index 51b38ac52..000000000 --- a/docs/specs/BRAINSTORM-config-concept-graph-integration-2026-01-22.md +++ /dev/null @@ -1,354 +0,0 @@ -# Config → Concept Graph Integration Brainstorm - -**Generated:** 2026-01-22 -**Context:** flow-cli teach analyze (Phase 0 → Phase 1 enhancement) -**Topic:** Auto-populate week numbers from `teach-config.yml` instead of requiring them in frontmatter - ---- - -## Problem Statement - -Currently, `teach analyze` Phase 0 resolves week numbers via: - -1. **Filename parsing:** `week-05-lecture.qmd` → week 5 -2. **Frontmatter field:** `week: 5` in YAML header -3. **Fallback:** Unknown (returns 0) - -This creates **redundancy** — the instructor must maintain week numbers in two places: -- `teach-config.yml` → `structure.units[].weeks[]` (maps weeks to topics) -- Each `.qmd` file → `week:` frontmatter field - -**Goal:** Make `teach-config.yml` the single source of truth for week assignments, with the concept graph auto-deriving week numbers from it. - ---- - -## Current Data Sources - -### teach-config.yml (authoritative schedule) - -```yaml -structure: - units: - - name: 'Unit 1: Foundations' - weeks: [1, 2, 3, 4] - topics: - - 'R basics & reproducible workflow' - - 'Data visualization I' - - 'Data wrangling' - - 'Data visualization II' -``` - -**Key insight:** `weeks[i]` ↔ `topics[i]` is a positional mapping. Week 1 = topic[0], Week 2 = topic[1], etc. - -### Lecture files (content) - -```yaml ---- -title: "Data Visualization I" -concepts: - introduces: - - id: ggplot2-basics - requires: - - r-environment ---- -``` - -**Current:** Must add `week: 2` to frontmatter. -**Desired:** Derive week from config lookup. - ---- - -## Options - -### Option A: Topic-Title Matching (Fuzzy) - -**Approach:** Match `.qmd` title to `structure.units[].topics[]` by string similarity. - -**Resolution chain:** -1. Read `structure.units` from config -2. For each `.qmd` file, compare `title:` to all topics -3. Match → derive week number from positional index - -**Pros:** -- Zero new frontmatter fields needed -- Works with existing config format -- No naming conventions required - -**Cons:** -- Fuzzy matching is fragile (titles evolve, abbreviations differ) -- Ambiguity when topics are similar across weeks -- Performance cost of string comparison -- Hard to debug when matching fails silently - -**Implementation complexity:** Medium (needs fuzzy matching logic) - ---- - -### Option B: Explicit File→Week Mapping in Config (Recommended) - -**Approach:** Add `files:` array to each unit's week definition. - -**New config format:** - -```yaml -structure: - units: - - name: 'Unit 1: Foundations' - weeks: - - number: 1 - topic: 'R basics & reproducible workflow' - files: ['lectures/week-01-*.qmd'] # glob pattern - - number: 2 - topic: 'Data visualization I' - files: ['lectures/week-02-*.qmd'] -``` - -**Resolution chain:** -1. Parse `structure.units[].weeks[].files[]` glob patterns -2. Match each `.qmd` file against patterns -3. Assign week number from matched entry - -**Pros:** -- Explicit, no ambiguity -- Supports multiple files per week -- Glob patterns handle naming variations -- Easy to validate (unmatched files = warning) -- Single source of truth - -**Cons:** -- Requires config schema change (breaking for existing configs) -- More verbose config (trade-off: clarity vs brevity) -- Instructor must maintain file list in config - -**Implementation complexity:** Low (glob matching is straightforward in ZSH) - ---- - -### Option C: Directory Convention (Zero-Config) - -**Approach:** Derive week from directory structure. - -**Expected layout:** - -``` -lectures/ -├── week-01/ -│ ├── lecture.qmd -│ └── lab.qmd -├── week-02/ -│ └── lecture.qmd -``` - -**Resolution chain:** -1. Parse parent directory name for `week-NN` pattern -2. Fall back to filename pattern (`week-NN-*.qmd`) -3. Fall back to frontmatter `week:` field - -**Pros:** -- Zero configuration needed -- Already partially implemented (filename parsing) -- Natural project organization - -**Cons:** -- Forces specific directory structure -- Many courses use flat `lectures/` with named files -- Doesn't leverage config schedule data at all -- Doesn't solve the "SSOT" problem - -**Implementation complexity:** Low (already mostly done) - ---- - -### Option D: Hybrid — Config-Enriched Filename Convention - -**Approach:** Keep filename-based resolution but validate/enrich with config data. - -**How it works:** - -1. **Primary:** Parse `week-NN` from filename (existing behavior) -2. **Enrich:** Cross-reference with `structure.units[].weeks[]` to get topic/unit context -3. **Validate:** Warn if file references a week not in config schedule -4. **Fallback:** If filename doesn't match, check `semester_info.weeks[]` array - -**New config addition (optional):** - -```yaml -semester_info: - weeks: - - number: 1 - start_date: '2026-01-12' - topic: 'R basics' - - number: 2 - start_date: '2026-01-19' - topic: 'Visualization I' -``` - -This array already exists in the schema but is rarely populated in practice. - -**Pros:** -- Non-breaking (filename parsing still works) -- Progressive enhancement (config adds context, doesn't replace) -- Validates consistency between filenames and config -- Catches drift (file says week 5, config has no week 5 topic) -- Reuses existing `semester_info.weeks[]` schema - -**Cons:** -- Still requires `week-NN` in filenames as primary source -- Two sources exist (filename + config) even if config is "enrichment" -- Doesn't fully eliminate frontmatter `week:` field - -**Implementation complexity:** Low-Medium - ---- - -## Recommended Path - -### Phase 0 (Current): Keep filename + frontmatter (no change) - -Already planned and in progress. Ship the MVP without config integration. - -### Phase 1 Enhancement: Option D (Hybrid Enrichment) - -Add config cross-referencing as a **validation layer**, not a replacement: - -```zsh -_get_week_from_file() { - local file="$1" - local config_file="$2" # NEW: optional config path - - # 1. Primary: filename pattern - local week=$(_parse_week_from_filename "$file") - - # 2. Fallback: frontmatter field - [[ -z "$week" ]] && week=$(_parse_week_from_frontmatter "$file") - - # 3. NEW: Validate against config (if available) - if [[ -n "$config_file" && -n "$week" ]]; then - _validate_week_in_config "$week" "$config_file" - fi - - # 4. NEW: Last resort - match title to config topics - if [[ -z "$week" && -n "$config_file" ]]; then - week=$(_resolve_week_from_config_topic "$file" "$config_file") - fi - - echo "${week:-0}" -} -``` - -### Phase 2 (Future): Option B (Explicit Mapping) - -Once the concept graph is proven useful, add the full `files:` mapping to config for courses that want zero-ambiguity week resolution. - ---- - -## Integration Points - -### Existing Infrastructure to Leverage - -| Component | Location | What It Provides | -|-----------|----------|-----------------| -| `_calculate_current_week()` | `lib/teaching-utils.zsh` | Week-from-date math | -| `semester_info.weeks[]` | Schema | Per-week topic+date array | -| `structure.units[]` | Config | Week→topic positional map | -| `_parse_week_number()` | `lib/index-helpers.zsh` | Filename week extraction | -| Config validator | `lib/config-validator.zsh` | Schema validation | - -### New Functions Needed (Phase 1) - -```zsh -# Resolve week number using config as authority -_resolve_week_from_config() { - local file="$1" - local config="$2" - # Try: filename → frontmatter → config topic match -} - -# Validate week assignment against config schedule -_validate_week_in_config() { - local week="$1" - local config="$2" - # Check: does config have this week? Is it a break week? -} - -# Match file title to config topic (fuzzy-ish) -_resolve_week_from_config_topic() { - local file="$1" - local config="$2" - # Extract title, compare to structure.units[].topics[] -} -``` - -### Schema Addition (for Phase 2) - -```json -"structure": { - "properties": { - "units": { - "items": { - "properties": { - "weeks": { - "oneOf": [ - { "type": "array", "items": { "type": "integer" } }, - { "type": "array", "items": { - "type": "object", - "properties": { - "number": { "type": "integer" }, - "topic": { "type": "string" }, - "files": { "type": "array", "items": { "type": "string" } } - } - }} - ] - } - } - } - } - } -} -``` - ---- - -## Validation Rules (New) - -When config integration is active, `teach analyze` gains these checks: - -| Rule | Severity | Description | -|------|----------|-------------| -| `week-not-in-config` | WARNING | File claims week N but config has no week N | -| `break-week-content` | WARNING | Content assigned to a break week | -| `orphan-week` | INFO | Config week has no matching content files | -| `topic-mismatch` | INFO | File topic differs significantly from config topic | - ---- - -## Decision Matrix - -| Criterion | Option A (Fuzzy) | Option B (Explicit) | Option C (Dir) | Option D (Hybrid) | -|-----------|-----------------|--------------------|----|---------| -| Breaking changes | None | Schema change | None | None | -| Accuracy | Low | High | Medium | High | -| Config effort | None | Medium | None | Low | -| SSOT achieved | Partial | Full | No | Partial → Full | -| Phase 0 compatible | Yes | No | Yes | Yes | -| Validation value | Low | High | Low | High | - ---- - -## Quick Wins (Immediate) - -1. **Populate `semester_info.weeks[]`** in the config template — currently empty in template but defined in schema -2. **Add topic field** to `semester_info.weeks[]` items (already in schema) -3. **Cross-reference in `_build_concept_graph()`** — add config awareness during graph construction - -## Next Steps - -1. [ ] Finish Phase 0 implementation (current priority) -2. [ ] Add `_validate_week_in_config()` to Phase 1 plan -3. [ ] Update schema examples to show populated `semester_info.weeks[]` -4. [ ] Design `teach analyze --validate-schedule` flag for config↔content consistency -5. [ ] Consider whether `structure.units` should be canonical or `semester_info.weeks` - ---- - -**Recommended:** Start with Option D in Phase 1 (non-breaking enrichment), graduate to Option B in Phase 2 if the concept graph proves its value. This avoids premature config schema changes while still delivering validation feedback. diff --git a/docs/specs/BRAINSTORM-dashboard-workflow-deep-dive-2026-01-19.md b/docs/specs/BRAINSTORM-dashboard-workflow-deep-dive-2026-01-19.md deleted file mode 100644 index e131a720b..000000000 --- a/docs/specs/BRAINSTORM-dashboard-workflow-deep-dive-2026-01-19.md +++ /dev/null @@ -1,170 +0,0 @@ -# Deep Dive: teach dashboard - Complete Workflow & Integration Analysis - -**Created:** 2026-01-19 -**Context:** Maximum depth interactive feature exploration -**Related:** SPEC-teach-dashboard-2026-01-18.md -**Status:** Comprehensive workflow analysis - ---- - -## Executive Summary - -This document provides a **complete walkthrough** of how `teach dashboard` integrates with the existing flow-cli teaching workflow, from course setup through student-facing deployment. - -**Key Insight:** The dashboard feature sits at the **intersection** of three systems: -1. **Config Management** (teach-config.yml) -2. **Content Generation** (Scholar) -3. **Deployment Pipeline** (teach deploy → GitHub Pages) - ---- - -[Content continues as before through Part 9...] - ---- - -## Part 10: Security Considerations - -### XSS Prevention in Client JavaScript - -**CRITICAL:** Dashboard content includes user-provided announcements that must be sanitized before display. - -**Vulnerable code (DO NOT USE):** - -```javascript -// ❌ VULNERABLE: innerHTML with untrusted content -function updateDashboard(week, announcements) { - const dashboard = document.getElementById('dashboard'); - dashboard.innerHTML = `

${week.topic}

`; // XSS risk! -} -``` - -**Secure code (REQUIRED):** - -```javascript -// ✅ SECURE: Use textContent or createElement -function updateDashboard(week, announcements) { - const dashboard = document.getElementById('dashboard'); - dashboard.textContent = ''; // Clear existing - - // Create elements safely - const hero = document.createElement('div'); - hero.className = 'hero-banner'; - - const title = document.createElement('h1'); - title.textContent = week.topic; // Safe - no HTML parsing - hero.appendChild(title); - - const focus = document.createElement('p'); - focus.textContent = week.focus; - hero.appendChild(focus); - - dashboard.appendChild(hero); -} -``` - -**For HTML content (use DOMPurify):** - -```javascript -import DOMPurify from 'dompurify'; - -function updateAnnouncements(announcements) { - announcements.forEach(a => { - const div = document.createElement('div'); - div.className = `announcement ${a.type}`; - - const title = document.createElement('h4'); - title.textContent = a.title; // Plain text - safe - div.appendChild(title); - - const content = document.createElement('p'); - // If content contains HTML, sanitize it - if (a.content_html) { - content.innerHTML = DOMPurify.sanitize(a.content_html); - } else { - content.textContent = a.content; // Plain text - safe - } - div.appendChild(content); - - document.getElementById('announcements').appendChild(div); - }); -} -``` - -**teach dashboard announce validation:** - -```zsh -_teach_dashboard_announce() { - local title="$1" - local content="$2" - - # Validate no HTML injection attempts - if echo "$title" | grep -q '<\|>\|&\|"'; then - _flow_log_error "Title contains unsafe characters" - _flow_log_info "Avoid: < > & \" in titles" - return 1 - fi - - if echo "$content" | grep -q '` | -| Scholar path reference | Add clarifying note | "In the Scholar plugin repository" | -| Integration priority | teach prompt command | Core functionality first | -| Prompt customization | Future consideration | Wait for v1 usage feedback | -| Additional prompts | Wait for Scholar | Scholar commands cover these | -| Testing strategy | Markdown lint only | Fast structural validation | -| Documentation location | Add to TEACHING-WORKFLOW-V3-GUIDE | Integrate with existing guide | - ---- - -## Quick Wins (< 30 min each) - -### ⚡ 1. Fix README Scholar Path Reference - -**File:** `lib/templates/teaching/claude-prompts/README.md` -**Change:** Add clarifying note to line 38 - -```markdown -## Integration with Teaching Style - -These prompts work with the Scholar plugin's 4-layer teaching style system. - -> **Note:** Teaching style examples are located in the Scholar plugin repository, -> not in flow-cli. See: `~/.claude/plugins/cache/scholar/*/examples/teaching-styles/` -``` - -**Effort:** 5 minutes - -### ⚡ 2. Rename IMPLEMENTATION.md → SPEC-teaching-prompts.md - -**Action:** Move and rename after merge - -```bash -# After PR #283 merges to dev -git mv IMPLEMENTATION.md docs/specs/SPEC-teaching-prompts.md -``` - -**Effort:** 2 minutes - -### ⚡ 3. Add Markdown Linting to PR Checklist - -**File:** `IMPLEMENTATION.md` (before move) -**Add to Test plan:** - -```markdown -## Test plan - -- [x] Verify all prompts are valid Markdown (`npx markdownlint-cli lib/templates/teaching/claude-prompts/*.md`) -- [ ] Test integration with Scholar `/teaching:lecture` -- [ ] Verify README documentation is accurate -``` - -**Effort:** 5 minutes - ---- - -## Medium Effort (1-2 hours) - -### 🔧 4. Implement `teach prompt` Command - -**Location:** `lib/dispatchers/teach-dispatcher.zsh` - -**Subcommands:** - -| Command | Action | -|---------|--------| -| `teach prompt` | Show help (same as `teach prompt help`) | -| `teach prompt list` | List available prompts | -| `teach prompt ` | Display prompt content | -| `teach prompt help` | Show command help | - -**Implementation Sketch:** - -```zsh -# In teach-dispatcher.zsh, add to case statement: -prompt) shift; _teach_prompt "$@" ;; - -# New function -_teach_prompt() { - local prompts_dir="${FLOW_ROOT}/lib/templates/teaching/claude-prompts" - - case "$1" in - list) - _flow_log_header "Available Teaching Prompts" - echo "" - for f in "$prompts_dir"/*.md; do - [[ "$(basename "$f")" == "README.md" ]] && continue - local name=$(basename "$f" .md) - local desc=$(head -5 "$f" | grep -E "^#" | head -1 | sed 's/^# //') - printf " ${CYAN}%-25s${RESET} %s\n" "$name" "$desc" - done - echo "" - echo "Usage: ${BOLD}teach prompt ${RESET} to view a prompt" - ;; - help|--help|-h) - _teach_prompt_help - ;; - "") - _teach_prompt_help - ;; - *) - local prompt_file="$prompts_dir/$1.md" - if [[ -f "$prompt_file" ]]; then - ${PAGER:-less} "$prompt_file" - else - _flow_log_error "Unknown prompt: $1" - echo "Run ${BOLD}teach prompt list${RESET} to see available prompts" - return 1 - fi - ;; - esac -} - -_teach_prompt_help() { - cat << 'EOF' -teach prompt - Display Claude Code teaching prompts - -USAGE: - teach prompt - -COMMANDS: - list List available prompts - Display prompt content (opens in pager) - help Show this help - -AVAILABLE PROMPTS: - lecture-notes Comprehensive lecture documents (20-40 pages) - revealjs-slides Visual presentations (25+ slides) - derivations-appendix Mathematical theory appendices - -EXAMPLES: - teach prompt list # See all prompts - teach prompt lecture-notes # View lecture prompt - teach prompt derivations-appendix # View derivations prompt - -INTEGRATION: - These prompts complement Scholar plugin commands: - - /teaching:lecture uses lecture-notes.md structure - - /teaching:slides uses revealjs-slides.md structure - - Customize output via .claude/teaching-style.local.md -EOF -} -``` - -**Effort:** 45-60 minutes - -### 🔧 5. Update TEACHING-WORKFLOW-V3-GUIDE - -**Location:** `docs/guides/TEACHING-WORKFLOW-V3-GUIDE.md` - -**Add new section:** - -```markdown -## Claude Code Teaching Prompts - -flow-cli includes optimized prompts for generating statistics course content. - -### Available Prompts - -| Prompt | Purpose | Output | -|--------|---------|--------| -| `lecture-notes` | Comprehensive lecture documents | 20-40 pages | -| `revealjs-slides` | Visual presentations | 25+ slides | -| `derivations-appendix` | Mathematical theory appendices | Variable | - -### Viewing Prompts - -```bash -# List all prompts -teach prompt list - -# View a specific prompt -teach prompt lecture-notes - -# Use with Scholar (recommended) -/teaching:lecture "Factorial ANOVA" -``` - -### Integration with Scholar - -These prompts work with Scholar's `/teaching:*` commands. Configure your -teaching style in `.claude/teaching-style.local.md` for customization. - -See [Scholar plugin documentation](https://github.com/Data-Wise/scholar) for -teaching style examples. - -``` - -**Effort:** 30-45 minutes - ---- - -## Long-term (Future Sessions) - -### 📋 6. Course-Level Prompt Customization (Future) -**Trigger:** After v1 usage feedback -**Scope:** -- `teach init` option to copy prompts to `.teach/prompts/` -- Course-specific overrides via `teaching.yml` -- Template inheritance system - -### 📋 7. Additional Prompts (Scholar Owns) -**Status:** Wait for Scholar plugin -**Candidates:** assignment.md, exam.md, syllabus.md, rubric.md -**Rationale:** Scholar commands (`/teaching:assignment`, etc.) already handle these - -### 📋 8. ai-recipes Integration (Future) -**Scope:** Add `[teach-lecture]`, `[teach-slides]` recipes -**Depends on:** Successful teach prompt command adoption - ---- - -## Recommended Path - -**Immediate (PR #283 amendments):** -1. ⚡ Fix README Scholar path reference (5 min) -2. ⚡ Add markdown lint to test plan (5 min) - -**Follow-up PR (feature/teach-prompt-command):** -3. 🔧 Implement `teach prompt` command (60 min) -4. 🔧 Update TEACHING-WORKFLOW-V3-GUIDE (45 min) -5. ⚡ Move IMPLEMENTATION.md to docs/specs/ (2 min) - -**Timeline:** ~2 hours total implementation - ---- - -## Implementation Order - -``` - -PR #283 (current) -├── Quick fix: README path clarification -└── Quick fix: Add markdown lint to test plan - ↓ -[Merge PR #283 to dev] - ↓ -PR #284 (new: feature/teach-prompt-command) -├── Add _teach_prompt() to teach-dispatcher.zsh -├── Add _teach_prompt_help() function -├── Update TEACHING-WORKFLOW-V3-GUIDE.md -├── Move IMPLEMENTATION.md → docs/specs/SPEC-teaching-prompts.md -└── Add completions for teach prompt - ↓ -[Merge to dev, prepare v5.16.0] - -``` - ---- - -## Files to Modify - -| File | Change | Priority | -|------|--------|----------| -| `lib/templates/teaching/claude-prompts/README.md` | Add Scholar path note | Now | -| `IMPLEMENTATION.md` | Add lint to test plan | Now | -| `lib/dispatchers/teach-dispatcher.zsh` | Add prompt subcommand | Follow-up | -| `docs/guides/TEACHING-WORKFLOW-V3-GUIDE.md` | Add prompts section | Follow-up | -| `completions/_teach` | Add prompt completions | Follow-up | -| `IMPLEMENTATION.md` | Move to docs/specs/ | After merge | - ---- - -## Next Steps - -1. **Amend PR #283** with quick fixes (README + lint) -2. **Merge PR #283** to dev -3. **Create feature/teach-prompt-command** branch -4. **Implement teach prompt** command -5. **Update documentation** -6. **Release v5.16.0** - ---- - -## Related Commands - -- `/workflow:spec-review` - Review this as a spec -- `/craft:do "implement teach prompt"` - Start implementation -- `gh pr view 283` - View current PR - ---- - -*Generated by /workflow:brainstorm deep* diff --git a/docs/specs/BRAINSTORM-scholar-check-mechanism.md b/docs/specs/BRAINSTORM-scholar-check-mechanism.md deleted file mode 100644 index 0a2b401c7..000000000 --- a/docs/specs/BRAINSTORM-scholar-check-mechanism.md +++ /dev/null @@ -1,250 +0,0 @@ -# BRAINSTORM: flow-cli Scholar Check Mechanism - -**Generated:** 2026-01-14 -**Context:** flow-cli + Scholar coordination -**Principle:** Teaching will ALWAYS be coordinated with Scholar - ---- - -## Overview - -Create a mechanism for flow-cli to verify Scholar availability before invoking Scholar teaching commands. - ---- - -## Problem Statement - -When a user runs `teach exam "Topic"` (future wrapper), flow-cli needs to: -1. Check if Claude Code CLI is available -2. Check if Scholar plugin is installed/enabled -3. Check if the specific command exists -4. Optionally check version compatibility - -Currently, flow-cli has no way to verify Scholar availability - it would just fail if Scholar isn't present. - ---- - -## Options - -### Option A: Simple CLI Check (Recommended) - -**Effort:** ⚡ Quick (30 min) -**Pros:** Simple, no dependencies, works offline -**Cons:** Can't check plugin availability directly - -```zsh -_teach_check_scholar() { - # 1. Check Claude Code CLI exists - if ! command -v claude &>/dev/null; then - _flow_log_error "Claude Code CLI not found" - _flow_log_info "Install: https://claude.ai/code" - return 1 - fi - - # 2. Check version (optional) - local version=$(claude --version 2>/dev/null | head -1) - if [[ -z "$version" ]]; then - _flow_log_warn "Could not determine Claude Code version" - fi - - return 0 -} -``` - ---- - -### Option B: Plugin Detection via Settings - -**Effort:** 🔧 Medium (1-2 hours) -**Pros:** Can verify Scholar is actually installed -**Cons:** Relies on settings file structure (may change) - -```zsh -_teach_check_scholar() { - local settings_global="$HOME/.claude/settings.json" - local settings_local=".claude/settings.local.json" - - # Check if Scholar plugin is configured - # Note: This depends on Claude Code settings structure - - # Global settings - if [[ -f "$settings_global" ]]; then - if grep -q '"scholar"' "$settings_global" 2>/dev/null; then - return 0 - fi - fi - - # Local settings - if [[ -f "$settings_local" ]]; then - if grep -q '"scholar"' "$settings_local" 2>/dev/null; then - return 0 - fi - fi - - _flow_log_warn "Scholar plugin not detected in Claude Code settings" - return 1 -} -``` - ---- - -### Option C: Runtime Validation (Most Robust) - -**Effort:** 🏗️ Large (2-3 hours) -**Pros:** Actually tests Scholar availability -**Cons:** Requires Claude API call, slower, costs API credits - -```zsh -_teach_validate_scholar() { - # Actually call Scholar to verify it works - local result=$(claude --print "/scholar:ping" 2>&1) - - if [[ "$result" == *"pong"* ]]; then - return 0 - else - _flow_log_error "Scholar plugin not responding" - return 1 - fi -} -``` - -**Note:** This requires Scholar to have a `/scholar:ping` command (doesn't exist yet). - ---- - -### Option D: Hybrid Approach (Pragmatic) - -**Effort:** 🔧 Medium (1 hour) -**Pros:** Fast local checks + graceful degradation -**Cons:** More complex logic - -```zsh -_teach_check_scholar() { - local status=0 - - # Level 1: CLI exists - if ! command -v claude &>/dev/null; then - _flow_log_error "Claude Code CLI not found" - _flow_log_info "Install: https://claude.ai/code" - return 1 - fi - - # Level 2: Config exists (optional) - if [[ ! -f ".flow/teach-config.yml" ]]; then - _flow_log_warn "No .flow/teach-config.yml found" - _flow_log_info "Run 'teach init' to create config" - status=2 # Warning level - fi - - # Level 3: Scholar section in config (optional) - if [[ -f ".flow/teach-config.yml" ]] && ! grep -q "^scholar:" .flow/teach-config.yml; then - _flow_log_warn "No 'scholar:' section in config" - _flow_log_info "Scholar will use defaults" - # Don't fail - Scholar can work without explicit config - fi - - return $status -} -``` - ---- - -## Recommended Path - -**Start with Option A + D hybrid:** - -1. **Phase 1:** Implement simple CLI check (Option A) - - Fast, reliable, no false negatives - - Works offline - -2. **Phase 2:** Add config validation (Option D) - - Check for .flow/teach-config.yml - - Check for scholar section - - Provide helpful messages - -3. **Future:** Consider Option C if Scholar adds `/scholar:ping` - - Only for explicit validation scenarios - - Not for every command invocation - ---- - -## Implementation Location - -``` -lib/dispatchers/teach-dispatcher.zsh -├── _teach_check_scholar() # NEW: Validation function -├── _teach_scholar_wrapper() # Uses _teach_check_scholar -└── teach() # Main dispatcher -``` - ---- - -## Integration with Wrapper Spec - -The wrapper spec (`SPEC-teach-scholar-wrappers.md`) already defines preflight checks: - -```zsh -_teach_preflight() { - # 1. Check config exists - # 2. Check Scholar section exists (warning only) - # 3. Check Claude Code available ← This is _teach_check_scholar -} -``` - -The `_teach_check_scholar()` function implements step 3. - ---- - -## User Experience - -### Before (no check) - -```bash -$ teach exam "Hypothesis Testing" -# ... long pause ... -# Cryptic error from Claude CLI -``` - -### After (with check) - -```bash -$ teach exam "Hypothesis Testing" -❌ teach: Claude Code CLI not found - Install: https://claude.ai/code - -# OR - -⚠️ teach: No .flow/teach-config.yml found - Run 'teach init' first or Scholar will use defaults - -Proceeding with Scholar... -``` - ---- - -## Quick Wins - -1. ⚡ Add `command -v claude` check to teach dispatcher (5 min) -2. ⚡ Add helpful error messages with install links (10 min) -3. ⚡ Add config existence check (10 min) - ---- - -## Next Steps - -1. [ ] Decide which option to implement -2. [ ] Add to teach-dispatcher.zsh -3. [ ] Test with and without Claude Code installed -4. [ ] Update SPEC-teach-scholar-wrappers.md with final implementation - ---- - -## Questions for User - -1. Should the check run on every `teach` command, or only Scholar wrappers? -2. Should we fail hard (exit 1) or warn and continue? -3. Want to add a `teach doctor` command for explicit validation? - ---- - -*Last Updated: 2026-01-14* diff --git a/docs/specs/BRAINSTORM-scholar-coordination-2026-01-21.md b/docs/specs/BRAINSTORM-scholar-coordination-2026-01-21.md deleted file mode 100644 index bb0df1da5..000000000 --- a/docs/specs/BRAINSTORM-scholar-coordination-2026-01-21.md +++ /dev/null @@ -1,1126 +0,0 @@ -# Scholar Integration Coordination - Deep Brainstorm - -**Generated:** 2026-01-21 -**Context:** Coordinating teaching prompt enhancements (PR #283) with Scholar plugin v2.2.0 -**Related Specs:** -- SPEC-teaching-prompts-enhancement-2026-01-21.md -- Scholar: BRAINSTORM-flow-cli-coordination-2026-01-14.md -- Scholar: BRAINSTORM-unified-architecture-2026-01-15.md - ---- - -## Executive Summary - -**Goal:** Seamlessly integrate new 3-tier prompt storage system with Scholar's teaching content generation while maintaining single unified config (teach-config.yml) and enabling bidirectional sync. - -**Current State:** -- flow-cli v5.15.0 has teach-dispatcher (9 commands, 853 lines) -- Scholar v2.0.1 released (8 teaching commands) -- Scholar v2.1.0 in progress (/teaching:lecture command) -- Scholar v2.2.0 planned with --config flag support -- Shared config protocol (RFC-001) exists with ownership sections - -**Coordination Challenge:** -- New prompt system adds 3-tier storage + template rendering + customization -- Scholar needs course config for content generation -- Both systems need to read/write teach-config.yml without conflicts -- Users want: "two-way sync, merge into single config, leverage all Scholar features" - ---- - -## Architecture Overview - -### Current Integration (v5.9.0 → v2.0.1) - -``` -┌─────────────────────────────────────────────────────────┐ -│ User Command: teach lecture "ANOVA" │ -└──────────────┬──────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────┐ -│ teach-dispatcher.zsh (flow-cli) │ -│ - Routes to _teach_lecture() │ -│ - Validates teach-config.yml exists │ -│ - Launches Scholar with args │ -└──────────────┬──────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────┐ -│ Scholar Plugin (via Claude Code) │ -│ - Reads .flow/teach-config.yml │ -│ - Uses scholar: section for style settings │ -│ - Generates content based on course: section │ -│ - Returns generated Quarto file │ -└─────────────────────────────────────────────────────────┘ -``` - -### Proposed Integration (v5.15.0 → v2.2.0) - -``` -┌─────────────────────────────────────────────────────────┐ -│ User Command: teach lecture "ANOVA" │ -└──────────────┬──────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────┐ -│ teach-dispatcher.zsh (flow-cli) - ENHANCED │ -│ - Auto-detects prompt from teach-config.yml │ -│ - Resolves prompt via 3-tier system: │ -│ 1. .claude/prompts/lecture-notes.local.md (Course) │ -│ 2. ~/.flow/prompts/lecture-notes.md (User) │ -│ 3. lib/templates/.../lecture-notes.md (Global) │ -│ - Renders template variables from teach-config.yml │ -│ - Launches Scholar with --config + --prompt flags │ -└──────────────┬──────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────┐ -│ Scholar Plugin v2.2.0 - ENHANCED │ -│ - Receives --config .flow/teach-config.yml │ -│ - Receives --prompt .claude/prompts/lecture.local.md │ -│ - Merges prompt with Scholar's teaching style system │ -│ - Validates against schema (YAML DSL) │ -│ - Generates content with full context │ -│ - Writes back metadata to teach-config.yml (optional) │ -└─────────────────────────────────────────────────────────┘ -``` - ---- - -## Coordination Points - -### 1. Config File Unification - -**Current State:** -- flow-cli uses: `.flow/teach-config.yml` -- Scholar reads: `.flow/teach-config.yml` -- RFC-001 defines ownership sections - -**Enhancement:** -- Merge `scholar:` section into `teach-config.yml` -- Add `prompts:` section (new) for prompt metadata -- Add `templates:` section (new) for rendering variables - -**Proposed Schema (teach-config.yml):** - -```yaml -# === flow-cli OWNED SECTIONS === -course: - name: "STAT 440: Regression Analysis" - code: "STAT 440" - semester: "Spring 2025" - institution: "Iowa State University" - instructor: "Dr. Example" - - # NEW: R Package Configuration - r_packages: - core: [emmeans, lme4, car] - diagnostics: [DHARMa, performance] - reporting: [sjPlot, modelsummary] - - # NEW: LaTeX Notation Configuration - notation: - expectation: "\\mathbb{E}" - variance: "\\text{Var}" - probability: "\\mathbb{P}" - style: "macros" # macros | inline | mixed - - # NEW: Pedagogy Configuration - pedagogy: - derivation_depth: "rigorous-with-intuition" # heuristic | rigorous-with-intuition | full-rigor - practice_problems_count: [4, 10] - include_diagnostic_workflow: true - -semester_info: - start_date: "2025-01-13" - end_date: "2025-05-09" - weeks: 16 - holidays: ["2025-03-17", "2025-03-19"] - -branches: - main: "main" - draft: "draft" - archive: "archive" - -deployment: - method: "github-pages" - url: "https://example.github.io/stat-440/" - auto_deploy: true - -# === Scholar OWNED SECTIONS === -scholar: - course_info: - level: "undergraduate" # undergraduate | graduate | phd - field: "statistics" - prerequisites: ["STAT 301", "STAT 326"] - - style: - tone: "formal" # formal | conversational | technical - notation: "statistical" # mathematical | statistical | computational - depth: "detailed" # overview | detailed | comprehensive - - teaching_layers: - layer_1_core: true - layer_2_intuition: true - layer_3_rigor: true - layer_4_advanced: false - -# === NEW: PROMPT SYSTEM SECTIONS === -prompts: - # Prompt metadata (managed by flow-cli) - library: "default" # default | custom-name - version: "1.0.0" - - # Per-prompt customizations - lecture: - template: "lecture-notes.md" - customized: true - last_modified: "2026-01-15" - - assignment: - template: "assignment.md" - customized: false - - exam: - template: "exam.md" - customized: true - last_modified: "2026-01-10" - -templates: - # Template rendering variables (used by both systems) - packages_display: "emmeans, lme4, car, DHARMa" - notation_summary: "Using \\mathbb{E} for expectation, \\text{Var} for variance" - course_title_short: "Regression Analysis" -``` - -**Ownership Protocol:** - -| Section | Owner | Can Read | Can Write | -|---------|-------|----------|-----------| -| `course:` | flow-cli | Both | flow-cli only | -| `semester_info:` | flow-cli | Both | flow-cli only | -| `branches:` | flow-cli | Both | flow-cli only | -| `deployment:` | flow-cli | Both | flow-cli only | -| `scholar:` | Scholar | Both | Scholar only | -| `prompts:` | flow-cli | Both | flow-cli only | -| `templates:` | flow-cli | Both | Both (read-only for Scholar) | - -**Write Coordination:** -- flow-cli writes: `course:`, `semester_info:`, `prompts:`, `templates:` -- Scholar writes: `scholar:` only -- Both read entire file -- No conflicts because sections are isolated - ---- - -### 2. Prompt System Integration - -**Challenge:** Scholar generates content, flow-cli manages prompts. How do they coordinate? - -#### Option A: Scholar-Aware Prompts (Recommended) - -**Flow:** - -1. User runs: `teach lecture "ANOVA"` -2. flow-cli resolves prompt via 3-tier system -3. flow-cli renders template variables from `teach-config.yml` -4. flow-cli passes **rendered prompt** to Scholar via `--prompt` flag -5. Scholar merges prompt with its teaching style layers -6. Scholar generates content using **combined context** - -**Benefits:** -- Scholar gets full context (prompt + config) -- Prompts can reference Scholar features -- Users customize both systems independently -- Clear separation of concerns - -**Example Prompt with Scholar Integration:** - -```markdown -# Lecture Notes Generation - Scholar Integration - -Generate comprehensive lecture notes for a {{course.level}} course in {{course.field}}. - -## Course Context -- Course: {{course.name}} ({{course.code}}) -- Instructor: {{course.instructor}} -- Level: {{scholar.course_info.level}} -- Field: {{scholar.course_info.field}} - -## Teaching Style -Apply Scholar's {{scholar.teaching_layers.layer_2_intuition ? "intuition-focused" : "standard"}} approach with {{scholar.style.tone}} tone. - -## Content Requirements - -### 1. Core Concepts (Layer 1) -- {{pedagogy.derivation_depth == "heuristic" ? "Heuristic explanations" : "Formal derivations"}} -- Visual diagrams where appropriate -- Real-world examples from {{course.field}} - -### 2. Statistical Implementation -- R code using: {{templates.packages_display}} -- Diagnostic workflow: {{pedagogy.include_diagnostic_workflow ? "Include DHARMa diagnostics" : "Basic diagnostics only"}} - -### 3. Mathematical Notation -- Expectation: {{notation.expectation}} -- Variance: {{notation.variance}} -- Style: {{notation.style == "macros" ? "Use LaTeX macros" : "Inline notation"}} - -### 4. Practice Problems -- Generate {{pedagogy.practice_problems_count[0]}}-{{pedagogy.practice_problems_count[1]}} problems -- Range from basic to challenging - -## Scholar Layer Integration - -{{#if scholar.teaching_layers.layer_2_intuition}} -**Layer 2 (Intuition):** Include conceptual explanations before formal definitions -{{/if}} - -{{#if scholar.teaching_layers.layer_3_rigor}} -**Layer 3 (Rigor):** Provide formal proofs and theoretical foundations -{{/if}} - -{{#if scholar.teaching_layers.layer_4_advanced}} -**Layer 4 (Advanced):** Add cutting-edge research connections -{{/if}} -``` - -**Implementation:** - -```bash -# In teach-dispatcher.zsh -_teach_lecture() { - local topic="$1" - - # 1. Resolve prompt via 3-tier system - local prompt_file=$(_resolve_prompt "lecture-notes") - - # 2. Render template variables from teach-config.yml - local rendered_prompt=$(_render_template "$prompt_file" "$PWD/.flow/teach-config.yml") - - # 3. Save rendered prompt to temp file - local temp_prompt=$(mktemp) - echo "$rendered_prompt" > "$temp_prompt" - - # 4. Launch Scholar with both flags - claude --skill "scholar:manuscript:lecture" \ - --config "$PWD/.flow/teach-config.yml" \ - --prompt "$temp_prompt" \ - "$topic" - - # 5. Cleanup - rm "$temp_prompt" -} -``` - -#### Option B: Scholar-Oblivious Prompts - -**Flow:** -1. Scholar reads `teach-config.yml` directly -2. flow-cli prompts are completely separate -3. User manually ensures consistency - -**Issues:** -- High chance of config drift -- Prompts can't reference Scholar features -- Poor user experience (two systems to maintain) - -**Verdict:** ❌ Not recommended - ---- - -### 3. Command Coordination - -**Current Scholar Commands (v2.0.1):** - -``` -/teaching:lecture - Generate lecture notes -/teaching:assignment - Generate homework -/teaching:exam - Generate exams -/teaching:quiz - Generate quizzes -/teaching:syllabus - Generate syllabus -/teaching:rubric - Generate grading rubrics -/teaching:feedback - Generate student feedback -/teaching:slides - Generate presentation slides -``` - -**Current teach-dispatcher Commands (v5.15.0):** - -``` -teach init - Initialize teaching project -teach status - Show project status -teach deploy - Deploy to GitHub Pages -teach exam - Wrapper for Scholar /teaching:exam -teach lecture - Wrapper for Scholar /teaching:lecture -teach assignment - Wrapper for Scholar /teaching:assignment -teach quiz - Wrapper for Scholar /teaching:quiz -teach syllabus - Wrapper for Scholar /teaching:syllabus -``` - -**New Prompt Commands (Proposed):** - -``` -teach prompt list - Show available prompts -teach prompt show - Display prompt (paginated) -teach prompt info - Show metadata -teach prompt edit - Copy to .claude/, open in $EDITOR -teach prompt enhance - Interactive wizard -teach prompt promote - Copy .local.md → ~/.flow/ -``` - -**Coordination Strategy:** - -| User Action | flow-cli Command | Scholar Skill | Prompt Used | -|-------------|------------------|---------------|-------------| -| Generate lecture | `teach lecture "Topic"` | `/teaching:lecture` | Auto-resolved via 3-tier | -| Customize lecture prompt | `teach prompt edit lecture` | N/A | Opens lecture-notes.md | -| Generate with custom prompt | `teach lecture "Topic"` | `/teaching:lecture` | Uses .claude/prompts/lecture.local.md | -| View current prompt | `teach prompt show lecture` | N/A | Displays resolved prompt | -| Generate assignment | `teach assignment "HW1"` | `/teaching:assignment` | Auto-resolved | -| List all prompts | `teach prompt list` | N/A | Shows all available | - -**Key Design Decision:** - -**Content generation commands** (`teach lecture`, `teach exam`, etc.) **automatically use prompts** from 3-tier system. No extra flags needed. - -**Prompt management commands** (`teach prompt *`) are **separate** and manage the prompt library. - -**Benefits:** -- Zero friction for users (prompts "just work") -- Clear mental model (teach X generates, teach prompt manages) -- Backward compatible (existing commands work without prompts) - ---- - -### 4. Template Variable Rendering - -**Challenge:** Both systems need access to config variables for rendering. - -#### Rendering Points - -**Point 1: flow-cli renders prompts** (before Scholar sees them) -- Template: `lib/templates/teaching/claude-prompts/lecture-notes.md` -- Variables: `{{course.name}}`, `{{pedagogy.derivation_depth}}`, etc. -- Output: Fully rendered prompt passed to Scholar - -**Point 2: Scholar renders content** (during generation) -- Template: Scholar's internal Quarto templates -- Variables: `{course.code}`, `{semester.start_date}`, etc. -- Output: Final Quarto file - -**Coordination:** - -```yaml -# teach-config.yml - Shared variables -templates: - # Used by flow-cli prompt rendering - packages_display: "emmeans, lme4, car" - notation_summary: "Using \\mathbb{E} for expectation" - - # Used by Scholar content rendering - course_header: "STAT 440: Regression Analysis" - instructor_info: "Dr. Example | Spring 2025" -``` - -**Implementation:** - -```bash -# flow-cli: Render prompt before Scholar -_render_template() { - local template_file="$1" - local config_file="$2" - - # Use yq + sed/awk for simple variable substitution - local content=$(cat "$template_file") - - # Extract variables from config - local course_name=$(yq '.course.name' "$config_file") - local packages=$(yq '.templates.packages_display' "$config_file") - - # Simple substitution (Phase 1) - content="${content//\{\{course.name\}\}/$course_name}" - content="${content//\{\{templates.packages_display\}\}/$packages}" - - echo "$content" -} -``` - -**Future Enhancement (Phase 3):** -- Full template engine (Handlebars/Mustache syntax) -- Conditionals: `{{#if scholar.teaching_layers.layer_2_intuition}}` -- Loops: `{{#each course.r_packages.core}}` -- Filters: `{{course.name | uppercase}}` - ---- - -### 5. Validation Coordination - -**Challenge:** Who validates what? - -#### Validation Responsibilities - -| System | Validates | When | How | -|--------|-----------|------|-----| -| flow-cli | teach-config.yml schema | `teach init`, `teach doctor` | YAML schema + hash caching | -| flow-cli | Prompt metadata | `teach prompt edit`, `teach prompt enhance` | Frontmatter validation | -| flow-cli | Quarto rendering | `teach deploy` | `quarto render --quiet` | -| Scholar | Content quality | During generation | Internal LLM validation | -| Scholar | teach-config.yml (Scholar sections) | Skill invocation | Schema validation | - -**Shared Validation:** - -**teach-config.yml must be valid for BOTH systems** - -**Solution: Unified Schema** - -```yaml -# .flow/teach-config.schema.yml (new file) -$schema: "http://json-schema.org/draft-07/schema#" -title: "Teaching Configuration Schema" -description: "Unified schema for flow-cli + Scholar integration" - -type: object -required: [course, scholar] - -properties: - # flow-cli sections - course: - type: object - required: [name, code] - properties: - name: {type: string} - code: {type: string} - r_packages: - type: object - properties: - core: {type: array, items: {type: string}} - diagnostics: {type: array, items: {type: string}} - reporting: {type: array, items: {type: string}} - notation: - type: object - properties: - expectation: {type: string} - variance: {type: string} - style: {enum: [macros, inline, mixed]} - pedagogy: - type: object - properties: - derivation_depth: {enum: [heuristic, rigorous-with-intuition, full-rigor]} - practice_problems_count: {type: array, minItems: 2, maxItems: 2} - include_diagnostic_workflow: {type: boolean} - - # Scholar sections - scholar: - type: object - required: [course_info, style] - properties: - course_info: - type: object - properties: - level: {enum: [undergraduate, graduate, phd]} - field: {type: string} - style: - type: object - properties: - tone: {enum: [formal, conversational, technical]} - notation: {enum: [mathematical, statistical, computational]} - - # Shared sections - templates: - type: object - additionalProperties: {type: string} -``` - -**Validation Implementation:** - -```bash -# teach-dispatcher.zsh -_validate_config() { - local config_file="$1" - - # 1. Check YAML syntax - yq eval "$config_file" > /dev/null 2>&1 || { - _flow_log_error "Invalid YAML syntax" - return 1 - } - - # 2. Validate against schema (using yq + custom validation) - _validate_schema "$config_file" ".flow/teach-config.schema.yml" - - # 3. Scholar-specific validation (if Scholar is available) - if command -v scholar-validate &> /dev/null; then - scholar-validate "$config_file" - fi -} -``` - ---- - -### 6. Migration Path - -**Challenge:** Existing courses using Scholar need smooth upgrade path. - -#### Current State (Pre-Enhancement) - -**Existing course using Scholar:** - -``` -my-course/ -├── .flow/ -│ └── teach-config.yml # Has course: + scholar: sections -├── lectures/ -│ └── lecture-01.qmd -└── README.md -``` - -**Current workflow:** -1. `teach lecture "ANOVA"` -2. Scholar reads `.flow/teach-config.yml` -3. Scholar generates lecture -4. User edits output - -#### Target State (Post-Enhancement) - -``` -my-course/ -├── .flow/ -│ ├── teach-config.yml # ENHANCED with prompts: + templates: -│ └── teach-config.schema.yml # Validation schema -├── .claude/ -│ └── prompts/ -│ ├── lecture-notes.local.md # Customized from global -│ └── assignment.local.md # Customized -├── lectures/ -│ └── lecture-01.qmd -└── README.md -``` - -**New workflow:** -1. `teach lecture "ANOVA"` -2. flow-cli resolves prompt (3-tier system) -3. flow-cli renders template variables -4. flow-cli passes rendered prompt to Scholar -5. Scholar generates with enhanced context -6. Better output, less editing needed - -#### Migration Steps - -**Automatic Migration (teach init):** - -```bash -# Detect existing teach-config.yml -teach init - -# Output: -# ✓ Found existing teach-config.yml -# ⚙ Migrating to v5.15.0 format... -# + Added prompts: section -# + Added templates: section -# + Copied global prompts to ~/.flow/prompts/ -# ✓ Migration complete -# -# Next steps: -# 1. Review changes: git diff .flow/teach-config.yml -# 2. Customize prompts: teach prompt edit lecture -# 3. Test generation: teach lecture "Test" -``` - -**Manual Migration (teach doctor):** - -```bash -teach doctor - -# Output: -# Teaching Project Health Check -# ============================= -# -# Dependencies -# ✓ yq (4.30.0) -# ✓ git (2.39.0) -# ✓ quarto (1.3.450) -# ✓ claude (1.0.0) -# -# Configuration -# ⚠ teach-config.yml missing prompts: section -# ⚠ teach-config.yml missing templates: section -# ⚠ Schema file not found -# -# Prompts -# ⚠ Global prompts not initialized -# ⚠ No course-specific customizations -# -# Recommendations: -# 1. Run: teach init --migrate -# 2. Or manually add sections (see docs) -``` - -**Migration Script:** - -```bash -# lib/migration-helpers.zsh (new file) - -_migrate_config_to_v5_15() { - local config_file="$1" - - # Backup original - cp "$config_file" "${config_file}.backup-$(date +%Y%m%d-%H%M%S)" - - # Add prompts: section if missing - if ! yq eval '.prompts' "$config_file" &> /dev/null; then - yq eval '.prompts = {"library": "default", "version": "1.0.0"}' -i "$config_file" - fi - - # Add templates: section if missing - if ! yq eval '.templates' "$config_file" &> /dev/null; then - # Auto-generate from course: section - local packages=$(yq eval '.course.r_packages.core | join(", ")' "$config_file") - yq eval ".templates.packages_display = \"$packages\"" -i "$config_file" - fi - - # Copy schema file - cp "$FLOW_ROOT/lib/templates/teaching/teach-config.schema.yml" \ - "$PWD/.flow/teach-config.schema.yml" - - _flow_log_success "Migration complete" -} -``` - ---- - -## Implementation Roadmap - -### Phase 1: Foundation (1-2 hours) - Quick Wins - -**Goal:** Get basic integration working without breaking existing Scholar workflows. - -**Tasks:** -1. **Add prompts: and templates: sections to teach-config.yml** (20 min) - - Update teach-config.yml.template - - Add default values - - Document new sections - -2. **Create unified schema file** (15 min) - - `.flow/teach-config.schema.yml` - - Validate both flow-cli + Scholar sections - - Add to teach init - -3. **Update teach-dispatcher with prompt resolution** (30 min) - - Add `_resolve_prompt()` function - - Integrate with existing Scholar wrappers - - Backward compatible (works without prompts) - -4. **Add basic template rendering** (20 min) - - Simple `{{variable}}` substitution - - Use yq to extract values - - Pass rendered prompt to Scholar - -5. **Documentation** (15 min) - - Update teach-dispatcher help - - Add examples to README - - Migration guide for existing courses - -**Deliverables:** -- ✅ teach-config.yml with new sections -- ✅ Schema validation -- ✅ Prompt auto-resolution (3-tier) -- ✅ Basic template rendering -- ✅ Backward compatible - -**Testing:** - -```bash -# Existing workflow (should still work) -teach lecture "ANOVA" - -# New workflow (with prompts) -teach prompt list -teach prompt show lecture -teach lecture "ANOVA" # Uses resolved prompt -``` - -### Phase 2: Enhancement (3-5 hours) - Power Features - -**Goal:** Add prompt management commands + advanced rendering. - -**Tasks:** -1. **Implement teach prompt commands** (2 hours) - - `teach prompt list/show/info` - - `teach prompt edit/enhance` - - `teach prompt promote` - -2. **Enhanced template rendering** (1 hour) - - Conditionals: `{{#if ...}}` - - Loops: `{{#each ...}}` - - Filters: `{{... | filter}}` - -3. **Scholar --config + --prompt flag support** (1 hour) - - Update Scholar plugin to accept flags - - Merge prompt with teaching style - - Validate combined context - -4. **Validation coordination** (1 hour) - - `teach validate` command - - Schema validation for both systems - - Auto-fix common issues - -**Deliverables:** -- ✅ Full prompt management -- ✅ Advanced template rendering -- ✅ Scholar flag support -- ✅ Unified validation - -### Phase 3: Advanced Features (8-12 hours) - Future - -**Goal:** Named collections, catalog, versioning. - -**Tasks:** -1. **Named prompt collections** (2 hours) - - `~/.flow/libraries/` - - `teach prompt library create/use` - - Library selection in teach init - -2. **New Scholar-integrated prompts** (6 hours) - - assignment.md - - exam.md - - syllabus.md - - rubric.md - - All with Scholar layer integration - -3. **Prompt catalog system** (2 hours) - - CATALOG.yml - - `teach prompt browse/install` - - Community prompts - -4. **Version management** (2 hours) - - Prompt versioning - - Upgrade notifications - - Migration guides - ---- - -## Technical Decisions Summary - -### ✅ Decided (From Planning Sessions) - -1. **Storage:** 3-tier (Global → User → Course) -2. **Precedence:** Course → User → Global -3. **Config File:** Unified teach-config.yml (both systems) -4. **Ownership:** RFC-001 protocol (isolated sections) -5. **Template Rendering:** flow-cli renders prompts before Scholar -6. **Prompt Passing:** Via `--prompt` flag (temp file) -7. **Validation:** Shared schema, each system validates its sections -8. **Migration:** Auto-migration via `teach init --migrate` -9. **Backward Compatibility:** Existing workflows work without prompts - -### 🤔 Open Questions - -1. **Should Scholar write back to teach-config.yml?** - - Scenario: Scholar generates metadata (e.g., topics covered) - - Option A: Scholar writes to `scholar:` section (two-way sync) - - Option B: Scholar read-only (one-way) - - **Recommendation:** Option A (two-way sync) for metadata only - -2. **How to handle prompt version conflicts?** - - Scenario: Global prompt updated, user has old customized version - - Option A: Auto-upgrade with migration - - Option B: Notify user, manual upgrade - - **Recommendation:** Option B (explicit user control) - -3. **Should teach prompt edit create .claude/ if missing?** - - Option A: Auto-create directory - - Option B: Error + suggest `teach init` - - **Recommendation:** Option A (better UX) - -4. **Template engine choice?** - - Option A: Custom ZSH implementation (lightweight) - - Option B: External tool (Handlebars, Mustache) - - **Recommendation:** Option A for Phase 1, Option B for Phase 3 - -5. **Should Scholar prompts be versioned separately?** - - Option A: Scholar has its own prompt versions - - Option B: flow-cli prompts are the source of truth - - **Recommendation:** Option B (flow-cli manages all prompts) - ---- - -## Coordination Protocol - -### Two-Way Sync Rules - -**flow-cli → Scholar (Always):** -- Reads entire teach-config.yml -- Renders prompts with template variables -- Passes rendered prompt via `--prompt` flag -- Passes config file via `--config` flag - -**Scholar → flow-cli (Optional):** -- Writes metadata to `scholar:` section -- Updates `prompts.lecture.last_modified` -- Notifies flow-cli of changes (if desired) - -**Example Sync Flow:** - -```bash -# 1. User customizes prompt -teach prompt edit lecture -# -> Opens .claude/prompts/lecture-notes.local.md -# -> User adds custom section about diagnostics - -# 2. User generates lecture -teach lecture "ANOVA" -# -> flow-cli resolves: .claude/prompts/lecture-notes.local.md -# -> flow-cli renders: {{course.r_packages.diagnostics}} → "DHARMa, performance" -# -> flow-cli passes to Scholar via --prompt flag -# -> Scholar generates lecture with custom diagnostics section -# -> Scholar writes metadata: -# scholar: -# last_generation: -# command: "lecture" -# topic: "ANOVA" -# timestamp: "2026-01-21T10:30:00Z" -# prompt_used: ".claude/prompts/lecture-notes.local.md" - -# 3. User checks status -teach status -# -> Shows last generation info from scholar: section -``` - ---- - -## Next Steps - -### Immediate (This Session) - -1. **Review this brainstorm** with user -2. **Clarify open questions** (5 items above) -3. **Create formal SPEC** if approved -4. **Update SPEC-teaching-prompts-enhancement** with Scholar coordination - -### Short-term (Next Session) - -1. **Implement Phase 1** (1-2 hours) - - Update teach-config.yml template - - Add schema file - - Implement prompt resolution - - Basic template rendering - -2. **Update Scholar plugin** (coordinate with Scholar development) - - Add `--config` flag support - - Add `--prompt` flag support - - Merge prompt with teaching style - -3. **Write integration tests** - - Test config reading (both systems) - - Test prompt resolution - - Test template rendering - - Test Scholar invocation with flags - -### Long-term (Future Sprints) - -1. **Phase 2 implementation** (3-5 hours) -2. **Phase 3 implementation** (8-12 hours) -3. **Community prompts catalog** -4. **Advanced validation** - ---- - -## Benefits Summary - -### For Users - -**Before (Current State):** -- Manually copy-paste Claude prompts -- Maintain config in teach-config.yml -- Scholar generates content -- Limited customization options - -**After (Enhanced State):** -- Zero-friction prompt usage (auto-detected) -- Single unified config file -- Customizable prompts (course-specific) -- Scholar integration (enhanced context) -- Shareable prompt libraries -- Version-controlled prompts -- Consistent content generation - -### For Developers - -**Before:** -- Two separate systems (flow-cli + Scholar) -- Manual coordination needed -- Risk of config drift - -**After:** -- Unified architecture -- Clear ownership protocol (RFC-001) -- Shared schema validation -- Extensible prompt system -- Clean separation of concerns - ---- - -## Risk Mitigation - -### Risk 1: Breaking Existing Workflows - -**Mitigation:** -- Backward compatibility (existing commands work without prompts) -- Graceful degradation (missing sections → defaults) -- Auto-migration (`teach init --migrate`) -- Comprehensive testing - -### Risk 2: Config File Conflicts - -**Mitigation:** -- Clear ownership protocol (RFC-001) -- Isolated sections (flow-cli vs Scholar) -- Schema validation prevents overwrites -- Git version control - -### Risk 3: Scholar Integration Delays - -**Mitigation:** -- Phase 1 works without Scholar changes (basic rendering) -- Phase 2 requires Scholar v2.2.0 (coordinate release) -- Fallback to Scholar-oblivious mode if needed - -### Risk 4: Complexity Creep - -**Mitigation:** -- Start simple (Phase 1: basic features) -- Add advanced features incrementally (Phase 2, 3) -- User testing at each phase -- Clear documentation - ---- - -## Success Metrics - -### Phase 1 Success - -- [ ] Existing `teach lecture` command works unchanged -- [ ] New `teach prompt list` shows available prompts -- [ ] Config file validated by both systems -- [ ] Template variables render correctly -- [ ] Zero user-reported regressions - -### Phase 2 Success - -- [ ] Users can customize prompts easily -- [ ] Scholar receives enhanced context -- [ ] Generated content quality improves -- [ ] Prompt management commands intuitive -- [ ] < 5 min to customize first prompt - -### Phase 3 Success - -- [ ] Named collections used by power users -- [ ] Community prompts catalog active -- [ ] Prompt upgrades seamless -- [ ] Multiple courses share prompts via ~/.flow/ - ---- - -## Appendix: Example Workflows - -### Workflow 1: New Course Setup - -```bash -# 1. Initialize teaching project -teach init -# -> Creates .flow/teach-config.yml with all sections -# -> Creates .flow/teach-config.schema.yml -# -> Initializes ~/.flow/prompts/ if needed -# -> Prompts for Scholar integration (yes/no) - -# 2. Customize config -$EDITOR .flow/teach-config.yml -# -> Add course name, packages, notation, pedagogy -# -> Add Scholar style preferences - -# 3. Generate first lecture (uses default prompts) -teach lecture "Introduction to Regression" -# -> flow-cli resolves: lib/templates/.../lecture-notes.md -# -> flow-cli renders variables -# -> Scholar generates with config + prompt - -# 4. Customize lecture prompt for this course -teach prompt edit lecture -# -> Copies global → .claude/prompts/lecture-notes.local.md -# -> Opens in $EDITOR -# -> User adds course-specific sections - -# 5. Generate next lecture (uses customized prompt) -teach lecture "Simple Linear Regression" -# -> flow-cli resolves: .claude/prompts/lecture-notes.local.md -# -> Uses course-specific customizations -``` - -### Workflow 2: Multi-Course Instructor - -```bash -# 1. Create named collection for undergraduate courses -teach prompt library create undergrad-stats -# -> Creates ~/.flow/libraries/undergrad-stats/ -# -> Copies default prompts - -# 2. Customize prompts in collection -cd ~/.flow/libraries/undergrad-stats/ -$EDITOR lecture-notes.md -# -> Add undergrad-friendly language -# -> Reduce rigor, increase intuition - -# 3. Use collection in Course A -cd ~/teaching/stat-226/ -teach init --library undergrad-stats -# -> Uses prompts from collection - -# 4. Use collection in Course B -cd ~/teaching/stat-301/ -teach init --library undergrad-stats -# -> Same prompts, different config - -# 5. Update collection (affects both courses) -cd ~/.flow/libraries/undergrad-stats/ -$EDITOR lecture-notes.md -# -> Make improvements -teach lecture "Topic" # Both courses get updates -``` - -### Workflow 3: Graduate Course with Advanced Prompts - -```bash -# 1. Setup graduate course -teach init -$EDITOR .flow/teach-config.yml -# -> Set scholar.course_info.level: "graduate" -# -> Set pedagogy.derivation_depth: "full-rigor" - -# 2. Customize lecture prompt for grad level -teach prompt edit lecture -# -> Opens .claude/prompts/lecture-notes.local.md -# -> Add sections: -# - Advanced theoretical foundations -# - Proof sketches -# - Research connections -# - Graduate-level references - -# 3. Generate lecture -teach lecture "Advanced Mixed Models" -# -> flow-cli renders: -# {{pedagogy.derivation_depth}} → "full-rigor" -# {{scholar.course_info.level}} → "graduate" -# -> Scholar uses graduate teaching layers -# -> Output includes proofs, advanced theory -``` - ---- - -**End of Brainstorm** - -**Next Actions:** -1. User reviews brainstorm -2. Clarify 5 open questions -3. Approve coordination strategy -4. Proceed to implementation (Phase 1) diff --git a/docs/specs/BRAINSTORM-teach-config-consolidation-2026-01-19.md b/docs/specs/BRAINSTORM-teach-config-consolidation-2026-01-19.md deleted file mode 100644 index f1d4642ca..000000000 --- a/docs/specs/BRAINSTORM-teach-config-consolidation-2026-01-19.md +++ /dev/null @@ -1,745 +0,0 @@ -# Brainstorm: Teaching Workflow Config File Strategy - -**Created:** 2026-01-19 -**Context:** Dashboard feature adding more config → need consolidation strategy -**Related:** SPEC-teach-dashboard-2026-01-18.md -**Status:** Brainstorm → Will inform v5.15.0+ config architecture - ---- - -## Current State: Config File Inventory - -### Existing Config Files (v5.14.0) - -| File | Owner | Type | Purpose | Size | Created By | -|------|-------|------|---------|------|------------| -| `.flow/teach-config.yml` | Instructor | Source | Master course config | ~200 lines | `teach init` | -| `.flow/lesson-plans/week-N.yml` | Instructor | Source | Per-week Scholar context | ~50 lines each | Manual | -| `lesson-plan.yml` | Instructor | Source | Global Scholar context (legacy) | ~100 lines | Manual | -| `.flow/semester-data.json` | teach dashboard | Generated | Dashboard data export | ~5 KB | `teach dashboard generate` ★ NEW | -| `.flow/.validation-cache.json` | teach validator | Cache | Config validation cache | ~1 KB | `teach` commands | -| `lib/templates/teaching/teach-config.schema.json` | Developer | Schema | JSON Schema validation | ~300 lines | Repository | - -### Related Quarto Files - -| File | Purpose | Updated By | -|------|---------|------------| -| `_quarto.yml` | Site configuration | Manual | -| `index.qmd` | Homepage (dashboard container) | Manual | -| Various `*.qmd` | Content files | Manual + Scholar | - ---- - -## Problem Analysis - -### Issue 1: Fragmentation - -**Current situation:** -- 3+ YAML files for course info -- Unclear precedence -- Duplication risk - -**Example confusion:** - -``` -lesson-plan.yml: # Global context - course: "STAT 545" - -.flow/teach-config.yml: # Master config - course: - name: "STAT 545" - -.flow/lesson-plans/week-5.yml: # Per-week - course: "STAT 545" -``` - -**Which is authoritative?** - -### Issue 2: Scholar Context Split - -**Current:** -- Global: `lesson-plan.yml` (root) -- Per-week: `.flow/lesson-plans/week-N.yml` -- Config: `.flow/teach-config.yml` - -**Scholar reads from:** -1. `lesson-plan.yml` first (if exists) -2. `.flow/lesson-plans/week-N.yml` (if --week specified) -3. `.flow/teach-config.yml` (for course metadata) - -**Problem:** Multiple sources, merge logic unclear - -### Issue 3: Dashboard Adds More Data - -**v5.15.0 adds:** - -```yaml -semester_info: - weeks: - - lecture: - title: "..." - url: "..." - lab: {...} - assignment: {...} - -dashboard: - show_labs: true - announcements: [...] -``` - -**teach-config.yml grows to ~300+ lines** for comprehensive courses - ---- - -## Design Principles for Config Architecture - -### 1. Single Source of Truth (SSOT) - -**Principle:** Each piece of data exists in exactly one authoritative location - -**Applied:** -- Course metadata → `teach-config.yml` -- Semester dates → `teach-config.yml` -- Week topics → `teach-config.yml` -- Scholar context → **TBD** (see options below) - -### 2. Separation of Concerns - -**Principle:** Different types of data in different files - -**Categories:** -1. **Structural data** - Course structure, schedule (teach-config.yml) -2. **Content context** - Pedagogical context for generation (Scholar files) -3. **Generated data** - Derived outputs (JSON, cache) -4. **Site configuration** - Quarto settings (_quarto.yml) - -### 3. Minimal Duplication - -**Principle:** Reference, don't duplicate - -**Pattern:** - -```yaml -# Good: Reference -weeks: - - number: 5 - topic: "Factorial Designs" - lecture_url: "lectures/week-05.qmd" - -# Bad: Duplicate -weeks: - - number: 5 - topic: "Factorial Designs" # In teach-config.yml - -lectures/week-05.qmd: - title: "Factorial Designs" # Same info! -``` - -### 4. Human-Readable & Editable - -**Principle:** Instructors should edit config comfortably - -**Requirements:** -- YAML (not JSON) for source files -- Comments and examples -- Clear structure -- Validation feedback - -### 5. Generate, Don't Duplicate - -**Principle:** Derived data should be generated, not manually maintained - -**Examples:** -- semester-data.json ← generate from teach-config.yml ✅ -- Week URLs ← could generate from conventions ✅ -- Assignment due dates ← calculate from week + offset ✅ - ---- - -## Integration Options - -### Option A: Monolithic teach-config.yml (Current Direction) - -**Strategy:** Everything in `.flow/teach-config.yml` - -```yaml -# .flow/teach-config.yml (400+ lines) - -course: - name: "STAT 545" - semester: "Spring 2026" - # ... course metadata - -semester_info: - start_date: "2026-01-19" - weeks: - - number: 1 - topic: "..." - lecture: {...} - lab: {...} - # ... schedule data - -dashboard: - show_labs: true - # ... dashboard config - -scholar_context: # NEW: Merge lesson-plan.yml here - learning_objectives: [...] - pedagogical_approach: "..." - course_philosophy: "..." - assessment_strategy: {...} - - # Per-week context - weeks: - 1: - focus: "..." - prerequisites: [...] - learning_goals: [...] -``` - -**Pros:** -- ✅ True SSOT -- ✅ One file to backup -- ✅ Easy to version control -- ✅ Clear precedence - -**Cons:** -- ❌ Large file (400+ lines) -- ❌ Hard to navigate -- ❌ Merge conflicts likely -- ❌ Mixing structure + content - -**Verdict:** 🟡 Works but unwieldy for large courses - ---- - -### Option B: Hierarchical Config Files (Recommended) - -**Strategy:** Split by concern, clear hierarchy - -``` -.flow/ -├── teach-config.yml # Course structure (200 lines) -│ ├── course metadata -│ ├── semester_info (dates, weeks, breaks) -│ └── dashboard config -│ -├── scholar-context.yml # Scholar context (150 lines) -│ ├── Global context -│ ├── learning_objectives -│ ├── pedagogical_approach -│ └── assessment_strategy -│ -├── lesson-plans/ -│ ├── week-01.yml # Per-week context (50 lines each) -│ ├── week-02.yml -│ └── ... -│ -└── semester-data.json # Generated (5 KB) -``` - -**teach-config.yml** - Structure only: - -```yaml -course: - name: "STAT 545" - semester: "Spring 2026" - -semester_info: - start_date: "2026-01-19" - weeks: - - number: 1 - topic: "Experimental Design" - lecture_url: "lectures/week-01.qmd" - -dashboard: - show_labs: true - -# Reference to Scholar context -scholar_context_file: ".flow/scholar-context.yml" -``` - -**scholar-context.yml** - Pedagogy only: - -```yaml -# Global pedagogical context for Scholar - -course_philosophy: | - This course emphasizes hands-on experimental design... - -learning_objectives: - - "Design randomized experiments" - - "Analyze factorial designs" - -pedagogical_approach: "active learning" - -assessment_strategy: - formative: ["quizzes", "labs"] - summative: ["exams", "project"] - -common_misconceptions: - - "Students often confuse..." -``` - -**lesson-plans/week-01.yml** - Week-specific context: - -```yaml -# Week 1 specific context - -focus: "Introduction to experimental design principles" - -prerequisites: - - "Basic statistics" - - "R programming" - -learning_goals: - - "Understand randomization" - - "Apply blocking techniques" - -key_concepts: - - "Experimental units" - - "Treatment factors" - -examples: - - "Agricultural field trials" - - "Clinical trials" -``` - -**Pros:** -- ✅ Manageable file sizes -- ✅ Clear separation of concerns -- ✅ Easy to navigate -- ✅ Fewer merge conflicts -- ✅ Can edit context without touching structure - -**Cons:** -- ⚠️ Multiple files to manage -- ⚠️ Need merge logic for Scholar -- ⚠️ Slightly more complex - -**Verdict:** 🟢 Best balance for large courses - ---- - -### Option C: Database-Style (Overkill) - -**Strategy:** SQLite database for all config - -**Verdict:** 🔴 Overengineered, loses human-readability - ---- - -## Recommended Integration Strategy - -### Phase 1: v5.15.0 (Dashboard Feature) - -**Keep current structure, add dashboard section** - -```yaml -# .flow/teach-config.yml - -# Existing sections unchanged -course: {...} -semester_info: {...} - -# NEW: Add dashboard section -dashboard: - show_labs: true - show_assignments: true - show_readings: false - card_style: "detailed" - hero_style: "banner" - enable_announcements: true - max_announcements: 5 - fallback_message: "Check Syllabus..." - announcements: [] -``` - -**Impact:** Adds ~30 lines to teach-config.yml - -**Rationale:** -- Minimal disruption -- Dashboard config is structural (like semester_info) -- Not urgent to refactor yet - ---- - -### Phase 2: v5.16.0 (Scholar Context Consolidation) - -**Consolidate Scholar context files** - -**Migration path:** - -```bash -teach scholar migrate -# Combines lesson-plan.yml + per-week files into scholar-context.yml -``` - -**New structure:** - -``` -.flow/ -├── teach-config.yml # Course structure -├── scholar-context.yml # NEW: Consolidated Scholar context -└── semester-data.json # Generated -``` - -**Deprecate:** -- `lesson-plan.yml` (root) - Move to `.flow/scholar-context.yml` -- `.flow/lesson-plans/week-N.yml` - Merge into scholar-context.yml - ---- - -### Phase 3: v5.17.0+ (Per-Week Modularization) - -**For very large courses, allow per-week override** - -``` -.flow/ -├── teach-config.yml -├── scholar-context.yml # Global defaults -└── scholar-overrides/ - ├── week-01.yml # Optional overrides - └── week-12.yml -``` - -**Merge logic:** -1. Load global `scholar-context.yml` -2. If `scholar-overrides/week-N.yml` exists, deep merge -3. Pass combined context to Scholar - ---- - -## File Precedence Rules - -### Current (v5.14.0) - -``` -Scholar context loading order: -1. lesson-plan.yml (root) - if exists -2. .flow/lesson-plans/week-N.yml - if --week specified -3. .flow/teach-config.yml - course metadata -``` - -**Problem:** Unclear merge behavior - ---- - -### Proposed (v5.16.0+) - -``` -Configuration precedence (highest to lowest): -1. CLI flags (--topic "...") -2. Per-week override (.flow/scholar-overrides/week-N.yml) -3. Global Scholar context (.flow/scholar-context.yml) -4. Course structure (.flow/teach-config.yml) -5. Defaults (built-in) -``` - -**Merge strategy:** -- Objects: Deep merge (lodash-style) -- Arrays: Override (not merge) -- Primitives: Override - -**Example:** - -```yaml -# scholar-context.yml -learning_objectives: - - "Objective A" - - "Objective B" - -# scholar-overrides/week-05.yml -learning_objectives: - - "Week 5 specific objective" # Replaces global - -# Result for week 5: -learning_objectives: - - "Week 5 specific objective" -``` - ---- - -## Config Validation Strategy - -### Schema Hierarchy - -``` -teach-config.schema.json # Master schema -├── course.schema.json # Course metadata -├── semester_info.schema.json # Semester schedule -└── dashboard.schema.json # Dashboard config (NEW) - -scholar-context.schema.json # Scholar context (v5.16.0) -``` - -### Validation Workflow - -```zsh -teach validate -# Validates all config files against schemas -# Caches results in .flow/.validation-cache.json - -teach validate --fix -# Auto-fix common issues (formatting, types) - -teach validate --strict -# Enforce all optional fields -``` - ---- - -## Migration Paths - -### v5.14.0 → v5.15.0 (Dashboard) - -**Manual migration:** - -```yaml -# Add to existing .flow/teach-config.yml - -dashboard: - show_labs: true - show_assignments: true - enable_announcements: true - fallback_message: "Check Syllabus..." -``` - -**Auto-migration:** Not needed, dashboard section is optional - ---- - -### v5.15.0 → v5.16.0 (Scholar Consolidation) - -**Auto-migration command:** - -```bash -teach scholar migrate - -📦 Migrating Scholar Context -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - -Found: - • lesson-plan.yml (root) - • .flow/lesson-plans/week-01.yml - • .flow/lesson-plans/week-02.yml - ... (12 more) - -Creating: .flow/scholar-context.yml - ✓ Merged global context - ✓ Merged 14 week-specific contexts - ✓ Validated schema - -Backup created: .flow/.scholar-context-backup-2026-01-19.tar.gz - -Next steps: - 1. Review: .flow/scholar-context.yml - 2. Test: teach lecture "Test" --week 1 - 3. Delete old files: teach scholar migrate --cleanup -``` - ---- - -## Best Practices - -### 1. Always Use teach-config.yml as SSOT - -```yaml -# ✅ Good: Reference teach-config.yml -semester_info: - weeks: - - number: 5 - topic: "Factorial Designs" - -# ❌ Bad: Duplicate in multiple files -``` - -### 2. Keep Generated Files in .flow/ - -``` -.flow/ -├── teach-config.yml # Source -├── scholar-context.yml # Source -├── semester-data.json # Generated ← here -└── .validation-cache.json # Generated ← here -``` - -**Rationale:** Clear separation, easy to .gitignore - -### 3. Version Control Strategy - -```gitignore -# .gitignore - -# Never commit generated files -.flow/semester-data.json -.flow/.validation-cache.json - -# Always commit source config -!.flow/teach-config.yml -!.flow/scholar-context.yml -``` - -### 4. Backup Before Migration - -```bash -# Automatic backup on migration -teach scholar migrate -# Creates: .flow/.scholar-context-backup-YYYY-MM-DD.tar.gz - -# Manual backup -teach backup create --scope config -``` - -### 5. Use Validation Hooks - -```zsh -# Git pre-commit hook -.git/hooks/pre-commit: -#!/bin/bash -teach validate || { - echo "❌ Config validation failed" - exit 1 -} -``` - ---- - -## Performance Implications - -### Current (v5.14.0) - -``` -teach lecture "Topic" --week 5 -│ -├─ Load lesson-plan.yml (10ms) -├─ Load .flow/lesson-plans/week-05.yml (10ms) -├─ Load .flow/teach-config.yml (15ms) -└─ Merge contexts (5ms) - Total: ~40ms -``` - -### Proposed (v5.16.0) - -``` -teach lecture "Topic" --week 5 -│ -├─ Load .flow/teach-config.yml (15ms) -├─ Load .flow/scholar-context.yml (20ms) -├─ Check .flow/scholar-overrides/week-05.yml (5ms, not found) -└─ Merge contexts (5ms) - Total: ~45ms -``` - -**Impact:** Negligible (~5ms slower) - ---- - -## Open Questions - -### 1. Should dashboard announcements live in YAML or only JSON? - -**Option A:** YAML (source), sync to JSON - -```yaml -dashboard: - announcements: - - title: "Welcome" - expires: "2026-01-26" -``` - -**Option B:** JSON only (generated) - -```bash -teach dashboard announce "Welcome" --expires 2026-01-26 -# Only updates .flow/semester-data.json -``` - -**Recommendation:** Option B (JSON only) for simplicity -- CLI announcements are ephemeral -- YAML would need sync logic -- Easier to manage programmatically - ---- - -### 2. Where should per-week URLs be stored? - -**Option A:** In teach-config.yml (structural) - -```yaml -weeks: - - number: 5 - lecture_url: "lectures/week-05.qmd" -``` - -**Option B:** Generated from conventions - -```bash -# Convention: lectures/week-{NN}_{topic-slug}.qmd -teach dashboard generate --infer-urls -``` - -**Recommendation:** Option A (explicit) for v5.15.0, Option B (inferred) for v5.16.0+ - ---- - -### 3. Should Scholar auto-update teach-config.yml? - -**Current:** Manual (Scholar creates file, user adds URL to config) - -**Proposed:** Auto-update - -```bash -teach lecture "Topic" --week 5 -# Creates: lectures/week-05_topic.qmd -# Updates: teach-config.yml weeks[5].lecture_url -# Regenerates: semester-data.json -``` - -**Recommendation:** v5.16.0+ feature (not v5.15.0) - ---- - -## Summary & Recommendations - -### For v5.15.0 (Dashboard Feature) - -| Decision | Recommendation | -|----------|----------------| -| **Config location** | Add `dashboard:` section to teach-config.yml | -| **File count** | Keep current file structure (no consolidation yet) | -| **Dashboard data** | Generated JSON only (no YAML source for announcements) | -| **Scholar integration** | Passive (no auto-update) | - -**Impact:** +30 lines to teach-config.yml, 1 new generated file - ---- - -### For v5.16.0+ (Future Consolidation) - -| Area | Strategy | -|------|----------| -| **Scholar context** | Consolidate to `.flow/scholar-context.yml` | -| **Per-week overrides** | Optional `.flow/scholar-overrides/week-N.yml` | -| **Migration** | Auto-migration command: `teach scholar migrate` | -| **Validation** | Separate schemas, unified validation command | - -**Benefits:** -- Clearer file organization -- Easier to navigate -- Better separation of concerns -- Reduced duplication - ---- - -### Guiding Principles - -1. **Start simple** - Don't over-engineer for v5.15.0 -2. **Iterate thoughtfully** - Consolidate in v5.16.0 when patterns are clear -3. **Respect user edits** - Never lose manually-edited content -4. **Generate, don't duplicate** - Prefer derived data -5. **Validate early** - Catch config errors before generation - ---- - -**End of Brainstorm** diff --git a/docs/specs/BRAINSTORM-teach-dashboard-integration-2026-01-19.md b/docs/specs/BRAINSTORM-teach-dashboard-integration-2026-01-19.md deleted file mode 100644 index 718f4765e..000000000 --- a/docs/specs/BRAINSTORM-teach-dashboard-integration-2026-01-19.md +++ /dev/null @@ -1,1093 +0,0 @@ -# Brainstorm: teach dashboard Integration & Architecture - -**Created:** 2026-01-19 -**Context:** Feature spec approved, exploring integration with existing teach workflow -**Related:** SPEC-teach-dashboard-2026-01-18.md -**Status:** Brainstorm → Will inform implementation - ---- - -## Overview - -Deep dive into how `teach dashboard` integrates with the existing teach dispatcher ecosystem, including teach dates, teach deploy, Scholar integration, and the broader teaching workflow. - ---- - -## Architecture Layers - -```mermaid -graph TB - subgraph "User Layer" - USER[Instructor] - end - - subgraph "Command Layer" - TEACH[teach dispatcher] - DATES[teach dates] - DASH[teach dashboard] - DEPLOY[teach deploy] - SCHOLAR[teach scholar/*] - end - - subgraph "Data Layer" - CONFIG[teach-config.yml] - JSON[semester-data.json] - LESSON[lesson-plan.yml] - end - - subgraph "Output Layer" - QMD[*.qmd files] - SITE[_site/] - REMOTE[GitHub Pages] - end - - USER -->|1. Define semester| CONFIG - USER -->|2. teach dates sync| DATES - DATES -->|auto-trigger| DASH - DASH -->|generate| JSON - USER -->|3. teach scholar lecture| SCHOLAR - SCHOLAR -->|create| QMD - SCHOLAR -->|load context| LESSON - USER -->|4. teach deploy| DEPLOY - DEPLOY -->|build| SITE - SITE -->|contains| JSON - DEPLOY -->|push| REMOTE - - style DASH fill:#ffd700 - style JSON fill:#98fb98 -``` - ---- - -## Integration Points - -### 1. teach dates Integration (Critical) - -**Decision:** Dashboard auto-generates when dates sync - -#### Implementation Options - -**Option A: Direct Hook in teach-dates.zsh (Recommended)** - -```zsh -# In _teach_dates_sync() after successful date updates -_teach_dates_sync() { - # ... existing date sync logic ... - - if [[ $changes_made == true ]]; then - _flow_log_success "Dates synced successfully" - - # Auto-generate dashboard if config exists - if [[ -f ".flow/teach-config.yml" ]] && _teach_has_dashboard_config; then - _flow_log_info "Regenerating dashboard..." - _teach_dashboard_generate --quiet || { - _flow_log_warn "Dashboard generation failed (non-fatal)" - } - fi - fi -} -``` - -**Pros:** -- Automatic, user doesn't think about it -- Always in sync -- Simple implementation - -**Cons:** -- Adds ~500ms to dates sync -- Hidden behavior (could surprise users) - -**Option B: Explicit Flag** - -```zsh -teach dates sync --regenerate-dashboard -``` - -**Pros:** -- Explicit control -- User knows what's happening - -**Cons:** -- Cognitive overhead -- Easy to forget - -**Recommendation:** Option A with informative logging - ---- - -### 2. teach deploy Integration - -**Question:** Should deploy verify dashboard is current? - -#### Implementation Options - -**Option A: Pre-deploy Check (Recommended)** - -```zsh -_teach_deploy() { - # ... existing checks ... - - # Check if dashboard is stale - if _teach_dashboard_is_stale; then - _flow_log_warn "Dashboard may be out of sync with config" - _flow_log_info "Run: teach dashboard generate" - - if _flow_confirm "Regenerate dashboard before deploying?"; then - _teach_dashboard_generate || return 1 - fi - fi - - # ... proceed with deploy ... -} - -_teach_dashboard_is_stale() { - local config_mtime=$(stat -f %m .flow/teach-config.yml 2>/dev/null) - local json_mtime=$(stat -f %m .flow/semester-data.json 2>/dev/null) - - [[ -z "$json_mtime" ]] && return 0 # JSON missing - [[ $config_mtime -gt $json_mtime ]] && return 0 # Config newer - return 1 # Dashboard current -} -``` - -**Pros:** -- Prevents deploying stale data -- Safety net for missed regeneration -- User can skip if intentional - -**Cons:** -- Adds step to deploy -- Might be annoying if intentionally not using dashboard - -**Option B: No Check, Trust Workflow** - -**Pros:** -- Faster deploys -- Simpler code - -**Cons:** -- Risk of stale dashboard on production - -**Recommendation:** Option A with skip option (--skip-dashboard-check) - ---- - -### 3. Scholar Integration - -**Question:** Should Scholar commands be aware of dashboard? - -#### Scenario: Creating Content - -```bash -# User generates lecture -teach exam "Factorial Designs" --week 5 - -# Scholar creates: -# - lectures/week-05_factorial-designs.qmd -# - Uses lesson-plan.yml context -# - Updates teach-config.yml weeks[5].lecture.url? -``` - -#### Option A: Scholar Passive (Recommended for MVP) - -Scholar creates content, dashboard reads config separately. - -**Pros:** -- Loose coupling -- Scholar doesn't need dashboard knowledge -- Works with manual config updates - -**Cons:** -- User must manually update teach-config.yml with URLs -- Two-step process - -#### Option B: Scholar Active (Future Enhancement) - -Scholar updates teach-config.yml when creating content. - -```zsh -_teach_scholar_lecture() { - # Generate lecture content - local output_file=$(scholar_generate_lecture "$topic") - - # Update config with file location - if [[ -n "$week" ]]; then - yq -i ".semester_info.weeks[$((week-1))].lecture.url = \"$output_file\"" \ - .flow/teach-config.yml - - # Trigger dashboard regeneration - _teach_dashboard_generate --quiet - fi -} -``` - -**Pros:** -- Fully automated workflow -- Single command updates everything -- Less chance of config drift - -**Cons:** -- Tight coupling -- Complex error handling -- What if Scholar fails? - -**Recommendation:** Start with Option A, add Option B in v5.16.0 - ---- - -### 4. Quarto Integration - -**Question:** How does Quarto copy JSON to _site/? - -#### Option A: Quarto Resource (Recommended) - -Add to `_quarto.yml`: - -```yaml -project: - resources: - - .flow/semester-data.json -``` - -**Quarto behavior:** -- Copies file to `_site/.flow/semester-data.json` on render -- Preserves directory structure -- No custom script needed - -**Client JS:** - -```javascript -fetch('.flow/semester-data.json') - .then(res => res.json()) - .then(data => updateDashboard(data)); -``` - -**Pros:** -- Standard Quarto behavior -- No custom build step -- Works with quarto preview - -**Cons:** -- `.flow/` in URL might be non-standard -- Possible path confusion - -#### Option B: Custom Copy Script - -```yaml -# _quarto.yml -project: - pre-render: scripts/copy-dashboard-data.sh -``` - -```bash -# scripts/copy-dashboard-data.sh -#!/bin/bash -cp .flow/semester-data.json _site/data/semester-data.json -``` - -**Pros:** -- Control over destination path -- Can add processing/validation -- Clear intent - -**Cons:** -- Extra script to maintain -- Runs every render (slower) - -**Recommendation:** Option A for simplicity, document path in spec - ---- - -## Data Flow Architecture - -### Complete Workflow Sequence - -``` -┌─────────────────────────────────────────────────────────────┐ -│ 1. SEMESTER SETUP │ -├─────────────────────────────────────────────────────────────┤ -│ teach init "STAT 545" │ -│ └─> Creates .flow/teach-config.yml │ -│ └─> Creates .flow/lesson-plan.yml │ -│ │ -│ [Manual: Edit teach-config.yml with weeks, topics, dates] │ -└─────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ 2. DATE SYNCHRONIZATION │ -├─────────────────────────────────────────────────────────────┤ -│ teach dates sync │ -│ ├─> Scans *.qmd files for date references │ -│ ├─> Updates dates in frontmatter + content │ -│ ├─> [HOOK] Auto-triggers teach dashboard generate │ -│ └─> Creates/updates .flow/semester-data.json │ -└─────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ 3. CONTENT GENERATION (Scholar) │ -├─────────────────────────────────────────────────────────────┤ -│ teach lecture "Factorial Designs" --week 5 │ -│ ├─> Loads lesson-plan.yml context │ -│ ├─> Generates lectures/week-05_factorial.qmd │ -│ └─> [Future] Updates teach-config.yml with URL │ -│ │ -│ teach exam "Midterm 1" --weeks 1-5 │ -│ └─> Generates exams/midterm1.qmd │ -└─────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ 4. ANNOUNCEMENTS (Ad-hoc) │ -├─────────────────────────────────────────────────────────────┤ -│ teach dashboard announce "Exam Next Week" \ │ -│ --expires 2026-03-05 --type warning │ -│ └─> Updates .flow/semester-data.json │ -└─────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ 5. PREVIEW & VALIDATION │ -├─────────────────────────────────────────────────────────────┤ -│ teach dashboard preview --week 5 │ -│ ├─> Reads .flow/semester-data.json │ -│ ├─> Calculates what students will see │ -│ └─> ASCII mockup of dashboard │ -│ │ -│ teach status │ -│ └─> Shows dashboard status (last generated, issues) │ -└─────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ 6. DEPLOYMENT │ -├─────────────────────────────────────────────────────────────┤ -│ teach deploy │ -│ ├─> [Pre-deploy check] Dashboard current? │ -│ ├─> Quarto render (copies JSON to _site/) │ -│ ├─> Git commit + push │ -│ └─> Creates PR to production │ -└─────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ 7. CLIENT-SIDE RENDERING │ -├─────────────────────────────────────────────────────────────┤ -│ Student visits homepage │ -│ ├─> Page loads index.html │ -│ ├─> stat545.js fetches .flow/semester-data.json │ -│ ├─> Calculates current week (timezone-aware) │ -│ ├─> Filters active announcements │ -│ ├─> Updates DOM with current week content │ -│ └─> Displays hero banner, cards, next up widget │ -└─────────────────────────────────────────────────────────────┘ -``` - ---- - -## File Structure & Organization - -### Project Tree - -``` -course-root/ -├── .flow/ -│ ├── teach-config.yml # Source of truth -│ ├── semester-data.json # Generated dashboard data ★ NEW -│ ├── lesson-plan.yml # Scholar context -│ └── .validation-cache.json # Config validation cache -│ -├── lectures/ -│ ├── week-01_intro.qmd -│ └── week-05_factorial.qmd -│ -├── assignments/ -│ ├── assignment1.qmd -│ └── assignment5.qmd -│ -├── index.qmd # Homepage (dashboard container) -├── _quarto.yml # Project config -│ -├── _site/ # Generated output -│ ├── .flow/ -│ │ └── semester-data.json # Copied by Quarto -│ ├── index.html -│ └── lectures/ -│ -└── js/ - └── stat545.js # Dashboard renderer ★ NEW -``` - -### File Ownership - -| File | Owner | Generated By | Modified By | -|------|-------|--------------|-------------| -| teach-config.yml | Instructor | teach init | Manual editing | -| semester-data.json | teach dashboard | teach dashboard generate | teach dashboard announce | -| lesson-plan.yml | Instructor | teach init | Manual editing | -| *.qmd | Instructor/Scholar | teach scholar/* | Manual editing | -| stat545.js | Developer | Manual | Manual | - ---- - -## teach-dispatcher.zsh Integration - -### Command Routing - -```zsh -teach() { - case "$1" in - # Existing commands - init|doctor|status|deploy) - shift; _teach_"$1" "$@" ;; - - dates) - shift; _teach_dates_dispatcher "$@" ;; - - # Scholar wrappers - lecture|exam|quiz|assignment|syllabus|rubric|feedback|slides|notes) - _teach_scholar_wrapper "$@" ;; - - # NEW: Dashboard subcommands - dashboard) - shift; _teach_dashboard_dispatcher "$@" ;; - - *) - _teach_help ;; - esac -} -``` - -### Dispatcher Implementation Location - -**Option A: Inline in teach-dispatcher.zsh** -- Simple -- All teach code in one file -- File already 3161 lines (getting large) - -**Option B: Separate file (Recommended)** - -``` -lib/dispatchers/teach-dashboard-impl.zsh -``` - -Load in teach-dispatcher.zsh: - -```zsh -if [[ -z "$_FLOW_TEACH_DASHBOARD_LOADED" ]]; then - local dashboard_path="${0:A:h}/teach-dashboard-impl.zsh" - [[ -f "$dashboard_path" ]] && source "$dashboard_path" - typeset -g _FLOW_TEACH_DASHBOARD_LOADED=1 -fi -``` - -**Recommendation:** Option B, follows existing pattern (teach-doctor-impl.zsh) - ---- - -## Configuration Schema Extensions - -### Current Schema (v5.14.0) - -```yaml -semester_info: - start_date: "2026-01-19" - end_date: "2026-05-16" - weeks: - - number: 1 - topic: "Introduction" - breaks: - - name: "Spring Break" - start: "2026-03-15" - end: "2026-03-22" -``` - -### Extended Schema (v5.15.0) - -```yaml -semester_info: - start_date: "2026-01-19" - end_date: "2026-05-16" - timezone: "America/Denver" # NEW (optional, default: system) - - weeks: - - number: 1 - topic: "Introduction to Experimental Design" - focus: "Randomization, replication, blocking" # NEW (optional) - - lecture: # NEW (optional) - title: "Design Principles" - url: "lectures/week-01_intro.qmd" - - lab: # NEW (optional) - title: "Getting Started with R" - url: "r_help.qmd" - - assignment: # NEW (optional) - title: "Assignment 1" - url: "assignments/assignment1.qmd" - due: "2026-01-29" - - reading: # NEW (optional, for seminar courses) - title: "Design of Experiments" - author: "Montgomery" - pages: "1-25" - url: "readings/week01.qmd" - - breaks: - - name: "Spring Break" - start: "2026-03-15" - end: "2026-03-22" - show_next: true # NEW (optional, default: true) - -# NEW section -dashboard: - # Structure options - show_labs: true - show_assignments: true - show_readings: false - - # Display options - card_style: "detailed" # detailed|simple - hero_style: "banner" # banner|minimal - - # Feature toggles - enable_announcements: true - max_announcements: 5 - - # Content - fallback_message: "Check the Syllabus for current week information." - - announcements: # Optional, can also use CLI - - id: "welcome-2026" - type: "note" - title: "Welcome!" - date: "2026-01-13" - content: "Review the syllabus..." - link: "syllabus/syllabus-final.qmd" - expires: "2026-01-26" -``` - -### Validation Rules - -```zsh -# Required fields -- semester_info.start_date -- semester_info.weeks[] (at least 1) -- semester_info.weeks[].number -- semester_info.weeks[].topic - -# Optional fields (with defaults) -- semester_info.timezone (default: system timezone) -- dashboard.* (all optional, sensible defaults) - -# Validation logic -_teach_validate_dashboard_config() { - # Check required fields exist - yq -e '.semester_info.start_date' .flow/teach-config.yml >/dev/null || { - _flow_log_error "Missing required field: semester_info.start_date" - return 1 - } - - # Validate timezone if specified - local tz=$(yq '.semester_info.timezone // ""' .flow/teach-config.yml) - if [[ -n "$tz" ]]; then - TZ="$tz" date >/dev/null 2>&1 || { - _flow_log_warn "Invalid timezone: $tz (will use system default)" - } - fi - - # Validate max_announcements - local max=$(yq '.dashboard.max_announcements // 5' .flow/teach-config.yml) - if [[ $max -lt 1 || $max -gt 20 ]]; then - _flow_log_warn "max_announcements out of range (1-20), using default: 5" - fi - - return 0 -} -``` - ---- - -## Error Handling & Edge Cases - -### Common Failure Scenarios - -#### 1. Config Missing or Invalid - -```zsh -_teach_dashboard_generate() { - # Check config exists - if [[ ! -f ".flow/teach-config.yml" ]]; then - _flow_log_error "teach-config.yml not found" - _flow_log_info "Run: teach init" - return 1 - fi - - # Validate config - _teach_validate_dashboard_config || { - _flow_log_error "Invalid config, cannot generate dashboard" - return 1 - } - - # ... proceed with generation ... -} -``` - -#### 2. Timezone Calculation Failure - -```zsh -_teach_calculate_current_week() { - local start_date="$1" - local timezone="${2:-America/Denver}" - - # Try with specified timezone - local current_date - if ! current_date=$(TZ="$timezone" date +%Y-%m-%d 2>/dev/null); then - _flow_log_warn "Timezone error, using system timezone" - current_date=$(date +%Y-%m-%d) - fi - - # Calculate week number - local days_since_start=$(( ($(date -j -f "%Y-%m-%d" "$current_date" +%s) - \ - $(date -j -f "%Y-%m-%d" "$start_date" +%s)) / 86400 )) - local week_number=$(( days_since_start / 7 + 1 )) - - # Bounds check - if [[ $week_number -lt 1 ]]; then - echo "0" # Before semester starts - else - echo "$week_number" - fi -} -``` - -#### 3. Break Week Handling - -```zsh -_teach_get_active_week_for_date() { - local target_date="$1" - local weeks_json="$2" - local breaks_json="$3" - - # Check if date is within a break - local in_break=$(echo "$breaks_json" | jq --arg date "$target_date" ' - map(select($date >= .start and $date <= .end)) | length > 0 - ') - - if [[ "$in_break" == "true" ]]; then - # Check if break has show_next: true - local show_next=$(echo "$breaks_json" | jq --arg date "$target_date" -r ' - map(select($date >= .start and $date <= .end))[0].show_next // true - ') - - if [[ "$show_next" == "true" ]]; then - # Show next week after break - local break_end=$(echo "$breaks_json" | jq --arg date "$target_date" -r ' - map(select($date >= .start and $date <= .end))[0].end - ') - # Calculate week for day after break - _teach_calculate_week_for_date "$(date -v+1d -j -f "%Y-%m-%d" "$break_end" +%Y-%m-%d)" - else - # Show message that course is on break - echo "BREAK" - fi - else - # Normal week calculation - _teach_calculate_week_for_date "$target_date" - fi -} -``` - -#### 4. JSON Generation Failure - -```zsh -_teach_dashboard_generate() { - # ... validation ... - - # Generate JSON with error handling - local json_output - json_output=$(_build_dashboard_json) || { - _flow_log_error "JSON generation failed" - return 1 - } - - # Validate JSON before writing - if ! echo "$json_output" | jq . >/dev/null 2>&1; then - _flow_log_error "Generated invalid JSON" - _flow_log_debug "$json_output" - return 1 - fi - - # Atomic write (write to temp, then move) - local temp_file=".flow/.semester-data.json.tmp" - echo "$json_output" > "$temp_file" || { - _flow_log_error "Failed to write temp file" - return 1 - } - - mv "$temp_file" ".flow/semester-data.json" || { - _flow_log_error "Failed to move JSON to final location" - rm -f "$temp_file" - return 1 - } - - _flow_log_success "Generated: .flow/semester-data.json" -} -``` - ---- - -## Performance Considerations - -### Generation Speed - -**Target:** < 500ms for typical course (16 weeks, 5 announcements) - -#### Optimization Strategies - -**1. Lazy Loading** - -```zsh -# Only load dashboard code if needed -if [[ "$1" == "dashboard" ]]; then - source "${0:A:h}/teach-dashboard-impl.zsh" -fi -``` - -**2. Caching** - -```zsh -# Cache parsed config (already exists via config-validator.zsh) -_TEACH_CONFIG_CACHE="" - -_get_cached_config() { - if [[ -z "$_TEACH_CONFIG_CACHE" ]]; then - _TEACH_CONFIG_CACHE=$(cat .flow/teach-config.yml) - fi - echo "$_TEACH_CONFIG_CACHE" -} -``` - -**3. Minimize yq/jq Calls** - -```zsh -# Bad: Multiple yq calls -weeks=$(yq '.semester_info.weeks' .flow/teach-config.yml) -start_date=$(yq '.semester_info.start_date' .flow/teach-config.yml) -timezone=$(yq '.semester_info.timezone' .flow/teach-config.yml) - -# Good: Single yq call -config=$(yq -o=json '{ - weeks: .semester_info.weeks, - start_date: .semester_info.start_date, - timezone: .semester_info.timezone -}' .flow/teach-config.yml) - -weeks=$(echo "$config" | jq -r '.weeks') -start_date=$(echo "$config" | jq -r '.start_date') -timezone=$(echo "$config" | jq -r '.timezone') -``` - -### JSON Size - -**Typical Course:** -- 16 weeks × ~200 bytes = 3.2 KB -- 5 announcements × ~150 bytes = 750 bytes -- Metadata: ~500 bytes -- **Total: ~4.5 KB** - -**Large Course:** -- 32 weeks × 200 bytes = 6.4 KB -- 10 announcements × 150 bytes = 1.5 KB -- **Total: ~8 KB** - -**Recommendation:** No need for minification, sizes are trivial. - ---- - -## Testing Strategy - -### Unit Tests (test-teach-dashboard.zsh) - -```bash -#!/usr/bin/env zsh - -# Test 1: Generate with minimal config -test_generate_minimal() { - setup_minimal_config - teach dashboard generate - assert_file_exists ".flow/semester-data.json" - assert_json_valid ".flow/semester-data.json" -} - -# Test 2: Generate with full config -test_generate_full() { - setup_full_config - teach dashboard generate - local json=$(cat .flow/semester-data.json) - assert_json_field_equals "$json" ".weeks | length" "16" - assert_json_field_equals "$json" ".announcements | length" "3" -} - -# Test 3: Preview current week -test_preview_current() { - setup_config_with_current_week_5 - output=$(teach dashboard preview) - assert_contains "$output" "Week 5" - assert_contains "$output" "Factorial Designs" -} - -# Test 4: Preview specific week -test_preview_week() { - setup_full_config - output=$(teach dashboard preview --week 8) - assert_contains "$output" "Week 8" - assert_not_contains "$output" "Week 5" -} - -# Test 5: Break week shows next -test_break_week_show_next() { - setup_config_with_break_week_3 - freeze_date "2026-02-02" # During break - output=$(teach dashboard preview) - assert_contains "$output" "Week 4" # Shows next week - assert_contains "$output" "Spring Break" -} - -# Test 6: Add announcement -test_add_announcement() { - setup_full_config - teach dashboard announce "Test" "Message" --expires 2026-12-31 - local json=$(cat .flow/semester-data.json) - assert_json_field_contains "$json" ".announcements[].title" "Test" -} - -# Test 7: Timezone handling -test_timezone_conversion() { - setup_config_with_timezone "America/New_York" - teach dashboard generate - local json=$(cat .flow/semester-data.json) - assert_json_field_equals "$json" ".timezone" "America/New_York" -} - -# Test 8: Invalid config -test_invalid_config() { - setup_invalid_config # Missing start_date - output=$(teach dashboard generate 2>&1) - assert_exit_code 1 - assert_contains "$output" "Missing required field" -} - -# Test 9: Config options respected -test_config_options_show_labs_false() { - setup_config_with_show_labs_false - teach dashboard generate - output=$(teach dashboard preview --week 1) - assert_not_contains "$output" "[LAB]" -} - -# Test 10: Stale check -test_dashboard_stale_check() { - setup_full_config - teach dashboard generate - sleep 1 - touch .flow/teach-config.yml # Make config newer - assert_true _teach_dashboard_is_stale -} -``` - -### Integration Tests - -```bash -# Test: Full workflow -test_full_workflow() { - # 1. Init course - teach init "Test Course" - - # 2. Edit config (simulate) - add_weeks_to_config - - # 3. Generate dashboard - teach dashboard generate - assert_file_exists ".flow/semester-data.json" - - # 4. Preview - output=$(teach dashboard preview) - assert_success - - # 5. Add announcement - teach dashboard announce "Welcome" "Test message" --expires 2026-12-31 - - # 6. Deploy (dry run) - output=$(teach deploy --dry-run) - assert_contains "$output" "Dashboard current" -} - -# Test: dates sync integration -test_dates_sync_regenerates_dashboard() { - setup_full_config - teach dashboard generate - local old_mtime=$(stat -f %m .flow/semester-data.json) - - sleep 1 - teach dates sync --force - - local new_mtime=$(stat -f %m .flow/semester-data.json) - assert_not_equal "$old_mtime" "$new_mtime" -} -``` - ---- - -## Future Enhancements - -### Phase 2 Features (v5.16.0+) - -#### 1. Scholar Auto-Update - -```bash -# When Scholar creates content, update config -teach lecture "Topic" --week 5 -# Auto-adds lecture.url to teach-config.yml week 5 -# Auto-regenerates dashboard -``` - -#### 2. Multi-Course Dashboard - -```bash -# Support multiple courses in one repo -teach dashboard generate --course stat545 -teach dashboard generate --course stat440 -``` - -#### 3. Dashboard Analytics - -```bash -# Track what students are viewing -teach dashboard analytics -# Shows: most viewed weeks, announcement click-through, etc. -``` - -#### 4. Custom Themes - -```bash -# User-provided templates -teach dashboard generate --theme custom.json -``` - -#### 5. Announcement Templates - -```bash -# Pre-defined announcement types -teach dashboard announce --template exam -# Fills in standard exam announcement -``` - ---- - -## Open Integration Questions - -### 1. teach status Integration - -**Should teach status show dashboard info?** - -```bash -teach status - -📊 Course Status -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - -Repository: Clean -Git Status: 3 files staged - -Dashboard: ✅ Current (generated 2 hours ago) - • Current Week: 5 - • Active Announcements: 2 - • Last Updated: 2026-01-19 14:30 - -Deployment: Last deployed 3 days ago - • Open PRs: 1 (deploy-2026-01-16) -``` - -**Recommendation:** Yes, add dashboard section - ---- - -### 2. Backup System Integration - -**Should backups include semester-data.json?** - -```bash -teach backup create -# Should this backup .flow/semester-data.json? -``` - -**Considerations:** -- JSON is generated, not source -- But contains announcements added via CLI -- Announcements might also be in teach-config.yml - -**Recommendation:** Backup JSON, but mark as "derived" in backup manifest - ---- - -### 3. Multi-Language Support - -**Should dashboard support i18n?** - -```yaml -dashboard: - language: "en" # en, es, fr - labels: - lecture: "Lecture" - lab: "Lab" - assignment: "Assignment" -``` - -**Recommendation:** Defer to v5.17.0, add when needed - ---- - -## Summary & Recommendations - -### Critical Decisions - -| Integration Point | Decision | Priority | -|-------------------|----------|----------| -| **teach dates sync** | Auto-trigger dashboard generate | High | -| **teach deploy** | Pre-deploy staleness check | High | -| **Scholar** | Passive (no auto-update) | Medium | -| **Quarto** | Use project resources | High | -| **File location** | lib/dispatchers/teach-dashboard-impl.zsh | High | -| **teach status** | Add dashboard section | Low | -| **Backups** | Include JSON as derived | Low | - -### Implementation Order - -1. **Phase 1:** Core generation (standalone) -2. **Phase 2:** teach dates integration -3. **Phase 3:** teach deploy integration -4. **Phase 4:** teach status integration -5. **Future:** Scholar auto-update, multi-course - -### Architecture Principles - -✅ **Loose Coupling:** Dashboard reads config, doesn't modify it (initially) -✅ **Graceful Degradation:** Works without dashboard config section -✅ **Progressive Enhancement:** Start simple, add features incrementally -✅ **Fail-Safe:** Errors don't break teach workflow - ---- - -## Next Actions - -1. **Commit this brainstorm** to dev branch -2. **Update SPEC** with integration details -3. **Create feature branch** via worktree -4. **Begin Phase 1** implementation - ---- - -**End of Brainstorm** diff --git a/docs/specs/BRAINSTORM-teaching-prompts-enhancement-2026-01-21.md b/docs/specs/BRAINSTORM-teaching-prompts-enhancement-2026-01-21.md deleted file mode 100644 index 605734f70..000000000 --- a/docs/specs/BRAINSTORM-teaching-prompts-enhancement-2026-01-21.md +++ /dev/null @@ -1,1012 +0,0 @@ -# 🧠 BRAINSTORM: Teaching Prompts Enhancement Plan - -**Generated:** 2026-01-21 -**Context:** PR #283 - Teaching prompts enhancement -**Mode:** Deep + Feature + Architecture + Automation -**Duration Budget:** 15 minutes -**Implementation Target:** 1-2 hours (quick wins) - ---- - -## 📋 Overview - -PR #283 introduces excellent teaching prompts but has key gaps: -1. **Discovery problem:** Users don't know prompts exist -2. **Generic content:** Prompts need course-specific customization -3. **Manual workflow:** No integration with teach-dispatcher -4. **No validation:** Can't verify generated content quality - -**Goal:** Transform static prompt files into an integrated, intelligent teaching content system with smart defaults and course-aware customization. - ---- - -## 🎯 User Stories - -### Primary User Story - -**As a** statistics instructor using flow-cli -**I want** seamless prompt integration in teach-dispatcher commands -**So that** I can generate course content with one command instead of copy-pasting prompts - -**Acceptance Criteria:** -- ✅ `teach lecture "Topic"` auto-uses lecture-notes.md prompt -- ✅ `teach slides "Topic"` auto-uses revealjs-slides.md prompt -- ✅ `teach appendix "Topic"` auto-uses derivations-appendix.md prompt -- ✅ Prompts auto-fill course-specific info (R packages, notation) from course.yml -- ✅ Generated content validates against prompt requirements - -### Secondary User Stories - -**As a** new flow-cli user -**I want** to discover available prompts easily -**So that** I know what content types I can generate - -**As a** instructor with specific pedagogical preferences -**I want** to customize prompts for my course -**So that** generated content matches my teaching style - ---- - -## ⚡ Quick Wins (< 30 min each) - -### 1. Add Versioning to Prompts (5-10 min) - -```markdown - - - -``` - -**Why:** Track prompt evolution, ensure compatibility -**Implementation:** Add header to each .md file - ---- - -### 2. Create `teach prompt` Command (20-30 min) - -**Commands:** - -```bash -teach prompt list # Show available prompts -teach prompt show lecture # Display lecture-notes.md -teach prompt show slides # Display revealjs-slides.md -teach prompt show appendix # Display derivations-appendix.md -teach prompt path lecture # Print file path (for scripting) -``` - -**Implementation:** Add to `lib/dispatchers/teach-dispatcher.zsh` - -```zsh -_teach_prompt() { - local action="${1:-list}" - local prompt_dir="$FLOW_ROOT/lib/templates/teaching/claude-prompts" - - case "$action" in - list) - echo "📋 Available Teaching Prompts:" - echo "" - echo " lecture - Comprehensive lecture notes (20-40 pages)" - echo " slides - RevealJS presentations (25+ slides)" - echo " appendix - Mathematical derivations & proofs" - echo "" - echo "Usage: teach prompt show " - ;; - show) - local type="$2" - case "$type" in - lecture) cat "$prompt_dir/lecture-notes.md" ;; - slides) cat "$prompt_dir/revealjs-slides.md" ;; - appendix) cat "$prompt_dir/derivations-appendix.md" ;; - *) echo "Unknown prompt type: $type" ;; - esac - ;; - path) - local type="$2" - case "$type" in - lecture) echo "$prompt_dir/lecture-notes.md" ;; - slides) echo "$prompt_dir/revealjs-slides.md" ;; - appendix) echo "$prompt_dir/derivations-appendix.md" ;; - esac - ;; - help) _teach_prompt_help ;; - *) _teach_prompt_help ;; - esac -} -``` - -**Benefit:** Immediate discoverability, zero learning curve - ---- - -### 3. Add Real-World Examples to README (15-20 min) - -**Current:** README describes prompts abstractly -**Enhancement:** Add "Example Output" sections - -```markdown -## Example: Lecture Notes - -**Input:** -```bash -teach lecture "Factorial ANOVA" -``` - -**Generated Output Structure:** -- 28 pages -- 6 learning objectives (Bloom's taxonomy) -- Motivating problem (agriculture experiment) -- 3 complete derivations (EMS for Factor A, B, AB) -- 5 R code examples with interpretation -- 8 practice problems with solutions -- Full diagnostic workflow with `performance::check_model()` - -**Sample excerpt:** [Link to sample-lecture-factorial-anova.md] - -``` - -**Why:** Users see concrete value immediately -**Implementation:** Create 3 sample outputs (one per prompt type) - ---- - -### 4. Scholar Integration Hook (25-30 min) - -**Problem:** Scholar's `/teaching:lecture` doesn't know about new prompts - -**Solution:** Add prompt reference to Scholar skill invocation - -```bash -# In teach-dispatcher.zsh -_teach_scholar_lecture() { - local topic="$1" - local prompt_path="$FLOW_ROOT/lib/templates/teaching/claude-prompts/lecture-notes.md" - - # Invoke Scholar with prompt reference - claude skill teaching:lecture "$topic" --reference "$prompt_path" -} -``` - -**Benefit:** Zero-config Scholar integration -**Fallback:** If Scholar not installed, show prompt content + instructions - ---- - -## 🔧 Medium Effort (1-2 hours each) - -### 5. Course-Aware Prompt Rendering (1-2 hours) - -**Problem:** Prompts are generic - no course-specific context - -**Solution:** Template rendering from `course.yml` - -**Template Syntax (in prompts):** - -```markdown -**Package Loading:** -```r -pacman::p_load( - {{course.r_packages}} # Auto-filled from course.yml -) -``` - -**Example course.yml:** - -```yaml -course: - name: "STAT 440 - Regression Analysis" - r_packages: - - emmeans - - lme4 - - car - - performance - notation: - expectation: "E[X]" - variance: "Var(X)" - derivation_depth: "rigorous-with-intuition" -``` - -**Rendering Logic:** - -```zsh -_teach_render_prompt() { - local prompt_file="$1" - local course_yml="$2" - - # Use yq to extract course config - local packages=$(yq '.course.r_packages[]' "$course_yml" | paste -sd, -) - - # Render template - sed "s/{{course.r_packages}}/$packages/g" "$prompt_file" -} -``` - -**Impact:** Prompts automatically adapt to course setup - ---- - -### 6. Prompt Customization System (1.5-2 hours) - -**Goal:** Let instructors create course-specific prompt overrides - -**Commands:** - -```bash -teach prompt customize lecture # Interactive customization -teach prompt customize slides # Interactive customization -teach prompt reset lecture # Restore default -``` - -**Interactive Flow:** - -``` -teach prompt customize lecture - -🎨 Customize Lecture Prompt for STAT 440 - -1. R packages (current: emmeans, lme4, car) - → Add packages? [y/N]: y - → Package names (comma-separated): DHARMa, broom - -2. Derivation depth (current: rigorous-with-intuition) - → Change? [y/N]: n - -3. Notation style (current: LaTeX macros) - → Change? [y/N]: n - -✅ Customized prompt saved to: - .claude/prompts/lecture-notes.local.md - -Use: teach lecture "Topic" (will use customized version) -``` - -**Implementation:** -1. Copy default prompt to `.claude/prompts/lecture-notes.local.md` -2. Apply sed replacements based on user input -3. Modify `_teach_scholar_lecture()` to check for `.local.md` first - -**Benefit:** One-time setup, all future lectures use customization - ---- - -### 7. Content Validation Tool (1-2 hours) - -**Command:** - -```bash -teach validate lecture Week-3-ANOVA.qmd -``` - -**Checks:** -- ✅ Has learning objectives section -- ✅ Learning objectives use Bloom's taxonomy verbs -- ✅ Has motivating problem -- ✅ Code chunks are labeled (`#| label:`) -- ✅ Figures have captions (`#| fig-cap:`) -- ✅ Has practice problems section -- ✅ Derivations have step annotations -- ⚠️ Missing: Diagnostic workflow (`performance::check_model()`) -- ⚠️ Warning: Only 2 practice problems (prompt recommends 4-10) - -**Output:** - -``` -📊 Validation Report: Week-3-ANOVA.qmd - -Prompt: lecture-notes.md (v1.0.0) -Status: ⚠️ PASS WITH WARNINGS (7/9 checks passed) - -✅ Required Elements - ✓ Learning objectives (6 found) - ✓ Bloom's taxonomy verbs used - ✓ Motivating problem present - ✓ Code chunks labeled (12/12) - ✓ Figures captioned (8/8) - ✓ Practice problems section - -⚠️ Warnings - ⚠ Only 2 practice problems (recommended: 4-10) - ⚠ Missing diagnostic workflow section - -❌ Errors - ✗ Derivation steps not annotated (line 245-280) - -💡 Suggestions: - - Add performance::check_model() in Section 4.2 - - Add step annotations to EMS derivation (see prompt line 45-60) - - Consider adding 2-3 more practice problems -``` - -**Implementation:** -- Parse `.qmd` with `yq` (YAML) + `grep` (content patterns) -- Compare against prompt checklist -- Generate actionable report - ---- - -## 🏗️ Long-Term (Future Sprints) - -### 8. Additional Prompt Templates - -Create 4 new prompt types: - -| Prompt | Purpose | Output | -|--------|---------|--------| -| `assignment.md` | Homework/project assignments | Problem sets with rubrics | -| `exam.md` | Exam generation | Multiple formats (MC, short answer, problems) | -| `syllabus.md` | Course syllabus | Complete syllabus with policies | -| `rubric.md` | Grading rubrics | Detailed rubrics for assignments/exams | - -**Priority:** assignment > exam > syllabus > rubric - -**Time:** 2-3 hours per prompt (research + writing + testing) - ---- - -### 9. AI Recipe Integration - -**Goal:** Trigger prompts via natural language - -**Example:** - -``` -[teach-lecture] Create lecture notes for "Interaction Effects" -[teach-slides] Generate 30 slides on "Random Effects" -[teach-appendix] Derive the expected mean squares for three-way ANOVA -``` - -**Implementation:** -1. Create `.claude/recipes/teaching-recipes.md` -2. Add trigger patterns + prompt references -3. Document in README - -**Time:** 1-2 hours - ---- - -### 10. Version Management System - -**Commands:** - -```bash -teach prompt versions lecture # Show version history -teach prompt upgrade lecture # Upgrade to latest version -teach prompt diff 1.0.0 1.1.0 # Compare versions -``` - -**Implementation:** -- Git-based versioning (tags for releases) -- Migration guides for breaking changes -- Backward compatibility warnings - -**Time:** 2-3 hours - ---- - -## 🏛️ Architecture - -### Current Structure - -``` -lib/templates/teaching/claude-prompts/ -├── README.md # Documentation -├── lecture-notes.md # Prompt (static) -├── revealjs-slides.md # Prompt (static) -└── derivations-appendix.md # Prompt (static) -``` - -### Enhanced Structure (Phase 1) - -``` -lib/templates/teaching/claude-prompts/ -├── README.md # Enhanced with examples -├── lecture-notes.md # v1.0.0 with versioning -├── revealjs-slides.md # v1.0.0 with versioning -├── derivations-appendix.md # v1.0.0 with versioning -├── examples/ # NEW: Sample outputs -│ ├── sample-lecture-anova.md -│ ├── sample-slides-regression.md -│ └── sample-appendix-ems.md -└── schemas/ # NEW: Validation schemas - ├── lecture-checklist.yml - ├── slides-checklist.yml - └── appendix-checklist.yml - -lib/dispatchers/teach-dispatcher.zsh -└── (enhanced with teach prompt commands) - -.claude/prompts/ # NEW: Course overrides -├── lecture-notes.local.md # (user-customized) -└── revealjs-slides.local.md # (user-customized) -``` - -### Enhanced Structure (Phase 2 - Future) - -``` -lib/templates/teaching/claude-prompts/ -├── ... -├── assignment.md # NEW: Assignment prompt -├── exam.md # NEW: Exam prompt -├── syllabus.md # NEW: Syllabus prompt -└── rubric.md # NEW: Rubric prompt - -lib/validators/ -└── teaching-content-validator.zsh # NEW: Validation engine - -.claude/recipes/ -└── teaching-recipes.md # NEW: AI recipe triggers -``` - ---- - -## 🔗 Integration Points - -### 1. teach-dispatcher Integration (Phase 1) - -**Commands Added:** - -```bash -teach prompt list # List available prompts -teach prompt show # Display prompt content -teach prompt path # Get file path -teach prompt customize # Interactive customization -teach prompt reset # Restore defaults - -teach lecture "Topic" # Auto-uses lecture prompt -teach slides "Topic" [count] # Auto-uses slides prompt -teach appendix "Topic" # Auto-uses appendix prompt - -teach validate # Validate against prompt checklist -``` - -**Routing Changes:** - -```zsh -# In teach-dispatcher.zsh -case "$1" in - prompt) shift; _teach_prompt "$@" ;; - lecture) shift; _teach_lecture "$@" ;; # NEW - slides) shift; _teach_slides "$@" ;; # NEW - appendix) shift; _teach_appendix "$@" ;; # NEW - validate) shift; _teach_validate "$@" ;; # NEW - # ... existing commands -esac -``` - ---- - -### 2. Scholar Plugin Integration (Phase 1) - -**Option A: Implicit (Recommended for Quick Win)** - -```bash -# teach lecture calls Scholar with prompt reference -teach lecture "ANOVA" → scholar teaching:lecture "ANOVA" --prompt lecture-notes.md -``` - -**Option B: Explicit (Phase 2)** - -```bash -# Scholar skills auto-detect prompts -/teaching:lecture "ANOVA" # Scholar checks for lib/templates/.../lecture-notes.md - # If found, uses it automatically -``` - -**Implementation:** Modify Scholar skill to check for prompt files in known locations - ---- - -### 3. AI Recipes Integration (Phase 2) - -**File:** `.claude/recipes/teaching-recipes.md` - -```markdown -# Teaching Content Recipes - -## [teach-lecture] -**Trigger:** [teach-lecture] -**Prompt:** lib/templates/teaching/claude-prompts/lecture-notes.md -**Action:** Generate comprehensive lecture notes - -## [teach-slides] -**Trigger:** [teach-slides] [slide_count] -**Prompt:** lib/templates/teaching/claude-prompts/revealjs-slides.md -**Action:** Generate RevealJS presentation -``` - -**Integration:** Hook into prompt-dispatcher or create new recipe handler - ---- - -### 4. course.yml Integration (Phase 1) - -**Template Variables:** - -```yaml -# course.yml -course: - name: "STAT 440" - r_packages: [emmeans, lme4, car, performance] - notation: - expectation: "\\E{X}" - variance: "\\Var{X}" - derivation_depth: "rigorous-with-intuition" - prompt_overrides: - lecture: - practice_problems_count: 6 - include_diagnostic_workflow: true -``` - -**Prompt Rendering:** - -```markdown - -pacman::p_load({{course.r_packages}}) # → pacman::p_load(emmeans, lme4, car, performance) -``` - -**Implementation:** `yq` for YAML parsing + `envsubst` or custom `sed` replacements - ---- - -## 📊 Dependencies - -### Required - -- ✅ `yq` - YAML parsing (course.yml, checklist schemas) -- ✅ `git` - Version tracking -- ✅ ZSH - teach-dispatcher implementation - -### Optional - -- ⚠️ Scholar plugin v2.x - Enhanced `/teaching:*` integration -- ⚠️ Claude Code - AI recipe triggers -- ⚠️ `pandoc` - Markdown validation (for teach validate) - -### New Files/Libraries - -- `lib/validators/teaching-content-validator.zsh` (Phase 2) -- `lib/templates/teaching/claude-prompts/schemas/*.yml` (Phase 1) -- `lib/templates/teaching/claude-prompts/examples/*.md` (Phase 1) - ---- - -## 📝 Implementation Roadmap - -### Phase 1: Quick Wins (1-2 hours total) - -**Wave 1: Foundation (20 min)** -- [ ] Add versioning headers to 3 prompts (5 min) -- [ ] Create 3 example outputs (sample-lecture, sample-slides, sample-appendix) (15 min) - -**Wave 2: teach-dispatcher Integration (30 min)** -- [ ] Add `teach prompt list` command (10 min) -- [ ] Add `teach prompt show ` command (10 min) -- [ ] Add `teach prompt path ` command (5 min) -- [ ] Update teach-dispatcher help (5 min) - -**Wave 3: Documentation (15 min)** -- [ ] Enhance README with example outputs section (10 min) -- [ ] Add "Quick Start" guide to README (5 min) - -**Wave 4: Scholar Integration (15 min)** -- [ ] Add `teach lecture` command (uses Scholar + prompt) (10 min) -- [ ] Add fallback for no-Scholar case (5 min) - -**Phase 1 Deliverables:** -- ✅ 4 new teach-dispatcher commands -- ✅ Versioned prompts -- ✅ Example outputs -- ✅ Enhanced documentation -- ✅ Basic Scholar integration - -**Total Time:** ~80 minutes (within 1-2 hour budget) - ---- - -### Phase 2: Medium Effort (3-5 hours) - -**Prerequisites:** Phase 1 complete + tested - -**Wave 1: Course-Aware Rendering (1.5 hours)** -- [ ] Implement template variable syntax -- [ ] Add `yq`-based rendering engine -- [ ] Test with sample course.yml -- [ ] Document template syntax - -**Wave 2: Validation System (1.5 hours)** -- [ ] Create validation schemas (3 YAML files) -- [ ] Implement `teach validate` command -- [ ] Add validation report formatter -- [ ] Test with sample generated content - -**Wave 3: Customization System (1.5 hours)** -- [ ] Implement `teach prompt customize` -- [ ] Add `.claude/prompts/` override logic -- [ ] Create reset command -- [ ] Test customization workflow - -**Phase 2 Deliverables:** -- ✅ Course-aware prompt rendering -- ✅ Content validation tool -- ✅ Prompt customization system - -**Total Time:** ~4.5 hours - ---- - -### Phase 3: Long-Term (8-12 hours) - -**Wave 1: New Prompts (6 hours)** -- [ ] Research + create `assignment.md` (2 hours) -- [ ] Research + create `exam.md` (2 hours) -- [ ] Research + create `syllabus.md` (1 hour) -- [ ] Research + create `rubric.md` (1 hour) - -**Wave 2: AI Recipes (2 hours)** -- [ ] Create teaching-recipes.md -- [ ] Test recipe triggers -- [ ] Document recipe usage - -**Wave 3: Version Management (2 hours)** -- [ ] Implement version commands -- [ ] Create migration system -- [ ] Document versioning - -**Phase 3 Deliverables:** -- ✅ 4 new prompt types -- ✅ AI recipe integration -- ✅ Version management - -**Total Time:** ~10 hours - ---- - -## 🧪 Testing Strategy - -### Phase 1 Testing (Manual) - -**Prompt Discovery:** - -```bash -# Test: Can users find prompts? -teach prompt list # → Should show 3 prompts -teach prompt show lecture # → Should display lecture-notes.md -``` - -**Scholar Integration:** - -```bash -# Test: Does Scholar integration work? -teach lecture "Test Topic" # → Should invoke Scholar or show fallback -``` - -**Documentation:** - -```bash -# Test: Are examples clear? -cat lib/templates/teaching/claude-prompts/examples/sample-lecture-anova.md -# → Should show realistic lecture output -``` - ---- - -### Phase 2 Testing (Unit + Integration) - -**Template Rendering:** - -```bash -# Test: Does course.yml integration work? -teach lecture "Test" --course course.yml -# → Generated content should use course-specific packages -``` - -**Validation:** - -```bash -# Test: Does validation catch issues? -teach validate test-lecture.qmd -# → Should report missing elements -``` - -**Customization:** - -```bash -# Test: Does customization persist? -teach prompt customize lecture # → Interactive flow -teach lecture "Test" # → Should use customized prompt -``` - ---- - -### Phase 3 Testing (Comprehensive) - -**New Prompts:** - -```bash -# Test: Do new prompts generate valid content? -teach assignment "Homework 3" -teach exam "Midterm" -teach validate hw3.qmd # → Validates against assignment.md checklist -``` - -**Recipes:** - -```bash -# Test: Do recipes work? -[teach-lecture] Generate lecture for "GLMs" -# → Should trigger lecture prompt -``` - ---- - -## 🎯 Success Metrics - -| Metric | Target | Measurement | -|--------|--------|-------------| -| **Discovery** | 100% users know prompts exist | Survey after 2 weeks | -| **Usage** | 5+ prompts used per week | Git commits with prompt-generated content | -| **Customization** | 50% courses have .local.md overrides | Count .claude/prompts/*.local.md files | -| **Quality** | 90% validation pass rate | teach validate results | -| **Time Savings** | 30 min → 5 min to create lecture | Time tracking before/after | - ---- - -## ⚠️ Open Questions - -1. **Scholar Dependency:** Should teach-dispatcher require Scholar plugin, or provide fallback? - - **Recommendation:** Fallback (show prompt + instructions if Scholar not found) - -2. **Validation Strictness:** Should `teach validate` block deployment if checks fail? - - **Recommendation:** Warnings only for Phase 1, blocking in Phase 2 (opt-in) - -3. **Prompt Versioning:** How to handle breaking changes in prompts? - - **Recommendation:** Semantic versioning (1.0.0 → 1.1.0 = backward compatible, 2.0.0 = breaking) - -4. **Course.yml Location:** Where should course.yml live? - - **Recommendation:** Project root (same as .STATUS, course.yml.template) - -5. **Override Precedence:** What if both .local.md and course.yml exist? - - **Recommendation:** .local.md wins (explicit > implicit) - ---- - -## 🔑 Key Decisions - -### Decision 1: teach-dispatcher as Primary Interface - -**Rationale:** Users already familiar with teach commands, minimal learning curve - -### Decision 2: Course-Specific Customization via .local.md - -**Rationale:** Follows flow-cli convention (.claude/*.local.md pattern) - -### Decision 3: Validation as Opt-In (Phase 1) - -**Rationale:** Avoid breaking existing workflows, introduce gradually - -### Decision 4: Examples Over Abstractions - -**Rationale:** Users learn faster from concrete examples than descriptions - -### Decision 5: Phased Rollout (3 phases) - -**Rationale:** Quick wins build momentum, validate direction before heavy investment - ---- - -## 📚 Documentation Plan - -### Phase 1 Documentation - -**Updates:** -1. **README.md** - Add "Quick Start", "Example Outputs", "Integration" sections -2. **IMPLEMENTATION.md** - Add Phase 1 implementation guide -3. **teach-dispatcher help** - Add new commands to help text - -**New Files:** -1. `examples/sample-lecture-anova.md` - Full lecture example -2. `examples/sample-slides-regression.md` - Full slides example -3. `examples/sample-appendix-ems.md` - Derivation example - ---- - -### Phase 2 Documentation - -**New Guides:** -1. `docs/guides/TEACHING-PROMPTS-GUIDE.md` - Comprehensive user guide -2. `docs/guides/PROMPT-CUSTOMIZATION.md` - Customization tutorial -3. `docs/reference/TEACH-PROMPT-REFERENCE.md` - Command reference - -**Updates:** -1. **course.yml.template** - Add prompt-related fields -2. **teach-dispatcher help** - Add validation, customization commands - ---- - -### Phase 3 Documentation - -**New Guides:** -1. `docs/guides/PROMPT-DEVELOPMENT.md` - Creating new prompts -2. `docs/guides/AI-RECIPES-TEACHING.md` - Recipe usage - -**Updates:** -1. **README.md** - Add all 7 prompt types -2. **Changelog** - Document version history - ---- - -## 💡 Next Steps - -### Immediate (Today) - -1. **Review this brainstorm** - Validate approach, identify any gaps -2. **Approve Phase 1 scope** - Confirm 1-2 hour quick wins are sufficient -3. **Create implementation branch** - `feature/teaching-prompts-integration` -4. **Start Wave 1** - Add versioning + examples (20 min) - -### This Week - -1. **Complete Phase 1** - All 4 waves (1-2 hours total) -2. **Test with real course** - STAT 440 or similar -3. **Document Phase 1 results** - Update README, IMPLEMENTATION.md -4. **Create PR** - Merge Phase 1 to dev - -### Next Sprint - -1. **Phase 2 planning** - Detailed spec for validation + customization -2. **Gather user feedback** - What's working, what's missing -3. **Prioritize Phase 2 features** - Based on actual usage patterns - ---- - -## 🎨 Visual Workflows - -### Current Workflow (Before Enhancement) - -``` -User wants lecture notes - ↓ -Open browser → claude.ai - ↓ -Copy prompt from GitHub - ↓ -Paste into Claude - ↓ -Type topic - ↓ -Generate (wait 5 min) - ↓ -Copy output to .qmd - ↓ -Manual validation - ↓ -Done (30 min total) -``` - -### Enhanced Workflow (After Phase 1) - -``` -User wants lecture notes - ↓ -teach lecture "Topic" - ↓ -Scholar invoked with prompt - ↓ -Generated content saved - ↓ -Done (5 min total) - -Savings: 25 minutes (83% reduction) -``` - -### Enhanced Workflow (After Phase 2) - -``` -User wants customized lecture - ↓ -teach prompt customize lecture (one-time setup) - ↓ -teach lecture "Topic" - ↓ -Generated (course-aware) - ↓ -teach validate output.qmd - ↓ -Fix warnings (if any) - ↓ -Done (8 min total, 100% validated) - -Savings: 22 minutes (73% reduction) + quality guarantee -``` - ---- - -## 🚀 Recommended Implementation Path - -### For 1-2 Hour Session (Quick Wins) - -**Priority Order:** -1. ⚡ **Wave 1: Foundation** (20 min) - Versioning + examples -2. ⚡ **Wave 2: teach-dispatcher** (30 min) - `teach prompt` commands -3. ⚡ **Wave 4: Scholar Integration** (15 min) - `teach lecture` command -4. ⚡ **Wave 3: Documentation** (15 min) - README updates - -**Total:** 80 minutes - -**Why this order:** -- Versioning is foundation for everything else -- Examples demonstrate value immediately -- teach-dispatcher integration is most requested feature -- Scholar integration adds immediate automation -- Documentation last (captures what we built) - -**Defer to Phase 2:** -- Course-aware rendering (needs more testing) -- Validation system (needs schema design) -- Customization (complex UX, needs iteration) - ---- - -## 🔄 Iteration Plan - -### After Phase 1 (Week 1) - -- Gather user feedback on teach prompt commands -- Measure usage: Are prompts being discovered? -- Identify pain points with current workflow -- Decide: Proceed to Phase 2 or iterate on Phase 1? - -### After Phase 2 (Week 4) - -- Gather feedback on validation accuracy -- Measure customization adoption -- Identify missing features -- Decide: Proceed to Phase 3 or add Phase 2.5? - -### After Phase 3 (Week 8) - -- Measure comprehensive system usage -- Identify next generation features -- Consider: Prompt marketplace, community templates? - ---- - -## 📋 Review Checklist - -Before starting implementation: - -- [ ] Brainstorm reviewed and approved -- [ ] Phase 1 scope confirmed (1-2 hours) -- [ ] Wave order agreed upon -- [ ] Examples identified (which topics?) -- [ ] Scholar integration approach confirmed -- [ ] Branch created: feature/teaching-prompts-integration -- [ ] Tests planned (what to validate?) -- [ ] Documentation updates scoped - ---- - -## 🎯 Success Criteria - -**Phase 1 is successful if:** -- ✅ Users can discover prompts via `teach prompt list` -- ✅ Users can generate content via `teach lecture "Topic"` -- ✅ Examples demonstrate realistic output quality -- ✅ README clearly explains usage -- ✅ Implementation completed in 1-2 hours - -**Phase 2 is successful if:** -- ✅ Course-specific rendering works (packages auto-filled) -- ✅ Validation catches 90% of quality issues -- ✅ Customization reduces setup time by 50% - -**Phase 3 is successful if:** -- ✅ All 7 prompt types available -- ✅ AI recipes work seamlessly -- ✅ Version management prevents breakage - ---- - -**Generated in:** 14 minutes -**Status:** ✅ Ready for implementation -**Next:** Review → Approve Phase 1 → Create branch → Start Wave 1 - diff --git a/docs/specs/BRAINSTORM-wt-workflow-enhancement-2026-01-17.md b/docs/specs/BRAINSTORM-wt-workflow-enhancement-2026-01-17.md deleted file mode 100644 index 595b44287..000000000 --- a/docs/specs/BRAINSTORM-wt-workflow-enhancement-2026-01-17.md +++ /dev/null @@ -1,123 +0,0 @@ -# BRAINSTORM: WT Workflow Enhancement - -**Date:** 2026-01-17 -**Mode:** Feature | **Depth:** Deep | **Duration:** ~10 min - ---- - -## Topic - -Enhance the `wt` workflow: -- Which is more reliable to show worktree list: `wt list` or `wt`? -- Add "delete" and "update" options to `pick wt` - ---- - -## Context Analysis - -### Current Behavior - -| Command | Behavior | Reliability | -|---------|----------|-------------| -| `wt` (no args) | `cd ~/.git-worktrees` + `ls -la` | ❌ Raw listing, no structure | -| `wt list` | `git worktree list` | ✅ Reliable (git native) | -| `wt status` | Formatted table with icons | ✅ Best formatted view | -| `pick wt` | fzf picker with session indicators | ✅ Interactive selection | - -### Problem - -- `wt` alone is not useful (just navigates + raw ls) -- No quick way to delete worktrees from `pick wt` -- Cache doesn't refresh automatically after create/remove - ---- - -## Questions & Answers (8 Expert Questions) - -### Q1: When you run `wt` with no args, what should happen? - -**A:** Show formatted list (like `wt status` lite) - Quick overview with branch, status icons, path - -### Q2: For `pick wt` delete action, what should happen after selection? - -**A:** Confirm each deletion individually - Safe: 'Delete feature/auth? [y/N]' - -### Q3: What should 'update' mean for `pick wt`? - -**A:** Refresh cache + rescan worktrees - Force cache invalidation - -### Q4: Should delete/update work on multiple worktrees at once? - -**A:** Yes - fzf multi-select with Tab - Power user: select multiple, act on all - -### Q5: Should `wt` (formatted list) also show session indicators like `pick wt`? - -**A:** Yes - show 🟢/🟡/⚪ Claude session status - Match pick wt display - -### Q6: Where should delete confirmation happen? - -**A:** Inside fzf preview pane - See details before confirming - -### Q7: Should deleted worktrees also delete the branch? - -**A:** Ask each time: 'Also delete branch? [y/N]' - User decides per deletion - -### Q8: For the 'update' (cache refresh), should it also show the refreshed list? - -**A:** Yes - refresh then show wt (formatted list) - Quick view after refresh - -### Q9 (Bonus): What keybindings should trigger delete/update in pick wt? - -**A:** ctrl-x for delete, ctrl-r for refresh - x=remove, r=refresh - -### Q10 (Bonus): Should `wt` also accept a filter argument like `pick wt`? - -**A:** Yes - `wt flow` shows only flow-cli worktrees - Match pick wt filter behavior - ---- - -## Quick Wins (< 30 min each) - -1. ⚡ **Change `wt` default** - Call `_wt_overview()` instead of `cd + ls` -2. ⚡ **Add filter argument** - `wt ` filters by project name -3. ⚡ **Add fzf keybindings** - `--bind 'ctrl-x:...,ctrl-r:...'` - -## Medium Effort (1-2 hours) - -- [ ] Implement `_wt_overview()` with session indicators -- [ ] Implement `_pick_wt_delete()` with confirmation flow -- [ ] Implement `_pick_wt_refresh()` with formatted output - -## Long-term (Future sessions) - -- [ ] Add worktree archiving (move to `.archive/` before delete) -- [ ] Add `wt diff` to show changes across all worktrees -- [ ] Add `wt sync` to rebase all worktrees onto base branch - ---- - -## Recommended Path - -→ **Start with Phase 1** (enhanced `wt` default) because: -- Immediate improvement to daily workflow -- Low risk (doesn't change existing commands) -- Foundation for Phase 2 (shares session detection code) - ---- - -## Spec Captured - -✅ **SPEC-wt-workflow-enhancement-2026-01-17.md** created with: -- 3 implementation phases (6-8 hours total) -- Full UI/UX specifications -- Delete and refresh flows documented -- Keybindings defined (ctrl-x, ctrl-r) -- Testing strategy included - ---- - -## Next Steps - -1. Review spec: `docs/specs/SPEC-wt-workflow-enhancement-2026-01-17.md` -2. Create worktree: `wt create feature/wt-enhancement` -3. Start Phase 1: Enhanced `wt` default (2h) diff --git a/docs/specs/SPEC-KEYCHAIN-DEFAULT-v5.19.0.md b/docs/specs/SPEC-KEYCHAIN-DEFAULT-v5.19.0.md deleted file mode 100644 index d79682fc7..000000000 --- a/docs/specs/SPEC-KEYCHAIN-DEFAULT-v5.19.0.md +++ /dev/null @@ -1,1062 +0,0 @@ -# Implementation Plan: Keychain-Default Storage (v5.19.0) - -**Date:** 2026-01-24 -**Goal:** Make Apple Keychain the default storage backend with optional Bitwarden sync -**Scope:** Architecture change + user experience improvements -**Source:** Comprehensive brainstorm with 12 user decisions -**Estimated Effort:** 18 hours (3 phases) - ---- - -## Executive Summary - -Transform flow-cli token storage from dual-storage-always to Keychain-first with optional Bitwarden backup. This plan reflects user decisions from interactive brainstorming. - -**Key Changes:** -1. ✅ Default: Keychain-only (silent, no prompt) -2. ✅ Opt-in: `--sync-bitwarden` flag for cloud backup -3. ✅ Migration: Existing tokens become Keychain-authoritative (on upgrade) -4. ✅ Visibility: Color-coded `dot secret list` -5. ✅ Sync management: `dot secret sync enable/disable` -6. ✅ Conflict resolution: Interactive `dot secret reconcile` - ---- - -## User Decisions (Brainstorm Answers) - -| Question | User's Choice | -|----------|--------------| -| **Existing tokens** | Keep dual, make Keychain authoritative (immediately on upgrade) | -| **Sync trigger** | One-time choice per token (silent Keychain-only default) | -| **Updates** | Always ask again about Bitwarden sync | -| **Visibility** | Enhanced list with color-coded indicators | -| **Add UX** | Silent default (--sync-bitwarden flag to opt-in) | -| **Authority** | Immediately on upgrade (mark all as Keychain-authoritative) | -| **Conflicts** | Show diff and ask user to choose | -| **Conversion** | `dot secret sync enable/disable ` | -| **Rotation** | Always prompt during rotation | -| **List format** | Color-coded (blue=Keychain, green=synced, yellow=conflict) | -| **Bulk ops** | `dot secret sync enable --all` | -| **Bitwarden unavailable** | Warn but continue (no blocking) | - ---- - -## Phase 1: Core Changes (6 hours) - -### 1.1 Metadata Schema v2.2 (1 hour) - -**Add new fields to token metadata:** - -```json -{ - "dot_version": "2.2", - "type": "github", - "token_type": "classic", - "sync_bitwarden": false, // NEW: Sync preference (default: false) - "keychain_authoritative": true, // NEW: Keychain is source of truth - "created": "2026-01-24T14:30:00Z", - "updated": "2026-01-24T15:00:00Z", // NEW: Last update timestamp - "expires_days": 90, - "expires": "2026-04-24", - "github_user": "username" -} -``` - -**Files to modify:** -- `lib/dispatchers/dot-dispatcher.zsh:2100-2200` (metadata generation) - -**Implementation:** -```zsh -# In _dot_token_github() around line 2100 -local metadata=$(cat << EOF -{ - "dot_version": "2.2", - "type": "github", - "token_type": "$token_type", - "sync_bitwarden": ${sync_bitwarden:-false}, - "keychain_authoritative": true, - "created": "$(date -u +%Y-%m-%dT%H:%M:%SZ)", - "updated": "$(date -u +%Y-%m-%dT%H:%M:%SZ)", - "expires_days": 90, - "expires": "$(date -d '+90 days' +%Y-%m-%d)", - "github_user": "$username" -} -EOF -) -``` - ---- - -### 1.2 Default to Keychain-Only (2 hours) - -**Modify `_dot_token_github()` to skip Bitwarden unless flag is set:** - -**Current behavior (lines 2150-2163):** -```zsh -# ALWAYS stores in Bitwarden -echo "$new_item" | bw encode | bw create item --session "$BW_SESSION" -bw sync --session "$BW_SESSION" - -# ALSO stores in Keychain -security add-generic-password -a "$token_name" -s flow-cli -w "$token_value" -j "$metadata" -U -``` - -**New behavior:** -```zsh -# ALWAYS store in Keychain -security add-generic-password \ - -a "$token_name" \ - -s "$_DOT_KEYCHAIN_SERVICE" \ - -w "$token_value" \ - -j "$metadata" \ - -U 2>/dev/null - -# CONDITIONALLY store in Bitwarden (if --sync-bitwarden flag) -local sync_bitwarden=false - -# Check for flag -if [[ "$@" == *"--sync-bitwarden"* ]]; then - sync_bitwarden=true - - _flow_log_info "Syncing to Bitwarden..." - - # Store in Bitwarden - local new_item=$(cat << EOF -{ - "organizationId": null, - "folderId": null, - "type": 1, - "name": "$token_name", - "notes": "$metadata", - "favorite": false, - "login": { - "username": "$username", - "password": "$token_value", - "totp": null - } -} -EOF - ) - echo "$new_item" | bw encode | bw create item --session "$BW_SESSION" 2>/dev/null - bw sync --session "$BW_SESSION" 2>/dev/null - - if [[ $? -eq 0 ]]; then - _flow_log_success "Synced to Bitwarden" - else - _flow_log_warning "Bitwarden sync failed (but token stored in Keychain)" - fi -else - _flow_log_info "Stored in Keychain only (use --sync-bitwarden for cloud backup)" -fi -``` - -**Files to modify:** -- `lib/dispatchers/dot-dispatcher.zsh:2150-2180` (_dot_token_github) -- `lib/dispatchers/dot-dispatcher.zsh` (_dot_token_npm, _dot_token_pypi) - similar changes - ---- - -### 1.3 Enhanced List Command (1 hour) - -**Add color-coded sync status to `dot secret list`:** - -**Current output:** -``` -dot secret list -github-token -npm-token -pypi-token -``` - -**New output:** -``` -dot secret list - -🔵 github-token Keychain only -🟢 npm-token Synced to Bitwarden -🟡 pypi-token ⚠ Conflict detected -``` - -**Implementation:** - -Create new helper function in `lib/keychain-helpers.zsh`: - -```zsh -# NEW: Check sync status (Keychain vs Bitwarden) -_dot_check_sync_status() { - local name=$1 - - # Get Keychain metadata - local kc_metadata=$(security find-generic-password \ - -a "$name" \ - -s "$_DOT_KEYCHAIN_SERVICE" \ - -g 2>&1 | grep "note:" | sed 's/note: //') - - if [[ -z "$kc_metadata" ]]; then - echo "missing" - return - fi - - # Check if sync enabled - local sync_enabled=$(echo "$kc_metadata" | jq -r '.sync_bitwarden // false') - - if [[ "$sync_enabled" != "true" ]]; then - echo "keychain-only" - return - fi - - # Check if Bitwarden copy exists (requires bw CLI) - if ! command -v bw &>/dev/null; then - echo "synced" # Assume synced if bw not available - return - fi - - # Get Bitwarden copy (if logged in) - local bw_value=$(bw get password "$name" --session $BW_SESSION 2>/dev/null) - - if [[ -z "$bw_value" ]]; then - echo "keychain-only" # Bitwarden copy missing - return - fi - - # Compare values - local kc_value=$(security find-generic-password -a "$name" -s "$_DOT_KEYCHAIN_SERVICE" -w 2>/dev/null) - - if [[ "$kc_value" == "$bw_value" ]]; then - echo "synced" - else - echo "conflict" - fi -} -``` - -**Update `_dot_kc_list()` in `lib/keychain-helpers.zsh:183-200`:** - -```zsh -_dot_kc_list() { - # Get all Keychain entries for flow-cli - local secrets=$(security dump-keychain 2>/dev/null | grep -A 10 "$_DOT_KEYCHAIN_SERVICE" | grep "0x00000007" | cut -d'"' -f 4 | sort | uniq) - - if [[ -z "$secrets" ]]; then - echo "" - echo "${FLOW_COLORS[muted]}No secrets found in Keychain${FLOW_COLORS[reset]}" - echo "" - return - fi - - echo "" - while IFS= read -r name; do - local status=$(_dot_check_sync_status "$name") - - case "$status" in - keychain-only) - echo "${FLOW_COLORS[info]}🔵 $name${FLOW_COLORS[reset]} Keychain only" - ;; - synced) - echo "${FLOW_COLORS[success]}🟢 $name${FLOW_COLORS[reset]} Synced to Bitwarden" - ;; - conflict) - echo "${FLOW_COLORS[warning]}🟡 $name${FLOW_COLORS[reset]} ⚠ Conflict detected" - ;; - missing) - echo "${FLOW_COLORS[error]}🔴 $name${FLOW_COLORS[reset]} ❌ Missing in Keychain" - ;; - esac - done <<< "$secrets" - echo "" -} -``` - -**Files to modify:** -- `lib/keychain-helpers.zsh:183-200` (_dot_kc_list) -- `lib/keychain-helpers.zsh` (add _dot_check_sync_status helper) - ---- - -### 1.4 Migration Script (2 hours) - -**Auto-detect v5.18.0 → v5.19.0 upgrade and migrate tokens:** - -Create new file: `lib/migration-v5.19.0.zsh` - -```zsh -#!/usr/bin/env zsh -# Migration: v5.18.0 → v5.19.0 (Keychain-authoritative) - -_dot_migrate_v5_19_0() { - local migration_flag="$HOME/.flow/.migrated-v5.19.0" - - # Check if already migrated - if [[ -f "$migration_flag" ]]; then - return 0 - fi - - echo "" - echo "${FLOW_COLORS[header]}╭───────────────────────────────────────────────────╮${FLOW_COLORS[reset]}" - echo "${FLOW_COLORS[header]}│${FLOW_COLORS[reset]} ${FLOW_COLORS[bold]}🔄 Migrating to v5.19.0${FLOW_COLORS[reset]} ${FLOW_COLORS[header]}│${FLOW_COLORS[reset]}" - echo "${FLOW_COLORS[header]}╰───────────────────────────────────────────────────╯${FLOW_COLORS[reset]}" - echo "" - - local migrated=0 - local skipped=0 - local errors=0 - - # Get all Keychain tokens - local secrets=$(security dump-keychain 2>/dev/null | grep -A 10 "$_DOT_KEYCHAIN_SERVICE" | grep "0x00000007" | cut -d'"' -f 4 | sort | uniq) - - while IFS= read -r name; do - [[ -z "$name" ]] && continue - - # Get current metadata - local metadata=$(security find-generic-password \ - -a "$name" \ - -s "$_DOT_KEYCHAIN_SERVICE" \ - -g 2>&1 | grep "note:" | sed 's/note: //') - - # Check if already migrated (has keychain_authoritative field) - if echo "$metadata" | jq -e '.keychain_authoritative' >/dev/null 2>&1; then - ((skipped++)) - continue - fi - - # Get token value - local token_value=$(security find-generic-password -a "$name" -s "$_DOT_KEYCHAIN_SERVICE" -w 2>/dev/null) - - if [[ -z "$token_value" ]]; then - ((errors++)) - continue - fi - - # Update metadata with new fields - local new_metadata=$(echo "$metadata" | jq -c '. + { - "keychain_authoritative": true, - "sync_bitwarden": true, - "migrated": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'" - }') - - # Update Keychain entry - security delete-generic-password -a "$name" -s "$_DOT_KEYCHAIN_SERVICE" 2>/dev/null - security add-generic-password \ - -a "$name" \ - -s "$_DOT_KEYCHAIN_SERVICE" \ - -w "$token_value" \ - -j "$new_metadata" \ - -U 2>/dev/null - - if [[ $? -eq 0 ]]; then - ((migrated++)) - else - ((errors++)) - fi - - done <<< "$secrets" - - # Save migration flag - mkdir -p "$(dirname "$migration_flag")" - echo "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$migration_flag" - - echo "" - echo "${FLOW_COLORS[success]}✅ Migration complete:${FLOW_COLORS[reset]}" - echo " Migrated: $migrated tokens" - echo " Skipped: $skipped tokens (already migrated)" - [[ $errors -gt 0 ]] && echo " ${FLOW_COLORS[error]}Errors: $errors tokens${FLOW_COLORS[reset]}" - echo "" - echo "${FLOW_COLORS[info]}What changed:${FLOW_COLORS[reset]}" - echo " • Existing tokens marked as Keychain-authoritative" - echo " • Both Keychain and Bitwarden copies preserved" - echo " • New tokens default to Keychain-only" - echo "" - echo "${FLOW_COLORS[muted]}Learn more: docs/guides/MIGRATION-V5.19.0-GUIDE.md${FLOW_COLORS[reset]}" - echo "" -} -``` - -**Trigger migration on first `dot secret list` or `dot token` command:** - -In `lib/dispatchers/dot-dispatcher.zsh`, add at top of `_dot_secret()`: - -```zsh -_dot_secret() { - # Auto-migrate on first use (v5.18.0 → v5.19.0) - if [[ -f "${0:A:h}/../lib/migration-v5.19.0.zsh" ]]; then - source "${0:A:h}/../lib/migration-v5.19.0.zsh" - _dot_migrate_v5_19_0 - fi - - # ... rest of function -} -``` - -**Files to create:** -- `lib/migration-v5.19.0.zsh` (new file) - -**Files to modify:** -- `lib/dispatchers/dot-dispatcher.zsh:1107` (_dot_secret function start) -- `flow.plugin.zsh` (source migration script) - ---- - -## Phase 2: Sync Management (8 hours) - -### 2.1 Sync Enable Command (2 hours) - -**Create:** `lib/sync-manager.zsh` - -```zsh -#!/usr/bin/env zsh -# Sync management for tokens (enable/disable Bitwarden sync) - -_dot_secret_sync_enable() { - local name=$1 - - # Bulk enable - if [[ "$name" == "--all" ]]; then - local tokens=($(_dot_list_keychain_only_tokens)) - echo "Enable Bitwarden sync for ${#tokens[@]} tokens?" - read -q "?Continue? [y/N] " - echo "" - [[ $? -ne 0 ]] && return 1 - - for token in "${tokens[@]}"; do - _dot_sync_single_token "$token" - done - return 0 - fi - - # Single token - _dot_sync_single_token "$name" -} - -_dot_sync_single_token() { - local name=$1 - - # Get current token value and metadata - local token_value=$(security find-generic-password -a "$name" -s "$_DOT_KEYCHAIN_SERVICE" -w 2>/dev/null) - local metadata=$(security find-generic-password -a "$name" -s "$_DOT_KEYCHAIN_SERVICE" -g 2>&1 | grep "note:" | sed 's/note: //') - - if [[ -z "$token_value" || -z "$metadata" ]]; then - _flow_log_error "Token not found: $name" - return 1 - fi - - # Check if already synced - local already_synced=$(echo "$metadata" | jq -r '.sync_bitwarden // false') - if [[ "$already_synced" == "true" ]]; then - _flow_log_warning "Already synced to Bitwarden: $name" - return 0 - fi - - # Update metadata - local new_metadata=$(echo "$metadata" | jq -c '. + { - "sync_bitwarden": true, - "updated": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'" - }') - - # Upload to Bitwarden - local username=$(echo "$metadata" | jq -r '.github_user // "user"') - local new_item=$(cat << EOF -{ - "organizationId": null, - "folderId": null, - "type": 1, - "name": "$name", - "notes": "$new_metadata", - "favorite": false, - "login": { - "username": "$username", - "password": "$token_value", - "totp": null - } -} -EOF - ) - - echo "$new_item" | bw encode | bw create item --session "$BW_SESSION" 2>/dev/null - bw sync --session "$BW_SESSION" 2>/dev/null - - if [[ $? -ne 0 ]]; then - _flow_log_warning "Bitwarden upload failed (but metadata updated)" - fi - - # Update Keychain metadata - security delete-generic-password -a "$name" -s "$_DOT_KEYCHAIN_SERVICE" 2>/dev/null - security add-generic-password \ - -a "$name" \ - -s "$_DOT_KEYCHAIN_SERVICE" \ - -w "$token_value" \ - -j "$new_metadata" \ - -U 2>/dev/null - - _flow_log_success "Enabled Bitwarden sync for: $name" -} - -_dot_list_keychain_only_tokens() { - local secrets=$(security dump-keychain 2>/dev/null | grep -A 10 "$_DOT_KEYCHAIN_SERVICE" | grep "0x00000007" | cut -d'"' -f 4 | sort | uniq) - - while IFS= read -r name; do - [[ -z "$name" ]] && continue - local status=$(_dot_check_sync_status "$name") - [[ "$status" == "keychain-only" ]] && echo "$name" - done <<< "$secrets" -} -``` - -**Add to dispatcher:** - -In `lib/dispatchers/dot-dispatcher.zsh`, add new case to `_dot_secret()`: - -```zsh -_dot_secret() { - # ... existing cases ... - - sync) - shift - source "${0:A:h}/../lib/sync-manager.zsh" - case "$1" in - enable) - shift - _dot_secret_sync_enable "$@" - ;; - disable) - shift - _dot_secret_sync_disable "$@" - ;; - status) - _dot_secret_sync_status - ;; - *) - echo "Usage: dot secret sync enable|disable|status [name]" - return 1 - ;; - esac - return - ;; -``` - -**Files to create:** -- `lib/sync-manager.zsh` (new file) - -**Files to modify:** -- `lib/dispatchers/dot-dispatcher.zsh` (add `sync` case) - ---- - -### 2.2 Sync Disable Command (1 hour) - -**Add to `lib/sync-manager.zsh`:** - -```zsh -_dot_secret_sync_disable() { - local name=$1 - - # Get current metadata - local metadata=$(security find-generic-password -a "$name" -s "$_DOT_KEYCHAIN_SERVICE" -g 2>&1 | grep "note:" | sed 's/note: //') - - # Update metadata (disable sync, but don't delete Bitwarden copy) - local new_metadata=$(echo "$metadata" | jq -c '. + { - "sync_bitwarden": false, - "updated": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'" - }') - - # Update Keychain - local token_value=$(security find-generic-password -a "$name" -s "$_DOT_KEYCHAIN_SERVICE" -w 2>/dev/null) - - security delete-generic-password -a "$name" -s "$_DOT_KEYCHAIN_SERVICE" 2>/dev/null - security add-generic-password \ - -a "$name" \ - -s "$_DOT_KEYCHAIN_SERVICE" \ - -w "$token_value" \ - -j "$new_metadata" \ - -U 2>/dev/null - - _flow_log_success "Disabled Bitwarden sync for: $name" - _flow_log_info "Bitwarden copy preserved (not deleted)" -} -``` - ---- - -### 2.3 Sync Status Command (1 hour) - -**Add to `lib/sync-manager.zsh`:** - -```zsh -_dot_secret_sync_status() { - local secrets=$(security dump-keychain 2>/dev/null | grep -A 10 "$_DOT_KEYCHAIN_SERVICE" | grep "0x00000007" | cut -d'"' -f 4 | sort | uniq) - - local synced=0 - local keychain_only=0 - local conflicts=0 - - local synced_list=() - local keychain_only_list=() - local conflict_list=() - - while IFS= read -r name; do - [[ -z "$name" ]] && continue - - local status=$(_dot_check_sync_status "$name") - - case "$status" in - synced) - ((synced++)) - synced_list+=("$name") - ;; - keychain-only) - ((keychain_only++)) - keychain_only_list+=("$name") - ;; - conflict) - ((conflicts++)) - conflict_list+=("$name") - ;; - esac - done <<< "$secrets" - - echo "" - echo "${FLOW_COLORS[header]}╭───────────────────────────────────────────────────╮${FLOW_COLORS[reset]}" - echo "${FLOW_COLORS[header]}│${FLOW_COLORS[reset]} ${FLOW_COLORS[bold]}Sync Status Summary${FLOW_COLORS[reset]} ${FLOW_COLORS[header]}│${FLOW_COLORS[reset]}" - echo "${FLOW_COLORS[header]}╰───────────────────────────────────────────────────╯${FLOW_COLORS[reset]}" - echo "" - - if [[ $synced -gt 0 ]]; then - echo "${FLOW_COLORS[success]}🟢 Synced ($synced tokens):${FLOW_COLORS[reset]}" - for token in "${synced_list[@]}"; do - echo " $token" - done - echo "" - fi - - if [[ $keychain_only -gt 0 ]]; then - echo "${FLOW_COLORS[info]}🔵 Keychain-only ($keychain_only tokens):${FLOW_COLORS[reset]}" - for token in "${keychain_only_list[@]}"; do - echo " $token" - done - echo "" - fi - - if [[ $conflicts -gt 0 ]]; then - echo "${FLOW_COLORS[warning]}🟡 Conflicts ($conflicts tokens):${FLOW_COLORS[reset]}" - for token in "${conflict_list[@]}"; do - echo " $token" - done - echo "" - echo "${FLOW_COLORS[muted]}Run 'dot secret reconcile ' to resolve conflicts${FLOW_COLORS[reset]}" - echo "" - fi -} -``` - ---- - -### 2.4 Conflict Resolution (4 hours) - -**Create:** `commands/secret-reconcile.zsh` - -```zsh -#!/usr/bin/env zsh -# Interactive conflict resolution for Keychain vs Bitwarden - -_dot_secret_reconcile() { - local name=$1 - - if [[ -z "$name" ]]; then - _flow_log_error "Usage: dot secret reconcile " - return 1 - fi - - # Get both copies - local kc_value=$(security find-generic-password -a "$name" -s "$_DOT_KEYCHAIN_SERVICE" -w 2>/dev/null) - local kc_metadata=$(security find-generic-password -a "$name" -s "$_DOT_KEYCHAIN_SERVICE" -g 2>&1 | grep "note:" | sed 's/note: //') - - local bw_value=$(bw get password "$name" --session $BW_SESSION 2>/dev/null) - local bw_notes=$(bw get notes "$name" --session $BW_SESSION 2>/dev/null) - - # Parse timestamps - local kc_updated=$(echo "$kc_metadata" | jq -r '.updated // .created') - local bw_updated=$(echo "$bw_notes" | jq -r '.updated // .created') - - # Show conflict UI - echo "" - echo "${FLOW_COLORS[header]}╭───────────────────────────────────────────────────╮${FLOW_COLORS[reset]}" - echo "${FLOW_COLORS[header]}│${FLOW_COLORS[reset]} ${FLOW_COLORS[warning]}⚠️ Conflict Detected${FLOW_COLORS[reset]} ${FLOW_COLORS[header]}│${FLOW_COLORS[reset]}" - echo "${FLOW_COLORS[header]}├───────────────────────────────────────────────────┤${FLOW_COLORS[reset]}" - echo "${FLOW_COLORS[header]}│${FLOW_COLORS[reset]} ${FLOW_COLORS[header]}│${FLOW_COLORS[reset]}" - echo "${FLOW_COLORS[header]}│${FLOW_COLORS[reset]} Token: $name$(printf '%*s' $((44 - ${#name})) '')${FLOW_COLORS[header]}│${FLOW_COLORS[reset]}" - echo "${FLOW_COLORS[header]}│${FLOW_COLORS[reset]} ${FLOW_COLORS[header]}│${FLOW_COLORS[reset]}" - echo "${FLOW_COLORS[header]}│${FLOW_COLORS[reset]} Keychain: ${FLOW_COLORS[header]}│${FLOW_COLORS[reset]}" - echo "${FLOW_COLORS[header]}│${FLOW_COLORS[reset]} Value: ${kc_value:0:10}****${kc_value:(-4)} (${#kc_value} chars)$(printf '%*s' $((13 - ${#kc_value})) '')${FLOW_COLORS[header]}│${FLOW_COLORS[reset]}" - echo "${FLOW_COLORS[header]}│${FLOW_COLORS[reset]} Updated: $kc_updated$(printf '%*s' $((33 - ${#kc_updated})) '')${FLOW_COLORS[header]}│${FLOW_COLORS[reset]}" - echo "${FLOW_COLORS[header]}│${FLOW_COLORS[reset]} ${FLOW_COLORS[header]}│${FLOW_COLORS[reset]}" - echo "${FLOW_COLORS[header]}│${FLOW_COLORS[reset]} Bitwarden: ${FLOW_COLORS[header]}│${FLOW_COLORS[reset]}" - echo "${FLOW_COLORS[header]}│${FLOW_COLORS[reset]} Value: ${bw_value:0:10}****${bw_value:(-4)} (${#bw_value} chars)$(printf '%*s' $((13 - ${#bw_value})) '')${FLOW_COLORS[header]}│${FLOW_COLORS[reset]}" - echo "${FLOW_COLORS[header]}│${FLOW_COLORS[reset]} Updated: $bw_updated$(printf '%*s' $((33 - ${#bw_updated})) '')${FLOW_COLORS[header]}│${FLOW_COLORS[reset]}" - echo "${FLOW_COLORS[header]}│${FLOW_COLORS[reset]} ${FLOW_COLORS[header]}│${FLOW_COLORS[reset]}" - echo "${FLOW_COLORS[header]}│${FLOW_COLORS[reset]} These values are DIFFERENT! ${FLOW_COLORS[header]}│${FLOW_COLORS[reset]}" - echo "${FLOW_COLORS[header]}│${FLOW_COLORS[reset]} ${FLOW_COLORS[header]}│${FLOW_COLORS[reset]}" - echo "${FLOW_COLORS[header]}╰───────────────────────────────────────────────────╯${FLOW_COLORS[reset]}" - echo "" - - # Get user choice - echo "Choose resolution:" - echo " 1. Use Keychain (newer)" - echo " 2. Use Bitwarden (older)" - echo " 3. Show full diff" - echo " 4. Delete both and re-add" - echo "" - - local choice - read "choice?Choice [1-4]: " - - case "$choice" in - 1) - # Use Keychain (overwrite Bitwarden) - local bw_item_id=$(bw get item "$name" --session $BW_SESSION | jq -r '.id') - - # Update Bitwarden item - local updated_item=$(cat << EOF -{ - "id": "$bw_item_id", - "organizationId": null, - "folderId": null, - "type": 1, - "name": "$name", - "notes": "$kc_metadata", - "favorite": false, - "login": { - "username": "$(echo "$kc_metadata" | jq -r '.github_user // "user"')", - "password": "$kc_value", - "totp": null - } -} -EOF - ) - - echo "$updated_item" | bw encode | bw edit item "$bw_item_id" --session "$BW_SESSION" >/dev/null - bw sync --session "$BW_SESSION" >/dev/null - - _flow_log_success "Resolved: Using Keychain value (synced to Bitwarden)" - ;; - - 2) - # Use Bitwarden (overwrite Keychain) - local bw_metadata=$(echo "$bw_notes" | jq -c) - - security delete-generic-password -a "$name" -s "$_DOT_KEYCHAIN_SERVICE" 2>/dev/null - security add-generic-password \ - -a "$name" \ - -s "$_DOT_KEYCHAIN_SERVICE" \ - -w "$bw_value" \ - -j "$bw_metadata" \ - -U 2>/dev/null - - _flow_log_success "Resolved: Using Bitwarden value (synced to Keychain)" - ;; - - 3) - # Show full diff - echo "" - echo "${FLOW_COLORS[section]}Keychain value:${FLOW_COLORS[reset]}" - echo "$kc_value" - echo "" - echo "${FLOW_COLORS[section]}Bitwarden value:${FLOW_COLORS[reset]}" - echo "$bw_value" - echo "" - # Re-prompt - _dot_secret_reconcile "$name" - ;; - - 4) - # Delete both - security delete-generic-password -a "$name" -s "$_DOT_KEYCHAIN_SERVICE" 2>/dev/null - bw delete item "$name" --session "$BW_SESSION" 2>/dev/null - bw sync --session "$BW_SESSION" >/dev/null - _flow_log_success "Deleted both copies. Run 'dot token ' to re-add." - ;; - - *) - _flow_log_error "Invalid choice. Aborted." - return 1 - ;; - esac -} -``` - -**Add to dispatcher:** - -In `lib/dispatchers/dot-dispatcher.zsh`: - -```zsh -_dot_secret() { - # ... existing cases ... - - reconcile) - shift - source "${0:A:h}/../../commands/secret-reconcile.zsh" - _dot_secret_reconcile "$@" - return - ;; -``` - -**Files to create:** -- `commands/secret-reconcile.zsh` (new file) - -**Files to modify:** -- `lib/dispatchers/dot-dispatcher.zsh` (add `reconcile` case) - ---- - -## Phase 3: Polish & Release (4 hours) - -### 3.1 Updated Token Rotation (2 hours) - -**Modify `_dot_token_rotate()` to always ask about Bitwarden sync:** - -In `lib/dispatchers/dot-dispatcher.zsh` (token rotation function): - -```zsh -_dot_token_rotate() { - # ... existing rotation logic (select token, create backup, get new token) ... - - # NEW: After getting new token, check sync preference - local old_metadata=$(get_metadata "$token_name-backup") - local was_synced=$(echo "$old_metadata" | jq -r '.sync_bitwarden // false') - - echo "" - echo "${FLOW_COLORS[section]}Bitwarden Sync${FLOW_COLORS[reset]}" - echo "" - - if [[ "$was_synced" == "true" ]]; then - echo "Old token was synced to Bitwarden." - echo "" - read -q "?Sync new token to Bitwarden? [Y/n] " sync_choice - echo "" - - local sync_new=true - if [[ "$sync_choice" == "n" || "$sync_choice" == "N" ]]; then - sync_new=false - _flow_log_info "New token will be Keychain-only" - else - _flow_log_info "New token will sync to Bitwarden" - fi - else - # Old token was Keychain-only - read -q "?Sync new token to Bitwarden? [y/N] " sync_choice - echo "" - - local sync_new=false - if [[ "$sync_choice" == "y" || "$sync_choice" == "Y" ]]; then - sync_new=true - _flow_log_info "New token will sync to Bitwarden" - else - _flow_log_info "New token will be Keychain-only" - fi - fi - - # Create new metadata with sync preference - local new_metadata=$(create_metadata "$token_type" "$sync_new") - - # Store in Keychain (always) - update_keychain "$token_name" "$new_token_value" "$new_metadata" - - # Store in Bitwarden (if enabled) - if [[ "$sync_new" == "true" ]]; then - bw_add_or_update "$token_name" "$new_token_value" "$new_metadata" - fi - - _flow_log_success "Token rotated successfully" -} -``` - -**Files to modify:** -- `lib/dispatchers/dot-dispatcher.zsh` (_dot_token_rotate function) - ---- - -### 3.2 Help Text Updates (1 hour) - -**Update `_dot_kc_help()` in `lib/keychain-helpers.zsh:356-385`:** - -```zsh -_dot_kc_help() { - cat < Store a secret (prompts for value) - dot secret get Retrieve a secret (or just: dot secret ) - dot secret Shortcut for 'get' - dot secret list List all stored secrets (color-coded) - dot secret delete Remove a secret - dot secret import Import from Bitwarden (one-time) - dot secret tutorial Interactive tutorial (10-15 min) - -${FLOW_COLORS[warning]}Sync Management (v5.19.0+):${FLOW_COLORS[reset]} - dot secret sync enable Enable Bitwarden sync for token - dot secret sync disable Disable Bitwarden sync - dot secret sync enable --all Bulk enable sync for all tokens - dot secret sync status Show sync status summary - dot secret reconcile Resolve Keychain/Bitwarden conflict - -${FLOW_COLORS[warning]}Examples:${FLOW_COLORS[reset]} - dot secret add github-token # Store GitHub token (Keychain-only) - dot token github --sync-bitwarden # Store with Bitwarden backup - dot secret list # See all secrets (color-coded) - dot secret sync enable github-token # Enable sync later - -${FLOW_COLORS[warning]}Usage in scripts:${FLOW_COLORS[reset]} - export GITHUB_TOKEN=\$(dot secret github-token) - gh auth login --with-token <<< \$(dot secret github-token) - -${FLOW_COLORS[warning]}Benefits:${FLOW_COLORS[reset]} - ${FLOW_COLORS[success]}•${FLOW_COLORS[reset]} Instant access (Keychain < 50ms) - ${FLOW_COLORS[success]}•${FLOW_COLORS[reset]} Touch ID / Apple Watch support - ${FLOW_COLORS[success]}•${FLOW_COLORS[reset]} Auto-locks with screen lock - ${FLOW_COLORS[success]}•${FLOW_COLORS[reset]} Optional Bitwarden cloud backup - -${FLOW_COLORS[muted]}Secrets stored in: macOS Keychain (service: $_DOT_KEYCHAIN_SERVICE)${FLOW_COLORS[reset]} -EOF -} -``` - ---- - -### 3.3 Documentation (1 hour) - -**Create migration guide:** `docs/guides/MIGRATION-V5.19.0-GUIDE.md` - -**Update existing docs:** -- `docs/reference/REFCARD-TOKEN-SECRETS.md` - Add sync commands -- `docs/guides/TOKEN-MANAGEMENT-COMPLETE.md` - Add sync management section -- `CHANGELOG.md` - Add v5.19.0 entry - ---- - -## Critical Files Summary - -| File | Type | Changes | -|------|------|---------| -| `lib/dispatchers/dot-dispatcher.zsh` | Modify | Metadata v2.2, default Keychain-only, sync case, reconcile case | -| `lib/keychain-helpers.zsh` | Modify | Enhanced list, check_sync_status helper, updated help | -| `lib/migration-v5.19.0.zsh` | New | Auto-migration on upgrade | -| `lib/sync-manager.zsh` | New | Sync enable/disable/status commands | -| `commands/secret-reconcile.zsh` | New | Interactive conflict resolution | -| `docs/guides/MIGRATION-V5.19.0-GUIDE.md` | New | Migration guide for users | - ---- - -## Verification Plan - -### 1. Unit Tests - -Create `tests/test-keychain-default.zsh`: - -```bash -# Test metadata v2.2 fields -# Test Keychain-only storage (no Bitwarden) -# Test --sync-bitwarden flag -# Test migration logic -# Test sync enable/disable -# Test conflict detection -``` - -### 2. Integration Tests - -Create `tests/e2e-keychain-default.zsh`: - -```bash -# Fresh install: Add token without flag → verify Keychain-only -# Fresh install: Add token with --sync-bitwarden → verify both backends -# Upgrade from v5.18.0: Verify migration message, check metadata -# Enable sync later: dot secret sync enable → verify Bitwarden copy created -# Conflict resolution: Manually create mismatch, run reconcile -``` - -### 3. Manual Testing Scenarios - -1. **Fresh install (v5.19.0):** - - Install v5.19.0 - - Run `dot token github` (no flag) - - Verify Keychain-only storage - - Run `dot secret list` → see blue indicator - - Run `dot secret sync enable github-token` - - Verify Bitwarden copy created - - Run `dot secret list` → see green indicator - -2. **Upgrade from v5.18.0:** - - Have existing dual-storage tokens - - Upgrade to v5.19.0 - - Run `dot secret list` - - See migration message - - Check metadata has `keychain_authoritative: true` - - Verify both copies still exist - -3. **Rotation with sync choice:** - - Have synced token - - Run `dot token rotate` - - See prompt "Sync new token to Bitwarden? [Y/n]" - - Choose Y → verify both backends updated - - Repeat with N → verify only Keychain updated - -4. **Conflict resolution:** - - Create token with sync enabled - - Manually update Bitwarden copy (via web UI) - - Run `dot secret list` → see yellow conflict indicator - - Run `dot secret reconcile ` - - See diff UI - - Choose option 1 (Use Keychain) - - Verify Bitwarden overwritten - - Run `dot secret list` → see green synced indicator - ---- - -## Rollback Plan - -If critical issues arise: - -```bash -# Revert to v5.18.0 -git checkout v5.18.0 -source flow.plugin.zsh - -# Or Homebrew -brew uninstall flow-cli -brew install flow-cli@5.18.0 -``` - -**Data safety:** -- Migration NEVER deletes data -- Both Keychain and Bitwarden copies preserved -- Metadata updates are additive - ---- - -## Success Criteria - -- [ ] Default `dot token github` stores in Keychain-only -- [ ] `--sync-bitwarden` flag enables dual storage -- [ ] Migration runs automatically on first use (v5.18→v5.19) -- [ ] `dot secret list` shows color-coded sync status -- [ ] `dot secret sync enable ` works -- [ ] `dot secret sync disable ` works -- [ ] `dot secret sync enable --all` works -- [ ] `dot secret sync status` shows summary -- [ ] `dot secret reconcile ` resolves conflicts -- [ ] Token rotation prompts for sync preference -- [ ] All existing tests pass -- [ ] New tests pass (unit + E2E) -- [ ] Documentation complete (migration guide, updated refs) - ---- - -## Timeline - -**Phase 1:** 6 hours (core changes) -**Phase 2:** 8 hours (sync management) -**Phase 3:** 4 hours (polish & release) - -**Total:** 18 hours (3 days @ 6 hours/day) - ---- - -**Ready to implement!** 🚀 diff --git a/docs/specs/SPEC-STAT-545-COMPREHENSIVE-PLAN.md b/docs/specs/SPEC-STAT-545-COMPREHENSIVE-PLAN.md deleted file mode 100644 index b022c82b0..000000000 --- a/docs/specs/SPEC-STAT-545-COMPREHENSIVE-PLAN.md +++ /dev/null @@ -1,1449 +0,0 @@ -# STAT 545 Comprehensive Migration & Workflow Plan - -**Version:** 1.0 -**Date:** January 11, 2026 -**Course:** STAT 545 - Design of Experiments -**Teaching Schedule:** Spring only (annual) - ---- - -## Executive Summary - -**Goal:** Migrate existing STAT 545 course to two-branch workflow with full automation (Tiers 1-3) - -**Key Decisions Made:** -- ✅ Two-branch strategy (draft + production) -- ✅ Spring-only teaching (simplified from semester model) -- ✅ Automation Tiers 1-3 (full quality assurance) -- ✅ Both .md and .qmd quiz formats -- ✅ .gitignore .qti.zip files (regenerate as needed) - -**Migration Scenarios:** -- **Scenario A:** In-place conversion (if git repo exists with history) -- **Scenario B:** Fresh start (if no git or minimal history) - -**Timeline:** 7-8 hours over 3 weeks - ---- - -## Table of Contents - -1. [Migration Scenarios](#migration-scenarios) -2. [Spring-Only Workflow](#spring-only-workflow) -3. [Branch Architecture](#branch-architecture) -4. [Daily Workflows](#daily-workflows) -5. [Semester Lifecycle](#semester-lifecycle) -6. [Automation Tiers](#automation-tiers) -7. [examark Integration](#examark-integration) -8. [Transition Day Protocol](#phase-3-transition-day-annual-event) -9. [Implementation Plan](#implementation-plan) -10. [Files to Generate](#files-to-generate) -11. [Next Steps](#next-steps) - ---- - -## Migration Scenarios - -### Scenario A: In-Place Conversion (Existing Git Repo) - -**When to Use:** -- Git repository already initialized -- Valuable commit history to preserve -- Multiple semesters of evolution tracked - -**Migration Steps:** - -```bash -# 1. Tag previous semester -git tag -a spring-2025-final -m "Spring 2025 Complete" -git push --tags - -# 2. Rename current branch to production -git branch -m main production # or master → production -git push -u origin production -git push origin --delete main - -# 3. Create draft branch -git checkout -b draft production -git push -u origin draft - -# 4. Add .gitignore for QTI files -cat >> .gitignore << 'EOF' - -# Examark outputs -*.qti.zip -*_qti/ -*_files/ -EOF - -git add .gitignore -git commit -m "chore: Ignore examark outputs" -git push origin draft - -# 5. Prepare for Spring 2026 -vim _quarto.yml # Update semester metadata -vim index.qmd # Update welcome message -git commit -m "chore: Prepare for Spring 2026" -git push origin draft -``` - -**Timeline:** 30 minutes - -**Result:** -- ✅ All history preserved -- ✅ Previous semester tagged -- ✅ Two-branch workflow established -- ✅ Ready for automation - ---- - -### Scenario B: Fresh Start (No Git or Minimal History) - -**When to Use:** -- No .git directory exists -- Minimal/unimportant commit history -- Want cleanest possible setup - -**Migration Steps:** - -```bash -# 1. Initialize repository -cd ~/projects/teaching/stat-545 -git init - -# 2. Create .gitignore -cat > .gitignore << 'EOF' -# Quarto outputs -_site/ -_freeze/ -.quarto/ - -# R -.Rproj.user/ -.Rhistory -.RData -.Ruserdata -renv/library/ -renv/staging/ - -# Examark outputs -*.qti.zip -*_qti/ -*_files/ - -# macOS -.DS_Store - -# IDE -.vscode/ -*.code-workspace - -# Temporary files -*.log -*.aux -*.toc -*.tex -EOF - -# 3. Initial commit (Spring 2025 final) -git add . -git commit -m "Initial commit: Spring 2025 final version" - -# 4. Rename to production -git branch -m main production - -# 5. Tag previous semester -git tag -a spring-2025-final -m "Spring 2025 Complete" - -# 6. Create draft branch -git checkout -b draft production - -# 7. Add remote (GitHub) -git remote add origin git@github.com:Data-Wise/doe.git -git push -u origin production -git push -u origin draft -git push --tags - -# 8. Prepare for Spring 2026 -vim _quarto.yml # Update metadata -vim index.qmd # Update welcome -git commit -m "chore: Prepare for Spring 2026" -git push origin draft -``` - -**Timeline:** 20 minutes - -**Result:** -- ✅ Clean git history starting now -- ✅ Previous work preserved and tagged -- ✅ Two-branch workflow from day one -- ✅ Ready for automation - ---- - -## Spring-Only Workflow - -### Annual Cycle - -**Spring-only teaching dramatically simplifies workflow:** - -``` -January-May: SPRING 2026 ACTIVE -├── High activity (daily commits) -├── Weekly deployments -├── draft → production (frequent merges) -└── Student-facing production branch - -June-December: BETWEEN SEMESTERS (9 months!) -├── Major content overhaul -├── All work on draft branch only -├── No deployment pressure -├── No parallel semester to manage -└── Prepare Spring 2027 - -January 10-12: TRANSITION DAY -├── Merge draft → production -├── Tag spring-2026-final -├── Tag spring-2027-start -└── Spring 2027 begins -``` - -**Key Simplifications:** - -✅ **No Future Semester Branch Needed** -- Never teaching Fall and Spring simultaneously -- Don't need spring-2027 branch during Spring 2026 -- All future work stays on draft - -✅ **Extended Prep Period (9 months)** -- Summer: Rest, reflect, plan (3 months) -- Fall: Intensive content creation (4 months) -- Winter: Final polish (1 month) -- No rush, no pressure - -✅ **Relaxed Between-Semester Work** -- Experiment freely -- Rewrite entire sections -- Change technology -- Test new tools - -✅ **Simplified Branch Strategy** -- During semester: draft → production (frequent) -- Between semesters: draft only (no merges) - ---- - -## Branch Architecture - -### Structure - -``` -draft # Your daily workspace -├── All commits here first -├── Push freely for backup -└── Experimental, unfinished content OK - -production # Students see this -├── Only updated via explicit merge -├── Stable, tested content only -└── Deploys automatically to GitHub Pages - -Tags (no branches) -├── spring-2025-final -├── spring-2026-final -└── spring-2027-final -``` - -### Mental Model - -**draft** = Unrestricted workspace -- Commit anything, anytime -- Push for backup (no deployment) -- Can break things safely - -**production** = Locked gate -- Only merge when ready -- Students see immediately -- Must be working content - -**Tags** = Semester snapshots -- Permanent historical record -- Can always recover old versions -- Branches deleted after tagging - ---- - -## Daily Workflows - -### Pattern 1: Quick Fix (90% of commits) - -**Scenario:** Student emails: "Week 8 has typo" - -**Workflow:** - -```bash -[2 minutes total] - -work stat-545 # Start session -git checkout draft # Ensure on draft -vim lectures/week-08_rcbd-blocking.qmd -# Fix: "variabiltiy" → "variability" - -git commit -m "fix: Correct typo in Week 8" -./scripts/quick-deploy.sh "Week 8 typo" -# → Script merges to production, pushes -# → GitHub Actions deploys in 2 min -# → Students see fix in 3 min total - -win "Fixed Week 8 typo" -``` - -**Key Points:** -- No need to switch branches manually -- One script call deploys -- Students see fix immediately -- Flow-cli tracks progress - ---- - -### Pattern 2: Weekly Assignment Prep - -**Scenario:** Friday, preparing Assignment 5 for Tuesday - -**Workflow:** - -```bash -[30 minutes prep, deploy Tuesday] - -# Friday - Create -work stat-545 -git checkout draft -claude /teaching:assignment "Incomplete Block Designs" "intermediate" -# → scholar generates assignment - -vim assignments/assignment-05.qmd -# → Edit scholar output, customize - -git commit -m "feat: Create Assignment 5" -quarto preview # Preview locally (not deployed) -win "Created Assignment 5" - -# Monday - Review -vim assignments/assignment-05.qmd -# → Minor tweaks -git commit -m "feat: Finalize Assignment 5" - -# Tuesday morning - Deploy -./scripts/quick-deploy.sh "Assignment 5" -win "Published Assignment 5" -``` - -**Key Points:** -- Work ahead safely (draft branch) -- Review before deployment -- Deploy exactly when ready -- No accidental early release - ---- - -### Pattern 3: Quiz Creation & Canvas Integration - -**Scenario:** Creating Week 12 quiz - -**Complete Workflow:** - -```bash -[45 minutes total] - -# Phase 1: Generate with scholar (10 min) -work stat-545 -git checkout draft -claude /teaching:quiz "Mixed Models ANOVA" -# → scholar generates 8-10 questions - -vim quizzes/week-12-quiz.md -# → Paste scholar output, customize -git commit -m "wip: Week 12 quiz draft" -win "Generated quiz questions" - -# Phase 2: Convert with examark (5 min) -cd ~/projects/apps/examark -examark ~/projects/teaching/stat-545/quizzes/week-12-quiz.md \ - -o ~/projects/teaching/stat-545/quizzes/week-12-quiz.qti.zip - -cd ~/projects/teaching/stat-545 -git add quizzes/week-12-quiz.qti.zip -git commit --amend --no-edit # Add .qti.zip to commit - -# Phase 3: Test in Canvas sandbox (15 min) -# → Upload to Canvas sandbox -# → Preview quiz -# → Take practice quiz -# → Verify answers correct -# → Iterate if needed - -# Phase 4: Deploy to production (5 min) -./scripts/quick-deploy.sh "Week 12 quiz" -# → Upload .qti.zip to real Canvas course -# → Set availability dates -win "Published Week 12 quiz" - -# Phase 5: Students access (automatic) -# → Quiz appears in Canvas at scheduled time -# → Website shows quiz in schedule -``` - -**Key Points:** -- scholar accelerates question generation -- examark handles Canvas format conversion -- Test in sandbox before production -- Both .md and .qti.zip tracked in git -- .qti.zip regenerated as needed (in .gitignore) - ---- - -### Pattern 4: Exam Preparation (High Stakes) - -**Scenario:** Creating final exam - -**Extended Workflow:** - -```bash -[2-3 hours spread over several days] - -# Week 1: Content generation -git checkout draft -claude /teaching:exam "cumulative" "covering weeks 1-15" -vim exams/final-exam.md -# → Customize, add specific problems -git commit -m "wip: Final exam initial draft" -win "Started final exam" - -# Week 2: Iteration & review -vim exams/final-exam.md # Refine questions -git commit -m "wip: Refine final exam" -examark exams/final-exam.md -o exams/final-exam.qti.zip -# → Import to Canvas sandbox -# → Take practice exam -# → Fix issues -git commit -m "wip: Final exam v2" -win "Refined final exam" - -# Week 3: Final version -vim exams/final-exam.md # Last tweaks -git commit -m "feat: Finalize exam" -examark exams/final-exam.md -o exams/final-exam.qti.zip -git add exams/final-exam.* -git commit --amend --no-edit - -# Finals Week: Deploy -./scripts/quick-deploy.sh "Final exam" -# → Upload to Canvas -# → Set exam window (e.g., Dec 15, 2-5pm) -# → Lock settings -win "Published final exam" - -# Post-Exam: Solutions (optional) -vim exams/final-exam-solutions.qmd -git commit -m "docs: Final exam solutions" -# → Render to PDF -# → Store in _private/ (not deployed to students) -``` - -**Security Note:** -- Keep exams in draft branch only (don't merge until exam day) -- Use _private/ folder for sensitive content (not rendered) -- Consider storing exams outside git entirely for max security - ---- - -## Semester Lifecycle - -### Phase 1: Active Semester (January-May) - -**Week 1-2: Syllabus Week** - -``` -Deploy Frequency: High (2-3 times/week) -Activities: - - Daily quick fixes - - Syllabus updates - - Assignment releases - -Branch Usage: draft → production (frequent) -``` - -**Week 3-13: Core Content** - -``` -Deploy Frequency: Moderate (1-2 times/week) -Activities: - - Weekly lecture updates - - Quiz releases - - Assignment feedback - -Branch Usage: draft → production (regular) -``` - -**Week 14-16: Final Exam Period** - -``` -Deploy Frequency: Low (critical fixes only) -Activities: - - Freeze production - - Exam materials in draft only - -Branch Usage: draft only (production frozen) -``` - ---- - -### Phase 2: Between Semesters (June-December) - -**Summer Relaxation (June-August) - 3 months** - -``` -Focus: Rest, reflect, plan -Pressure: Zero - -Optional: - - Review course evaluations - - Identify content gaps - - Research new topics - - Update technology stack - -Branch: draft only (experimental commits) -``` - -**Fall Intensive (September-December) - 4 months** - -``` -Focus: Build Spring 2027 content - -Week 1-4 (Sep): Major overhaul - - Rewrite weak lectures - - Add new datasets/examples - - Update R code packages - -Week 5-8 (Oct): Assessment creation - - Generate all quizzes (scholar) - - Create exams (scholar + examark) - - Test in Canvas sandbox - -Week 9-12 (Nov): Polish and review - - Review all lectures - - Update syllabus/schedule - - Generate assignments - -Week 13-16 (Dec): Final prep - - Test complete workflow - - Fix broken links/images - - Prepare deployment - -Branch: draft only (100+ commits) -Pressure: Self-imposed deadlines -``` - -**Winter Break (December-January) - 1 month** - -``` -Focus: Final checks - -Activities: - - Final review of content - - Test quarto render - - Verify Canvas materials ready - - Mental preparation - -Branch: draft (final commits) -Transition: January 10-12 (3 days before semester) -``` - ---- - -### Phase 3: Transition Day (Annual Event) - -**January 10, 2027 - Three Days Before Spring 2027** - -**Morning (9am-12pm): Final Review** - -```bash -work stat-545 -git checkout draft - -quarto render # Final build check -# Review _site/ directory -# Test all links -# Verify images load - -vim lectures/week-01_intro.qmd # Last-minute tweaks -git commit -m "fix: Final pre-semester tweaks" - -git log production..draft --oneline | wc -l -# Example: 127 commits since last semester -``` - -**Afternoon (1pm-3pm): Archival** - -```bash -# Archive Spring 2026 -git checkout production -git tag -a spring-2026-final -m "Spring 2026 Complete - May 2026" -git push --tags - -# Create GitHub Release -gh release create spring-2026-final \ - --title "Spring 2026 - Final Version" \ - --notes "Complete course materials from Spring 2026. - - Stats: - - Students: 38 - - Lectures: 15 weeks - - Assignments: 8 - - Quizzes: 12 - - Exams: 2 - - Major Changes: - - Added Bayesian methods lecture - - Updated all R code to tidyverse - - New blocking datasets - - Improved quiz quality" -``` - -**Afternoon (3pm-4pm): Transition** - -```bash -# Replace production with draft -git checkout production -git reset --hard draft -git push --force origin production - -# Tag new semester start -git tag -a spring-2027-start -m "Spring 2027 begins" -git push --tags - -# Sync draft to production -git checkout draft -git reset --hard production -git push --force origin draft - -# Confirm identical -git log production..draft # Should be empty -``` - -**Evening (4pm-5pm): Verification** - -```bash -# Wait for GitHub Actions (2-3 min) - -# Verify deployment -curl -I https://data-wise.github.io/doe/ -# → HTTP 200 OK - -# Spot check in browser: -# - Homepage shows Spring 2027 -# - Syllabus has correct dates -# - Week 1 lecture loads -# - Images display - -# If issues: Quick fix -git checkout production -vim index.qmd -git commit -m "fix: Deployment issue" -git push origin production -``` - -**Evening (5pm): Celebrate** - -```bash -win "Successfully transitioned to Spring 2027" -finish "Spring 2027 deployment complete" -yay - -# Done! Relax until January 13. -``` - ---- - -## Automation Tiers - -### Tier 1: Scripted Helpers - -**Scripts to Create:** - -**1. quick-deploy.sh** - Deploy single commit - -```bash -#!/bin/bash -# Deploy current draft commit to production immediately - -DESCRIPTION="$1" -CURRENT_COMMIT=$(git rev-parse HEAD) - -git checkout production -git merge $CURRENT_COMMIT --no-ff -m "📦 $DESCRIPTION" -git push origin production -git checkout draft - -echo "✅ Deployed: $DESCRIPTION" -``` - -**Usage:** - -```bash -./scripts/quick-deploy.sh "Week 8 typo fix" -``` - -**2. publish-batch.sh** - Deploy multiple commits - -```bash -#!/bin/bash -# Show unpublished commits, confirm, then deploy all - -echo "📊 Changes to publish:" -git log production..draft --oneline - -echo "Publish these? (y/n)" -read response - -if [[ "$response" == "y" ]]; then - git checkout production - git merge draft --no-ff -m "📦 Batch: $(date +%Y-%m-%d)" - git push origin production - git checkout draft - echo "✅ Published to students" -fi -``` - -**Usage:** - -```bash -./scripts/publish-batch.sh -``` - -**3. quiz-to-qti.sh** - examark wrapper with auto-commit - -```bash -#!/bin/bash -# Generate .qti.zip from .md and commit both - -QUIZ_MD="$1" -QUIZ_NAME=$(basename "$QUIZ_MD" .md) - -examark "$QUIZ_MD" -o "${QUIZ_MD%.md}.qti.zip" - -git add "$QUIZ_MD" "${QUIZ_MD%.md}.qti.zip" -git commit -m "feat: Add $QUIZ_NAME" - -echo "✅ Quiz ready for deployment" -echo "Next: ./scripts/quick-deploy.sh '$QUIZ_NAME'" -``` - -**Usage:** - -```bash -./scripts/quiz-to-qti.sh quizzes/week-12-quiz.md -``` - -**4. semester-archive.sh** - Annual transition helper - -```bash -#!/bin/bash -# Guide through semester transition - -SEMESTER_TAG="$1" -NEXT_SEMESTER="$2" - -echo "📦 Archiving $SEMESTER_TAG..." - -git checkout production -git tag -a "$SEMESTER_TAG" -m "Semester complete" -git push --tags - -echo "✅ Tagged as $SEMESTER_TAG" -echo "" -echo "Next steps:" -echo "1. Review draft branch" -echo "2. git reset --hard draft on production" -echo "3. Force push production" -echo "4. Tag new semester start" -``` - -**Usage:** - -```bash -./scripts/semester-archive.sh "spring-2026-final" "Spring 2027" -``` - -**Time Savings:** -- Quick fix: 2 min (was 5 min) → 60% faster -- Batch publish: 3 min (was 10 min) → 70% faster -- Quiz creation: 10 min (was 15 min) → 33% faster - -**Implementation:** 1 hour (write + test) - ---- - -### Tier 2: flow-cli Integration - -**Configuration File: `.flow/config.yml`** - -```yaml -project: - name: "STAT 545 - Design of Experiments" - type: teaching - course_code: STAT-545 - default_branch: draft - - versioning: - strategy: two-branch - branches: - work: draft - published: production - -shortcuts: - # Content creation - assignment: "claude /teaching:assignment" - quiz: "claude /teaching:quiz" - exam: "claude /teaching:exam" - - # Conversion & deployment - qti: "scripts/quiz-to-qti.sh" - deploy: "scripts/quick-deploy.sh" - publish: "scripts/publish-batch.sh" - - # Session helpers - current: "git checkout production && quarto preview --port 4321" - draft-preview: "git checkout draft && quarto preview" - -automation: - auto_push_draft: true # Auto-push draft for backup - remind_canvas_upload: true # Reminder after qti generation - -hooks: - pre_deploy: "quarto render" # Ensure site builds - post_deploy: "echo '✅ https://data-wise.github.io/doe/'" - -goals: - daily_commits: 2 # Target: 2 commits/day - -reminders: - - "Students see: https://data-wise.github.io/doe/" - - "Publish weekly on Fridays" -``` - -**Custom Aliases: `~/.zshrc`** - -```bash -# STAT 545 shortcuts -alias s545="work stat-545" -alias s545q="work stat-545 && flow qti" -alias s545d="work stat-545 && flow deploy" -alias s545p="work stat-545 && flow publish" - -# Quick git status -alias s545s="cd ~/projects/teaching/stat-545 && git status" - -# View unpublished commits -alias s545u="cd ~/projects/teaching/stat-545 && git log production..draft --oneline" -``` - -**Enhanced Workflow:** - -```bash -# All one-liners: -s545 # Start session -claude /teaching:quiz "Topic" # Generate quiz -flow qti quizzes/week-12-quiz.md # Convert to QTI -flow deploy "Week 12 quiz" # Deploy to students -``` - -**Benefits:** -- ✅ ADHD-optimized (minimal steps) -- ✅ Context-aware shortcuts -- ✅ Auto-backup (auto_push_draft) -- ✅ Progress tracking (wins, goals) -- ✅ Reminders when needed - -**Implementation:** 2 hours (config + aliases + testing) - ---- - -### Tier 3: GitHub Actions CI/CD - -**Workflow 1: Production Deployment** - -**File: `.github/workflows/deploy.yml`** - -```yaml -name: Deploy to Students - -on: - push: - branches: [production] - workflow_dispatch: - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Setup Quarto - uses: quarto-dev/quarto-actions/setup@v2 - with: - version: 1.4.550 - - - name: Setup R - uses: r-lib/actions/setup-r@v2 - with: - r-version: '4.3.2' - - - name: Restore renv packages - uses: r-lib/actions/setup-renv@v2 - - - name: Render site - run: quarto render - - - name: Deploy to GitHub Pages - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./_site - cname: data-wise.github.io -``` - -**Workflow 2: Quality Checks** - -**File: `.github/workflows/test.yml`** - -```yaml -name: Quality Checks - -on: - push: - branches: [draft, production] - pull_request: - -jobs: - validate: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Setup Quarto - uses: quarto-dev/quarto-actions/setup@v2 - - - name: Check Quarto syntax - run: quarto check - - - name: Render site (dry run) - run: quarto render --dry-run - - - name: Setup Node (for examark) - uses: actions/setup-node@v3 - with: - node-version: '18' - - - name: Validate quiz syntax - run: | - npm install -g examark - for quiz in quizzes/*.md; do - [ -f "$quiz" ] && examark check "$quiz" - done - - - name: Check for broken links - run: | - quarto render - npm install -g markdown-link-check - find _site -name "*.html" -exec markdown-link-check {} \; || true - - - name: R package dependencies - run: Rscript -e "renv::status()" -``` - -**Workflow 3: Draft Preview (Optional)** - -**File: `.github/workflows/preview-draft.yml`** - -```yaml -name: Preview Draft Branch - -on: - push: - branches: [draft] - workflow_dispatch: - -jobs: - preview: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Setup Quarto - uses: quarto-dev/quarto-actions/setup@v2 - - - name: Setup R - uses: r-lib/actions/setup-r@v2 - - - name: Restore renv - uses: r-lib/actions/setup-renv@v2 - - - name: Render site - run: quarto render - - - name: Deploy to preview - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./_site - destination_dir: preview -``` - -**Benefits:** -- ✅ Catches errors before students see them -- ✅ Validates quiz syntax automatically -- ✅ Checks for broken links -- ✅ Ensures R packages consistent -- ✅ Runs on every push (draft or production) -- ✅ Optional draft preview at /preview URL - -**Time Impact:** -- No manual steps removed -- Same workflow, higher quality -- Auto-detects errors -- Prevents broken deployments - -**Implementation:** 3 hours (write workflows + test + debug) - ---- - -## examark Integration - -### File Organization - -**Directory Structure:** - -``` -quizzes/ -├── week-08-quiz.md # Source (version controlled) -├── week-08-quiz.qti.zip # Generated (in .gitignore) -├── week-09-quiz.md -├── week-09-quiz.qti.zip -└── ... - -exams/ -├── midterm-exam.md -├── midterm-exam.qti.zip -├── final-exam.md -└── final-exam.qti.zip -``` - -**Why .gitignore .qti.zip files:** -- ✅ Binary files cause merge conflicts -- ✅ Easy to regenerate from .md source -- ✅ Keeps git history clean -- ✅ Reduces repository size -- ❌ Must regenerate before Canvas upload - -**Regeneration Workflow:** - -```bash -# When switching machines or after clone -cd ~/projects/apps/examark - -# Regenerate all quizzes -for quiz in ~/projects/teaching/stat-545/quizzes/*.md; do - examark "$quiz" -o "${quiz%.md}.qti.zip" -done - -# Regenerate all exams -for exam in ~/projects/teaching/stat-545/exams/*.md; do - examark "$exam" -o "${exam%.md}.qti.zip" -done -``` - ---- - -### Two Workflows: Static vs Dynamic - -**Workflow A: Static Markdown Quizzes** - -``` -File: quizzes/week-08-quiz.md (plain Markdown) - -Process: - .md → examark → .qti.zip → Canvas - -Advantages: - - Simple, no R execution - - Fast to write and edit - - examark processes directly - -Use case: Theory questions, conceptual quizzes -``` - -**Workflow B: Dynamic Quarto Quizzes** - -``` -File: quizzes/week-08-quiz.qmd (Quarto + R) - -Process: - .qmd → quarto render → .md → examark → .qti.zip → Canvas - -Advantages: - - Generate questions from R code - - Randomize numbers/datasets - - Include plots/figures - - Compute correct answers programmatically - -Example: ---- -title: "Week 8 Quiz" ---- - -```{r} -#| echo: false -set.seed(42) -data <- rnorm(20, mean=100, sd=15) -correct_mean <- round(mean(data), 2) -``` - -1. [Num] What is the mean? [2pts] - Correct: `r correct_mean` ± 0.5 - -Use case: Stats quizzes with calculations - -``` - -**Recommendation:** -- Start with Workflow A (simpler) -- Add Workflow B for specific needs -- Keep both options available - ---- - -### Integration with scholar + examark + flow-cli - -**Complete Content Pipeline:** - -``` - -┌─────────────┐ -│ scholar │ Generate content with AI -│ /teaching:* │ (assignments, quizzes, exams) -└──────┬──────┘ - │ .md file - ↓ -┌─────────────┐ -│ Manual │ Edit, customize -│ Editing │ (add problems, adjust) -└──────┬──────┘ - │ .md file - ↓ -┌─────────────┐ -│ examark │ Convert to Canvas format -│ (Quarto) │ (.qti.zip for import) -└──────┬──────┘ - │ .qti.zip - ↓ -┌─────────────┐ -│ Canvas │ Upload and configure -│ Manual │ (set dates, attempts) -└──────┬──────┘ - │ - ↓ -┌─────────────┐ -│ flow-cli │ Track progress -│ win │ Celebrate completion -└─────────────┘ - -``` - -**Example Session:** -```bash -# Morning: Create Assignment 6 -work stat-545 -git checkout draft -claude /teaching:assignment "ANCOVA" "advanced" -vim assignments/assignment-06.qmd # Customize -git commit -m "feat: Assignment 6" -win "Created Assignment 6" - -# Afternoon: Create Week 13 quiz -claude /teaching:quiz "Nested Designs" -vim quizzes/week-13-quiz.md # Edit -./scripts/quiz-to-qti.sh quizzes/week-13-quiz.md -win "Created Week 13 quiz" - -# End of day -yay # See today's wins (2) -finish "Created Assignment 6 + Week 13 quiz" - -# Next day: Deploy both -work stat-545 -./scripts/publish-batch.sh # Shows 2 commits -# → Confirm? y → Deploys to students -win "Published Assignment 6 and Week 13 quiz" -``` - ---- - -## Implementation Plan - -### Week 1: Foundation (4 hours) - -**Day 1 (1 hour): Repository Migration** -- [ ] Determine migration scenario (A or B) -- [ ] Execute migration steps -- [ ] Verify production + draft branches exist -- [ ] Tag spring-2025-final -- [ ] Push to GitHub - -**Day 2 (1 hour): Tier 1 Scripts** -- [ ] Create scripts/ directory -- [ ] Write quick-deploy.sh -- [ ] Write publish-batch.sh -- [ ] Write quiz-to-qti.sh -- [ ] Write semester-archive.sh -- [ ] Make all executable (chmod +x) -- [ ] Test quick-deploy.sh (dry run) - -**Day 3 (1 hour): Tier 2 Integration** -- [ ] Create .flow/ directory -- [ ] Write .flow/config.yml -- [ ] Add custom aliases to ~/.zshrc -- [ ] Source ~/.zshrc -- [ ] Test: s545, s545q, s545d -- [ ] Verify flow-cli shortcuts work - -**Day 4 (1 hour): Testing & Validation** -- [ ] Make test commit on draft -- [ ] Test quick-deploy.sh -- [ ] Verify students see update -- [ ] Test publish-batch.sh -- [ ] Document any issues -- [ ] Refine scripts if needed - ---- - -### Week 2: Automation (3 hours) - -**Day 1 (2 hours): Tier 3 GitHub Actions** -- [ ] Create .github/workflows/ directory -- [ ] Write deploy.yml -- [ ] Write test.yml -- [ ] Write preview-draft.yml (optional) -- [ ] Push workflows to draft -- [ ] Merge to production (trigger deploy) -- [ ] Verify deployment successful -- [ ] Check GitHub Actions logs - -**Day 2 (1 hour): End-to-End Testing** -- [ ] Create test quiz with scholar -- [ ] Convert with examark -- [ ] Commit to draft -- [ ] Verify CI passes -- [ ] Deploy with quick-deploy.sh -- [ ] Verify deployment -- [ ] Upload to Canvas sandbox -- [ ] Full workflow validation - -**Day 3 (bonus): Documentation** -- [ ] Write WORKFLOW.md -- [ ] Update README.md -- [ ] Document migration steps -- [ ] Create quick reference card - ---- - -### Week 3: Production Use - -**Daily:** -- [ ] Use workflow for real work -- [ ] Log friction points -- [ ] Note improvement ideas -- [ ] Track time savings - -**End of Week:** -- [ ] Review lessons learned -- [ ] Adjust scripts if needed -- [ ] Finalize process -- [ ] Celebrate success! - ---- - -## Files to Generate - -### Tier 1: Scripts (4 files) - -``` -scripts/ -├── quick-deploy.sh # Deploy single commit (50 lines) -├── publish-batch.sh # Deploy multiple commits (40 lines) -├── quiz-to-qti.sh # examark wrapper (30 lines) -└── semester-archive.sh # Annual transition helper (60 lines) -``` - -### Tier 2: Configuration (2 files) - -``` -.flow/ -└── config.yml # flow-cli configuration (50 lines) - -~/.zshrc additions # Custom aliases (10 lines) -``` - -### Tier 3: GitHub Actions (3 files) - -``` -.github/workflows/ -├── deploy.yml # Production deployment (40 lines) -├── test.yml # Quality checks (60 lines) -└── preview-draft.yml # Draft preview (optional, 35 lines) -``` - -### Documentation (3 files) - -``` -WORKFLOW.md # Daily usage guide (500 lines) -MIGRATION-COMPLETE.md # Migration record (100 lines) -.gitignore # Updated exclusions (30 lines) -``` - -**Total: 12 files + documentation** - ---- - -## Next Steps - -### Critical Decision Needed - -**To finalize migration path, please run:** - -```bash -cd ~/projects/teaching/stat-545 - -# Check if git exists -ls -la .git - -# If git exists, check history -git log --oneline -20 -git branch -a -git tag -l - -# Check current content -ls -la lectures/ | head -10 -ls -la quizzes/ | head -10 -``` - -**Share output to determine:** -- Scenario A (preserve history) vs. Scenario B (fresh start) -- What semester is currently in the repo -- If multiple past semesters need tagging - ---- - -### Additional Questions - -**1. GitHub Repository** -- Does github.com/Data-Wise/doe exist? -- If yes, empty or has content? -- Do you have admin access? - -**2. Current Semester Content** -- What semester is in ~/projects/teaching/stat-545/? -- Spring 2025? Older? -- Complete or in-progress? - -**3. Previous Semesters** -- Taught this course before Spring 2025? -- Materials archived elsewhere? -- Should we tag multiple past semesters? - -**4. Timeline** -- When do you want this operational? -- Actively working on Spring 2026 content now? -- Or waiting until summer/fall? - ---- - -### Ready to Generate Files - -**Once you confirm migration scenario, I'll generate:** - -✅ All 4 Tier 1 scripts (production-ready) -✅ Tier 2 flow-cli configuration -✅ All 3 Tier 3 GitHub Actions workflows -✅ Complete documentation -✅ Step-by-step migration guide -✅ Testing instructions - -**With:** -- Detailed inline comments -- Error handling -- Progress indicators -- Rollback procedures -- ADHD-friendly formatting - -**Estimated generation time:** 15-30 minutes - -**Estimated execution time:** 2 hours for complete setup - ---- - -## Success Metrics - -### Quantitative Targets - -| Metric | Baseline | After Tier 1-3 | -|--------|----------|----------------| -| Deploy time | 5 min | 2 min | -| Quiz creation | 45 min | 15 min | -| Weekly prep | 3 hours | 1.5 hours | -| Errors pre-deploy | 0% | 80% caught | -| Student downtime | <1 min | 0 min | -| Commits/week | 10 | 20+ | - -### Qualitative Goals - -**ADHD-Friendly:** -- ✅ Clear mental model (draft = work, production = live) -- ✅ Minimal context switching -- ✅ Visible progress (flow-cli wins) -- ✅ Low cognitive load (scripts handle complexity) -- ✅ Quick feedback loops - -**Content Quality:** -- ✅ Version controlled -- ✅ Peer reviewable -- ✅ Reproducible -- ✅ Portable - -**Student Experience:** -- ✅ Stable URLs -- ✅ Fast fixes (same-day) -- ✅ Consistent quality -- ✅ Accessible archives - ---- - -## Contact & Support - -**Questions or Issues?** -- Review this plan document -- Check WORKFLOW.md (after generation) -- Consult individual script comments - -**Ready to proceed when you confirm:** -1. Migration scenario (A or B) -2. Answers to additional questions -3. Preferred file generation approach - ---- - -**End of Comprehensive Plan** - -**Version:** 1.0 -**Last Updated:** January 11, 2026 -**Status:** Awaiting migration scenario confirmation diff --git a/docs/specs/SPEC-TEACH-GENERATION-UX.md b/docs/specs/SPEC-TEACH-GENERATION-UX.md deleted file mode 100644 index f536e568f..000000000 --- a/docs/specs/SPEC-TEACH-GENERATION-UX.md +++ /dev/null @@ -1,649 +0,0 @@ -# Teaching Material Generation CLI UX Design - -**Version:** 1.0.0 -**Author:** Claude (UX/UI Designer) -**Date:** January 17, 2026 -**Status:** Design Specification - ---- - -## Executive Summary - -This document specifies the CLI user experience for the `teach` dispatcher's AI-powered content generation commands. The design prioritizes ADHD-friendly workflows with clear feedback, smart defaults, and minimal cognitive load. - ---- - -## 1. Command Syntax Design - -### 1.1 Priority Command Groups - -Based on usage patterns, commands are grouped by workflow priority: - -| Priority | Commands | Use Case | -|----------|----------|----------| -| **P1** | `slides`, `lecture` | Presentation prep (often paired) | -| **P2** | `quiz`, `exam` | Assessment creation | -| **P3** | `feedback`, `assignment` | Student work & grading | - -### 1.2 Command Syntax Patterns - -All generation commands follow a consistent pattern: - -``` -teach [topic] [options] -``` - -**Core Pattern:** - -```bash -teach "Topic" # Basic: auto-detect week, default format -teach "Topic" -i # Interactive: step-by-step prompts -teach "Topic" --revise # Revise: improve existing file -teach # Smart default: current week's topic -``` - -### 1.3 Detailed Command Syntax - -#### Slides + Lecture (P1) - -```bash -# SLIDES - Generate presentation slides -teach slides "Topic" # Basic slides -teach slides "Topic" --format qmd # Quarto format (default) -teach slides "Topic" --format md # Markdown format -teach slides "Topic" --theme academic # Theme: academic|minimal|modern -teach slides "Topic" --from-lecture file # Generate from lecture notes -teach slides -i # Interactive mode -teach slides --revise slides/week03.qmd # Revise existing slides - -# LECTURE - Generate lecture notes -teach lecture "Topic" # Basic lecture notes -teach lecture "Topic" --outline # Outline only (quick) -teach lecture "Topic" --notes # Include speaker notes -teach lecture "Topic" --from-plan week03 # From lesson plan -teach lecture -i # Interactive mode -teach lecture --revise lectures/week03.qmd # Revise existing -``` - -#### Quiz + Exam (P2) - -```bash -# QUIZ - Generate quiz questions -teach quiz "Topic" # Default: 10 questions -teach quiz "Topic" -n 15 # Custom question count -teach quiz "Topic" --time 20 # 20 minute time limit -teach quiz "Topic" --types mc,tf # Question types: mc,tf,sa,fill -teach quiz -i # Interactive mode -teach quiz --revise quizzes/ch05.qmd # Revise existing - -# EXAM - Generate exam questions -teach exam "Topic" # Default: 25 questions, 90 min -teach exam "Topic" -n 30 # Custom question count -teach exam "Topic" --duration 120 # 2 hour exam -teach exam "Topic" --types mc,sa,essay # Mixed types -teach exam "Topic" --difficulty mixed # Difficulty: easy|medium|hard|mixed -teach exam -i # Interactive mode -teach exam --revise exams/midterm1.qmd # Revise existing -``` - -#### Feedback + Assignment (P3) - -```bash -# FEEDBACK - Generate student feedback -teach feedback "submission.pdf" # Analyze and provide feedback -teach feedback "work/" --batch # Batch feedback for directory -teach feedback "work.pdf" --rubric rubric.yml # Use specific rubric -teach feedback "work.pdf" --tone supportive # Tone: supportive|direct|detailed -teach feedback -i # Interactive mode - -# ASSIGNMENT - Generate homework assignment -teach assignment "Topic" # Basic assignment -teach assignment "Topic" --due 2026-01-24 # With due date -teach assignment "Topic" --points 100 # Point value -teach assignment "Topic" --parts 3 # Multi-part assignment -teach assignment -i # Interactive mode -teach assignment --revise hw/hw03.qmd # Revise existing -``` - -### 1.4 Universal Flags - -These flags work with ALL generation commands: - -```bash ---format # Output: qmd (default), md, latex ---output # Custom output path ---context # Include full course context (larger prompt) ---dry-run # Preview without saving ---verbose # Show AI prompt being sent --i, --interactive # Interactive mode (step-by-step) ---revise # Revise existing file ---week # Override week number (default: auto-detect) -``` - -### 1.5 Smart Defaults - -When no topic is provided, the system auto-detects: - -```bash -teach slides # Auto: Current week's topic from schedule -teach quiz # Auto: Topics covered so far this week -teach exam # Auto: All topics in current unit -``` - -**Auto-detection logic:** -1. Read `teach-config.yml` for semester dates -2. Calculate current week number -3. Look up topic in `schedule.yml` or `_schedule.yml` -4. Fall back to prompt if not found - ---- - -## 2. Progress Indicator Design - -### 2.1 Three-Phase Progress Display - -AI generation has 3 distinct phases: - -``` -Phase 1: Preparation (~1-2s) -Phase 2: AI Generation (~5-25s) -Phase 3: Post-processing (~1-3s) -``` - -### 2.2 Progress States - -**State 1: Starting (Preparation)** - -``` - Preparing slides for "Regression Analysis"... - Context: Week 8 of 15 | STAT 440 -``` - -**State 2: Generating (AI Working)** - -``` - Generating slides (estimated ~20-30s)... - [===============> ] 60% | 12s elapsed - Context: Week 8 of 15 | STAT 440 -``` - -**State 3: Finishing (Post-processing)** - -``` - Finalizing content... - Saving to slides/week08-regression.qmd -``` - -### 2.3 Spinner Implementation - -For operations without determinate progress: - -``` - Generating exam (~30-60s)... -``` - -**Spinner frames (Braille dots):** ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` - -**With elapsed time counter:** - -``` - Generating exam... 15s - Generating exam... 16s - Generating exam... 17s -``` - -### 2.4 Progress Indicator Specification - -| Phase | Duration | Display | Update Rate | -|-------|----------|---------|-------------| -| Prep | 1-2s | Static message | Once | -| Generate | 5-25s | Spinner + timer | 100ms | -| Finish | 1-3s | Static message | Once | - -**Implementation (ZSH):** - -```zsh -_teach_progress_indicator() { - local message="$1" - local estimate="$2" - local start_time=$(date +%s) - - # Use existing spinner from tui.zsh - _flow_spinner_start "$message" "$estimate" -} -``` - ---- - -## 3. Success/Failure Feedback - -### 3.1 Success Message Format - -``` - Created: slides/week08-regression.qmd - - File: slides/week08-regression.qmd - Slides: 24 slides - Sections: 6 sections - Images: 4 placeholders - -Next steps: - 1. Review in editor teach slides --revise slides/week08-regression.qmd - 2. Preview slides qu preview slides/week08-regression.qmd - 3. Commit changes [1] Review [2] Commit [3] Skip -``` - -### 3.2 Error Message Format - -**User Error (recoverable):** - -``` - teach: Topic not found in schedule - - Week 8 has no scheduled topic in _schedule.yml - -Recovery: - Specify topic explicitly: teach slides "Regression Analysis" - Edit schedule: teach config -``` - -**System Error (AI failure):** - -``` - teach: AI generation failed - - Claude returned an error after 45s - -Details: - Error: Rate limit exceeded - -Recovery: - 1. Wait 60 seconds and retry - 2. Try with --dry-run to test - 3. Check status: claude --status - -Retry? [Y/n] -``` - -**Config Error:** - -``` - teach: Configuration missing - - No .flow/teach-config.yml found - -Recovery: - Initialize teaching workflow: teach init "STAT 440" -``` - -### 3.3 Warning Message Format - -``` - teach: Using cached context (2 hours old) - - Course context from .flow/cache/context.json is stale - -Options: - Continue anyway: [Enter] - Refresh context: teach refresh -``` - -### 3.4 Status Icons Reference - -| Icon | Meaning | Color | -|------|---------|-------| -| | Success | Green (#72C77E) | -| | Error | Red (#E06C75) | -| | Warning | Yellow (#E5C07B) | -| | Info | Blue (#61AFEF) | -| | Working | Blue (animated) | - ---- - -## 4. Interactive Mode (-i) Design - -### 4.1 Interactive Flow - -Interactive mode provides step-by-step guidance: - -```bash -teach slides -i -``` - -**Step 1: Topic Selection** - -``` - Create Slides - -Topic: - 1. Week 8: Regression Analysis (scheduled) - 2. Week 9: Multiple Regression (upcoming) - 3. Enter custom topic - -Your choice [1-3]: _ -``` - -**Step 2: Configuration** - -``` - Slide Configuration - -Questions: - Format? [1] Quarto [2] Markdown (default: 1) - Theme? [1] Academic [2] Minimal [3] Modern (default: 1) - Sections? [1] Auto [2] Specify count (default: 1) - -Press Enter for defaults, or enter choices: _ -``` - -**Step 3: Confirmation** - -``` - Ready to Generate - - Topic: Regression Analysis - Format: Quarto (.qmd) - Theme: Academic - Week: 8 of 15 - -Generate now? [Y/n] _ -``` - -### 4.2 Interactive Shortcuts - -| Key | Action | -|-----|--------| -| Enter | Accept default | -| Number | Select option | -| q | Quit/cancel | -| ? | Show help | -| b | Go back | - ---- - -## 5. Revise Workflow (--revise) - -### 5.1 Revise Command Flow - -```bash -teach slides --revise slides/week08.qmd -``` - -**Step 1: Analysis** - -``` - Analyzing existing file... - - File: slides/week08.qmd - Created: 2026-01-15 14:32 - Slides: 18 slides - Last AI edit: None -``` - -**Step 2: Revision Options** - -``` - What would you like to improve? - - [1] Expand content Add more detail to existing slides - [2] Add examples Include practical examples - [3] Simplify language Make more accessible - [4] Add visuals Suggest images/diagrams - [5] Custom instructions Enter specific feedback - [6] Full regenerate Start fresh (keeps structure) - -Your choice [1-6]: _ -``` - -**Step 3: Custom Instructions (if option 5)** - -``` - Custom Instructions - -Enter your revision instructions: -> Add a slide about interaction terms after slide 12, and simplify the -> mathematical notation on slides 8-10. - -Continue? [Y/n] _ -``` - -**Step 4: Preview Changes** - -``` - Preview Changes - - Slides 8-10: Simplified notation - + New slide 13: "Interaction Terms" - + New slide 14: "Interaction Example" - -Apply changes? [1] Yes [2] Show diff [3] Cancel -``` - -### 5.2 Diff Display - -```diff - Slide 8: Model Assumptions - -- The regression model assumes $E[\epsilon_i | X_i] = 0$ -+ The regression model assumes the errors average to zero -+ (mathematically: E[e|X] = 0) - - Slide 13: Interaction Terms (NEW) - -+ When effects depend on other variables, we use interactions -+ Example: Does the effect of study time depend on sleep? -``` - ---- - -## 6. ADHD-Friendly UX Principles - -### 6.1 Design Principles Applied - -| Principle | Implementation | -|-----------|----------------| -| **Instant feedback** | Show spinner within 100ms of command | -| **Time awareness** | Always show elapsed time and estimates | -| **Smart defaults** | Auto-detect week, topic, format | -| **Progressive disclosure** | Basic → Interactive → Advanced | -| **Clear next steps** | Always show 1-3 actionable items | -| **Forgiving** | Typo tolerance, smart suggestions | -| **Consistent** | Same patterns across all commands | - -### 6.2 Cognitive Load Reduction - -**Before (high cognitive load):** - -```bash -teach exam --format quarto --questions 25 --duration 90 --types mc,sa,essay \ - --difficulty mixed --output exams/midterm1.qmd "Chapters 1-5" -``` - -**After (minimal cognitive load):** - -```bash -teach exam "Midterm 1" # Smart defaults -teach exam -i # Guided if unsure -``` - -### 6.3 Time Estimates - -Always provide realistic time estimates: - -| Command | Estimate | Display | -|---------|----------|---------| -| slides | 20-40s | "~30s" | -| lecture | 30-60s | "~45s" | -| quiz | 15-30s | "~20s" | -| exam | 30-90s | "~60s" | -| feedback | 10-30s | "~20s" | -| assignment | 20-40s | "~30s" | - -### 6.4 Error Recovery - -Every error should include: -1. **What went wrong** (clear, non-technical) -2. **Why it happened** (brief context) -3. **How to fix it** (specific command) -4. **Quick retry option** (if applicable) - ---- - -## 7. Suggested Aliases - -### 7.1 Command Shortcuts (in teach-dispatcher.zsh) - -Already implemented: - -```zsh -# In case statement -slides|sl) _teach_scholar_wrapper "slides" "$@" ;; -lecture|lec) _teach_scholar_wrapper "lecture" "$@" ;; -quiz|q) _teach_scholar_wrapper "quiz" "$@" ;; -exam|e) _teach_scholar_wrapper "exam" "$@" ;; -feedback|fb) _teach_scholar_wrapper "feedback" "$@" ;; -assignment|hw) _teach_scholar_wrapper "assignment" "$@" ;; -``` - -### 7.2 Global Shell Aliases (for power users) - -Add to user's `.zshrc` or document as optional: - -```zsh -# Teaching workflow shortcuts -alias tsl='teach slides' # Quick slides -alias tlec='teach lecture' # Quick lecture -alias tq='teach quiz' # Quick quiz -alias te='teach exam' # Quick exam -alias tfb='teach feedback' # Quick feedback -alias thw='teach assignment' # Quick homework - -# Interactive shortcuts -alias tsli='teach slides -i' # Interactive slides -alias tleci='teach lecture -i' # Interactive lecture -alias tqi='teach quiz -i' # Interactive quiz -alias tei='teach exam -i' # Interactive exam - -# Revise shortcuts -alias trev='teach --revise' # Start revision - -# Status shortcuts -alias ts='teach status' # Quick status -alias tw='teach week' # Current week -``` - -### 7.3 fzf-Powered Aliases (if fzf available) - -```zsh -# Pick and revise any generated file -alias trevp='teach --revise $(find . -name "*.qmd" -o -name "*.md" | fzf --header="Select file to revise")' - -# Pick topic from schedule -alias tpick='teach slides "$(yq -r ".weeks[].topic" _schedule.yml | fzf --header="Select topic")"' -``` - ---- - -## 8. Implementation Checklist - -### 8.1 Phase 1: Core UX (Current Sprint) - -- [ ] Update `_teach_execute()` with improved progress display -- [ ] Add elapsed time counter to spinner -- [ ] Implement structured success messages -- [ ] Implement structured error messages -- [ ] Add `--context` flag support - -### 8.2 Phase 2: Interactive Mode - -- [ ] Implement `-i` flag for all generation commands -- [ ] Create `_teach_interactive_wizard()` function -- [ ] Add step-by-step prompts for each command -- [ ] Implement keyboard navigation (q, b, ?) - -### 8.3 Phase 3: Revise Workflow - -- [ ] Implement `--revise` flag for all commands -- [ ] Create `_teach_analyze_file()` for existing content -- [ ] Implement revision options menu -- [ ] Add diff preview functionality - -### 8.4 Phase 4: Smart Defaults - -- [ ] Implement `_teach_auto_detect_topic()` -- [ ] Add week calculation from semester dates -- [ ] Parse `_schedule.yml` for topic lookup -- [ ] Cache course context with staleness detection - ---- - -## 9. Visual Reference - -### 9.1 Complete Happy Path Flow - -``` -$ teach slides "Regression Analysis" - - Preparing slides for "Regression Analysis"... - Context: Week 8 of 15 | STAT 440 - - Generating slides (~30s)... 12s - - Finalizing content... - - Created: slides/week08-regression.qmd - - File: slides/week08-regression.qmd - Slides: 24 slides (6 sections) - Format: Quarto RevealJS - -Next steps: - Review: teach slides --revise slides/week08-regression.qmd - Preview: qu preview slides/week08-regression.qmd - -Commit this content? [1] Review [2] Commit [3] Skip: _ -``` - -### 9.2 Complete Error Recovery Flow - -``` -$ teach exam - - teach: Cannot auto-detect topic - - No topic scheduled for Week 8 in _schedule.yml - - Available weeks with topics: - Week 6: Confidence Intervals - Week 7: Hypothesis Testing - Week 9: ANOVA - -Recovery options: - 1. Specify topic: teach exam "Midterm Topics" - 2. Edit schedule: $EDITOR _schedule.yml - 3. Interactive: teach exam -i - -Your choice [1-3]: _ -``` - ---- - -## 10. Appendix: Color Palette - -### FLOW_COLORS Reference (from core.zsh) - -| Name | ANSI Code | Hex | Usage | -|------|-----------|-----|-------| -| success | 38;5;114 | #87D787 | Completion, checkmarks | -| warning | 38;5;221 | #FFD75F | Warnings, cautions | -| error | 38;5;203 | #FF5F5F | Errors, failures | -| info | 38;5;117 | #87D7FF | Information, hints | -| header | 38;5;147 | #AFAFFF | Section headers | -| accent | 38;5;216 | #FFAF87 | Highlights | -| muted | 38;5;245 | #8A8A8A | Secondary text | -| cmd | 38;5;117 | #87D7FF | Command examples | - ---- - -**Document History:** -- v1.0.0 (2026-01-17): Initial design specification diff --git a/docs/specs/SPEC-ci-optimization-pick-wt-2025-12-31.md b/docs/specs/SPEC-ci-optimization-pick-wt-2025-12-31.md deleted file mode 100644 index 87ffb88fd..000000000 --- a/docs/specs/SPEC-ci-optimization-pick-wt-2025-12-31.md +++ /dev/null @@ -1,156 +0,0 @@ -# SPEC: CI Optimization & Pick Worktree Fix - -**Status:** implemented -**Created:** 2025-12-31 -**From Brainstorm:** In-session analysis - ---- - -## Overview - -Two issues to address: -1. **CI takes too long** - PR tests taking 5+ minutes, goal is <5 minutes -2. **`pick wt` not showing worktrees** - `PROJ_WORKTREE_DIR` not configured - ---- - -## Issue 1: CI Optimization - -### Current State - -| Job | Time | Notes | -|-----|------|-------| -| Main branch CI | ~31s | Fast | -| PR ZSH Tests | 5+ min | macOS tests slow | -| PR macOS Tests | 5+ min | Duplicate coverage | - -### Root Causes - -1. **macOS runner slow startup** - macOS runners take 30-60s just to start -2. **Duplicate test coverage** - macOS runs same tests as Ubuntu -3. **Sequential test steps** - Already optimized in latest commit -4. **Long-running optional tests** - sync, AI features, etc. - -### Quick Wins (< 30 min each) - -| # | Action | Impact | Effort | -|---|--------|--------|--------| -| ⚡1 | **Make macOS tests optional** - Use `continue-on-error: true` for entire macOS job | -3 min | 5 min | -| ⚡2 | **Remove macOS job entirely** - ZSH is cross-platform, Ubuntu coverage sufficient | -5 min | 2 min | -| ⚡3 | **Skip slow tests on PR** - Only run full suite on main branch | -2 min | 10 min | - -### Medium Effort (1-2 hours) - -| # | Action | Impact | Effort | -|---|--------|--------|--------| -| 1 | **Matrix strategy with fail-fast** - Run Ubuntu and macOS in parallel matrix | -1 min | 30 min | -| 2 | **Cache mock project structure** - Pre-build the mock dirs | -30s | 1 hr | -| 3 | **Split critical vs optional** - Fast critical path, slow optional path | -2 min | 1 hr | - -### Recommended Approach - -**Option A: Remove macOS job (Fastest)** - -```yaml -# Only keep zsh-tests job, remove macos-tests entirely -# ZSH is platform-agnostic, cross-platform stat already fixed -``` - -**Option B: Make macOS non-blocking** - -```yaml -macos-tests: - name: macOS Tests - runs-on: macos-latest - continue-on-error: true # Don't block PR merge -``` - -**Option C: Run macOS only on main** - -```yaml -macos-tests: - if: github.ref == 'refs/heads/main' -``` - ---- - -## Issue 2: Pick Worktree Not Working - -### Root Cause - -`PROJ_WORKTREE_DIR` environment variable not set in user's shell. - -### User Story - -> As a user, when I run `pick wt`, I want to see my worktrees listed so I can quickly switch between them. - -### Acceptance Criteria - -- [ ] `pick wt` shows worktrees when `PROJ_WORKTREE_DIR` is configured -- [ ] Clear error message when `PROJ_WORKTREE_DIR` is not set -- [ ] Documentation explains how to configure worktree directory - -### Fix - -1. **Add error message in pick.zsh:** - -```zsh -if [[ "$category" == "wt" ]]; then - if [[ -z "$PROJ_WORKTREE_DIR" ]]; then - echo "Error: PROJ_WORKTREE_DIR not set" - echo "Add to .zshrc: export PROJ_WORKTREE_DIR=~/projects/.worktrees" - return 1 - fi -fi -``` - -1. **Update documentation:** - - Add setup instructions to `docs/getting-started/quick-start.md` - - Add to `docs/reference/PICK-REFERENCE.md` - -2. **User configuration:** - -```zsh -# In ~/.zshrc -export PROJ_WORKTREE_DIR="$HOME/projects/.worktrees" -``` - ---- - -## Implementation Plan - -### Phase 1: CI Optimization (Immediate) - -1. Remove or make macOS job optional -2. Move optional tests to `continue-on-error` -3. Verify PR CI completes in <3 minutes - -### Phase 2: Pick Worktree Fix - -1. Add error message for missing `PROJ_WORKTREE_DIR` -2. Update documentation -3. Test `pick wt` with configured directory - ---- - -## Open Questions - -1. **Keep macOS tests at all?** - Do we need platform-specific testing? -2. **Default worktree location?** - Should we auto-detect or require config? - ---- - -## Review Checklist - -- [ ] CI completes in <5 minutes -- [ ] `pick wt` shows helpful error when unconfigured -- [ ] Documentation updated -- [ ] All existing tests still pass - ---- - -## History - -| Date | Change | -|------|--------| -| 2025-12-31 | Initial spec from brainstorm | diff --git a/docs/specs/SPEC-claude-code-plugin-integration-2026-01-17.md b/docs/specs/SPEC-claude-code-plugin-integration-2026-01-17.md deleted file mode 100644 index 7079ac0f3..000000000 --- a/docs/specs/SPEC-claude-code-plugin-integration-2026-01-17.md +++ /dev/null @@ -1,471 +0,0 @@ -# SPEC: Claude Code Plugin Integration - Scholar & Craft Dispatchers - -**Feature:** CLI wrappers for scholar and craft Claude Code plugins -**Status:** Design Phase -**Created:** 2026-01-17 -**Target Release:** flow-cli v5.13.0 -**Estimated Effort:** 18-24 hours over 2-3 weeks - ---- - -## Metadata - -| Field | Value | -|-------|-------| -| **Status** | Design → Implementation | -| **Priority** | High (enables research + teaching workflows) | -| **Complexity** | Medium (18-24 hours) | -| **Risk Level** | Low (additive, no breaking changes) | -| **Dependencies** | Claude Code CLI, scholar plugin, craft plugin | -| **Related Projects** | scholar v2.3.0+, craft v1.17.0+ | -| **Target Users** | Academic researchers, course instructors | -| **Branch Strategy** | feature/plugin-dispatchers → dev → main | - ---- - -## Executive Summary - -**Problem:** Scholar and craft are powerful Claude Code plugins with 108 combined commands (22 scholar + 86 craft), but they only work inside interactive Claude Code sessions. Users cannot leverage them from the command line for scripting, automation, or integration with flow-cli workflows. - -**Solution:** Create two new flow-cli dispatchers (`scholar` and `craft`) that wrap Claude Code plugin commands, enabling CLI usage while maintaining the power of AI-assisted generation. - -**Key Benefits:** -- **Teaching workflows:** `scholar quiz "topic" > quiz.md` for rapid content generation -- **Research automation:** `scholar arxiv "query" > papers.txt` for literature review -- **Quality assurance:** `craft check --for release` for pre-publication validation -- **Scripting:** All commands usable in shell scripts and automation -- **ADHD-friendly:** Fast, predictable, composable with existing flow-cli tools - -**Impact:** -- 10x faster course material creation (hours → minutes) -- Seamless integration with existing `teach-*`, `work`, `dash` commands -- Scriptable research pipelines (literature → code → paper) -- Zero context switching between terminal and Claude sessions - ---- - -## Problem Statement - -### Current State - -**Scholar Plugin (22 commands):** -- Literature: `/arxiv`, `/doi`, `/bib:search`, `/bib:add` -- Teaching: `/teaching:exam`, `/teaching:quiz`, `/teaching:syllabus`, `/teaching:slides` -- Research: `/scholar:lit-gap`, `/scholar:hypothesis`, `/scholar:analysis-plan` -- Manuscript: `/manuscript:methods`, `/manuscript:results`, `/manuscript:reviewer` - -**Craft Plugin (86 commands):** -- Smart orchestration: `/craft:do`, `/craft:check`, `/craft:hub` -- Testing: `/craft:test:run`, `/craft:test:coverage` -- Quality: `/craft:code:lint`, `/craft:security:scan` -- Documentation: `/craft:docs:validate`, `/craft:site:publish` - -**Problem:** Only work inside `claude` interactive sessions. - -**Limitations:** -1. Not scriptable - Cannot automate workflows -2. Manual context switching - Must enter/exit Claude sessions -3. No piping - Cannot compose with Unix tools -4. Flow-cli integration friction - -### Desired State - -```bash -# Scripting -scholar arxiv "bootstrap mediation" > lit-review.txt -scholar quiz "Linear Regression" > quiz.md - -# Integration -teach-exam "Hypothesis Testing" # Calls scholar -craft check --for release # Pre-publication - -# Automation -for topic in "regression" "anova"; do - scholar quiz "$topic" > "quizzes/$topic.md" -done -``` - ---- - -## Design Overview - -### Architecture - -**3-layer integration:** - -``` -┌────────────────────────────────────────────────┐ -│ Layer 3: flow-cli Workflows │ -│ teach-exam, teach-quiz, work, dash │ -├────────────────────────────────────────────────┤ -│ Layer 2: CLI Dispatchers (NEW) │ -│ scholar-dispatcher.zsh, craft-dispatcher.zsh │ -├────────────────────────────────────────────────┤ -│ Layer 1: Claude Code CLI │ -│ claude -p "/command" (print mode) │ -│ claude "/command" (interactive mode) │ -└────────────────────────────────────────────────┘ -``` - -**Key Decisions:** - -| Decision | Rationale | -|----------|-----------| -| Dispatcher pattern | Consistency with g, cc, mcp, r | -| Print mode default | Non-interactive, scriptable | -| Interactive fallback | Complex tasks need iteration | -| Output to stdout | Enable piping, redirection | -| Config integration | Read `.flow/teach-config.yml` | -| Completion support | ZSH completions for discovery | - ---- - -## Technical Validation - -### Claude Code CLI Capabilities (Verified 2026-01-17) - -**CLI Version:** 2.1.12 - -**Print Mode (`-p, --print`):** - -```bash -# Basic usage - outputs to stdout -claude -p "prompt text" - -# With slash commands (skills) -claude -p "/hub" -claude -p "/scholar:teaching:quiz 'Linear Regression'" - -# Output formats -claude -p "prompt" --output-format text # Default: plain text -claude -p "prompt" --output-format json # Structured JSON with metadata -claude -p "prompt" --output-format stream-json # Realtime streaming -``` - -**JSON Output Structure:** - -```json -{ - "type": "result", - "subtype": "success", - "is_error": false, - "duration_ms": 28561, - "num_turns": 4, - "result": "... output content ...", - "session_id": "uuid", - "total_cost_usd": 0.14, - "usage": { /* token counts */ } -} -``` - -**Key Flags for Automation:** - -| Flag | Purpose | Use Case | -|------|---------|----------| -| `-p, --print` | Non-interactive mode | Required for scripting | -| `--output-format json` | Structured output | Parse results programmatically | -| `--output-format text` | Plain text (default) | Piping to files | -| `--dangerously-skip-permissions` | Bypass confirmations | Sandboxed automation only | -| `--max-budget-usd ` | Cost limit | Prevent runaway costs | -| `--model ` | Model selection | `haiku` for fast/cheap | -| `--fallback-model ` | Auto-fallback | Handle overload | - -**Execution Characteristics:** -- **Latency:** 5-30 seconds typical (depends on task complexity) -- **Exit codes:** 0 on success, non-zero on failure -- **Stderr:** Error messages, debug info (with `--debug`) -- **Multi-turn:** Print mode completes full task before returning - -### Dispatcher Implementation Pattern - -```zsh -# Core execution function (shared by both dispatchers) -_flow_claude_exec() { - local cmd="$1" - local args="$2" - local output_format="${3:-text}" - - # Build command - arguments are passed as a single quoted string - local full_cmd="/$cmd $args" - - # Execute with appropriate flags - # Note: Using ZSH arrays prevents shell injection - local -a claude_args=( - -p "$full_cmd" - --output-format "$output_format" - --max-budget-usd 0.50 - ) - claude "${claude_args[@]}" 2>&1 -} - -# Example scholar wrapper -scholar() { - case "$1" in - quiz) shift; _flow_claude_exec "scholar:teaching:quiz" "$*" ;; - arxiv) shift; _flow_claude_exec "scholar:literature:arxiv" "$*" ;; - help) _scholar_help ;; - *) _scholar_help ;; - esac -} -``` - ---- - -## Error Handling - -### Error Categories - -| Category | Detection | Recovery | -|----------|-----------|----------| -| **Claude not installed** | `command -v claude` fails | Show install instructions | -| **Plugin not available** | Output contains "unknown command" | List available commands | -| **Network failure** | Exit code non-zero + timeout | Retry with backoff | -| **Rate limit** | Output contains "rate limit" | Wait and retry | -| **Permission denied** | Output contains "permission" | Suggest `--dangerously-skip-permissions` | -| **Timeout** | Command exceeds threshold | Increase timeout or switch to interactive | -| **Invalid arguments** | Output contains "invalid" | Show command help | -| **Cost exceeded** | JSON `total_cost_usd` > budget | Warn user, abort | - -### Implementation - -```zsh -# Error handling wrapper -_flow_claude_safe_exec() { - local cmd="$1" - shift - - # Pre-flight checks - if ! command -v claude &>/dev/null; then - _flow_log_error "Claude Code not installed" - _flow_log_info "Install: npm install -g @anthropic-ai/claude-code" - return 1 - fi - - # Build safe argument array (prevents injection) - local -a args=("$@") - local full_prompt="/$cmd ${args[*]}" - - # Execute with timeout - local output exit_code - output=$(timeout 120 claude -p "$full_prompt" 2>&1) - exit_code=$? - - # Handle timeout - if [[ $exit_code -eq 124 ]]; then - _flow_log_error "Command timed out after 120s" - _flow_log_info "Try: claude \"/$cmd ${args[*]}\" (interactive mode)" - return 124 - fi - - # Handle other errors - if [[ $exit_code -ne 0 ]]; then - _flow_log_error "Command failed (exit $exit_code)" - print -u2 "$output" - return $exit_code - fi - - # Check for known error patterns - if [[ "$output" == *"unknown command"* ]]; then - _flow_log_error "Plugin command not found: $cmd" - _flow_log_info "Ensure scholar/craft plugins are installed" - return 1 - fi - - # Success - output result - print "$output" -} -``` - -### Timeout Strategy - -| Command Type | Default Timeout | Rationale | -|--------------|-----------------|-----------| -| Quick lookup (doi, bib) | 30s | Simple retrieval | -| Search (arxiv, lit-gap) | 60s | API calls + processing | -| Generation (quiz, exam) | 120s | AI generation time | -| Complex (analysis-plan) | 180s | Multi-step reasoning | -| Interactive fallback | None | User controls | - -### User Feedback - -```zsh -# Progress indication for long-running commands -_flow_claude_with_spinner() { - local cmd="$1" - shift - local -a args=("$@") - - _flow_log_info "Running: $cmd..." - - # Create temp file for output - local tmpfile=$(mktemp) - trap "rm -f $tmpfile" EXIT - - # Run in background - claude -p "/$cmd ${args[*]}" > "$tmpfile" 2>&1 & - local pid=$! - - # Show spinner for commands > 5s - local i=0 - local spin='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏' - while kill -0 $pid 2>/dev/null; do - printf "\r ${spin:i++%10:1} Generating..." - sleep 0.1 - done - printf "\r" - - wait $pid - local exit_code=$? - - cat "$tmpfile" - return $exit_code -} -``` - ---- - -## Implementation Plan - -### Phase 1: Foundation (Week 1, 6h) - -**Deliverables:** -- [ ] `lib/dispatchers/scholar-dispatcher.zsh` -- [ ] `lib/dispatchers/craft-dispatcher.zsh` -- [ ] Argument parsing -- [ ] Help system -- [ ] Error handling - -### Phase 2: Scholar (Week 1-2, 10h) - -**Deliverables:** -- [ ] Literature commands -- [ ] Teaching commands -- [ ] Config auto-detection -- [ ] Output formatting - -### Phase 3: Craft (Week 2-3, 4h) - -**Deliverables:** -- [ ] Smart orchestration -- [ ] Testing commands -- [ ] Site management -- [ ] Mode selection - -### Phase 4: Integration (Week 3, 5h) - -**Deliverables:** -- [ ] Teaching workflow integration -- [ ] Work/dash enhancements -- [ ] Completions -- [ ] Documentation -- [ ] Tests - ---- - -## Success Metrics - -**Week 1:** Both dispatchers load, help works -**Week 2:** All scholar commands work, config auto-detection -**Week 3:** Craft commands work, integration complete -**Week 4+:** Daily usage, zero manual plugin invocation - ---- - -## Example Workflows - -### Teaching Workflow - -```bash -cd ~/teaching/stat-579 -work stat-579 - -scholar slides "Sequential Mediation" 75 --output slides/week08.qmd -scholar quiz "Sequential Mediation" --output quizzes/week08.md -craft site:validate -craft site:publish - -finish "Week 8 materials" -``` - -### Research Workflow - -```bash -cd ~/research/multiply-robust -work mr-paper - -scholar arxiv "multiply robust mediation" > lit-review.txt -scholar doi "10.1093/biomet/asz073" > paper.bib -scholar lit-gap "multiply robust" > gaps.md - -win "Literature review complete" -``` - ---- - -## Documentation - -**Reference:** -- `docs/reference/SCHOLAR-DISPATCHER-REFERENCE.md` -- `docs/reference/CRAFT-DISPATCHER-REFERENCE.md` - -**Tutorials:** -- `docs/tutorials/scholar-cli-usage.md` -- `docs/tutorials/craft-cli-usage.md` - ---- - -## Phased Brainstorming Plan - -This spec uses a **phased brainstorming approach** - detailed specs are created for each domain before implementation: - -| Domain | Spec Status | Implementation | -|--------|-------------|----------------| -| **Teaching/Scholar Enhancement** | ✅ Complete | [SPEC-teach-scholar-enhancement-2026-01-17.md](SPEC-teach-scholar-enhancement-2026-01-17.md) | -| **Research/Literature** | 🔜 Pending | Brainstorm after teaching implementation | -| **Craft Commands** | 🔜 Pending | Brainstorm after research implementation | - -### Teaching/Scholar Enhancement (Complete) - -Deep brainstorm completed with expert agent analysis (merged from 2 original specs): -- **UX Agent:** CLI user experience, progress indicators, ADHD-friendly design -- **Backend Architect:** Config schema, context handling, error strategies - -**Key Features:** - -- Smart defaults auto-detect current week's topic (`--week`, `--topic`) -- Interactive mode (`-i`) for step-by-step wizard -- Revision workflow (`--revise`) for iterating on content -- Context integration (`--context`) for course materials -- Content customization: 4 style presets + 9 content flags -- Lesson plan integration with YAML schema - -**Implementation:** 6 phases, 20-24 hours total - -### Research/Literature (Pending) - -To brainstorm after teaching implementation: -- `/scholar:literature:arxiv`, `/scholar:literature:doi` -- `/scholar:research:lit-gap`, `/scholar:research:hypothesis` -- Integration with Zotero/bibliography workflows - -### Craft Commands (Pending) - -To brainstorm after research implementation: -- `/craft:do`, `/craft:check`, `/craft:hub` -- Site management, testing, quality commands - ---- - -## Next Steps - -1. ✅ Main specification complete -2. ✅ Teaching/Scholar enhancement spec complete (merged from 2 specs) -3. ⏳ Begin teaching Phase 1 implementation (Flag Infrastructure) -4. 🔜 Complete teaching Phases 2-6 -5. 🔜 Brainstorm research/literature after teaching complete -6. 🔜 Brainstorm craft commands after research complete - ---- - -**Contact:** -Issues: [github.com/Data-Wise/flow-cli/issues](https://github.com/Data-Wise/flow-cli/issues) diff --git a/docs/specs/SPEC-documentation-debt-2026-01-21.md b/docs/specs/SPEC-documentation-debt-2026-01-21.md deleted file mode 100644 index 52997182b..000000000 --- a/docs/specs/SPEC-documentation-debt-2026-01-21.md +++ /dev/null @@ -1,484 +0,0 @@ -# SPEC: Documentation Debt Remediation - -**Created:** 2026-01-21 -**Status:** Phase 1 Complete ✅ -**Branch:** `feature/documentation-debt` -**Worktree:** `~/.git-worktrees/flow-cli/documentation-debt` -**Priority:** Low (non-blocking, quality improvement) - ---- - -## Executive Summary - -flow-cli has grown to 853 functions but only 8.6% have help text. While user-facing documentation is excellent (100% dispatcher coverage), the internal API documentation for helper libraries is nearly non-existent (0.5% coverage). This creates barriers for contributors and makes the codebase harder to maintain. - -### Key Metrics - -| Metric | Current | Target | Gap | -|--------|---------|--------|-----| -| Total functions | 853 | - | - | -| Functions with help | 73 | 200+ | 127+ | -| Dispatcher coverage | 100% | 100% | ✅ Done | -| Helper library coverage | 0.5% | 50%+ | ~170 functions | -| API reference docs | 0 pages | 3 pages | 3 pages | - ---- - -## Problem Statement - -### 1. Developer Experience Issues - -- **No API reference**: Contributors must read source code to understand helper functions -- **Inconsistent patterns**: Some functions have comments, most don't -- **Discovery barrier**: No way to find available utilities without grep-ing - -### 2. Maintenance Challenges - -- **Function duplication**: Similar functions exist because developers don't know utilities exist -- **Inconsistent usage**: Same task done different ways across codebase -- **Refactoring risk**: Hard to know what's public API vs internal - -### 3. Specific Gaps - -| Library | Functions | Documented | Impact | -|---------|-----------|------------|--------| -| `dotfile-helpers.zsh` | 27 | 0 | High - complex | -| `atlas-bridge.zsh` | 23 | 0 | Medium - optional | -| `plugin-loader.zsh` | 23 | 0 | Low - internal | -| `validation-helpers.zsh` | 19 | 0 | High - teaching | -| `git-helpers.zsh` | 17 | 0 | **Critical** | -| `tui.zsh` | 16 | 0 | **Critical** | -| `config.zsh` | 16 | 0 | Medium | -| `core.zsh` | 14 | 0 | **Critical** | -| Other (24 files) | 207 | 2 | Varies | -| **Total** | **342** | **2** | **0.6%** | - ---- - -## Proposed Solution - -### Phase 1: Core Libraries (Priority: Critical) - -**Scope:** 47 functions across 3 files -**Effort:** 4-6 hours -**Deliverable:** `docs/reference/MASTER-API-REFERENCE.md#core-library` - -#### 1.1 lib/core.zsh (14 functions) - -| Function | Purpose | Priority | -|----------|---------|----------| -| `_flow_log()` | Base logging function | P1 | -| `_flow_log_success()` | Success message (green ✓) | P1 | -| `_flow_log_warning()` | Warning message (yellow ⚠) | P1 | -| `_flow_log_error()` | Error message (red ✗) | P1 | -| `_flow_log_info()` | Info message (blue ℹ) | P1 | -| `_flow_log_muted()` | Muted/gray text | P2 | -| `_flow_log_debug()` | Debug output (FLOW_DEBUG) | P2 | -| `_flow_status_icon()` | Status → emoji mapping | P1 | -| `_flow_project_name()` | Extract project name from path | P1 | -| `_flow_find_project_root()` | Find .STATUS or .git root | P1 | -| `_flow_in_project()` | Check if in project | P2 | -| `_flow_format_duration()` | Format seconds as "Xh Ym" | P1 | -| `_flow_time_ago()` | Format timestamp as "2 hours ago" | P2 | -| `_flow_confirm()` | Y/N confirmation prompt | P1 | - -#### 1.2 lib/tui.zsh (16 functions) - -| Function | Purpose | Priority | -|----------|---------|----------| -| `_flow_progress_bar()` | ASCII progress bar | P1 | -| `_flow_sparkline()` | Sparkline graph ▁▂▃▅▇ | P2 | -| `_flow_table()` | Formatted table output | P1 | -| `_flow_box()` | Box around content | P1 | -| `_flow_has_fzf()` | Check fzf availability | P2 | -| `_flow_pick_project()` | fzf project picker | P1 | -| `_flow_show_project_preview()` | Preview for picker | P2 | -| `_flow_has_gum()` | Check gum availability | P2 | -| `_flow_input()` | Styled text input | P1 | -| `_flow_confirm_styled()` | Styled Y/N prompt | P1 | -| `_flow_choose()` | Selection menu | P1 | -| `_flow_widget_status()` | Status widget | P2 | -| `_flow_widget_timer()` | Timer widget | P2 | -| `_flow_spinner_start()` | Start spinner | P1 | -| `_flow_spinner_stop()` | Stop spinner | P1 | -| `_flow_with_spinner()` | Run command with spinner | P1 | - -#### 1.3 lib/git-helpers.zsh (17 functions) - -| Function | Purpose | Priority | -|----------|---------|----------| -| `_git_in_repo()` | Check if in git repo | P1 | -| `_git_current_branch()` | Get current branch name | P1 | -| `_git_remote_branch()` | Get remote tracking branch | P2 | -| `_git_is_clean()` | Check for uncommitted changes | P1 | -| `_git_is_synced()` | Check if synced with remote | P1 | -| `_git_has_unpushed_commits()` | Check for local commits | P1 | -| `_git_get_commit_count()` | Count commits since ref | P2 | -| `_git_get_commit_list()` | List commits since ref | P2 | -| `_git_teaching_files()` | Get teaching-related files | P3 | -| `_git_teaching_commit_message()` | Generate teaching commit msg | P3 | -| `_git_interactive_commit()` | Interactive commit flow | P2 | -| `_git_commit_teaching_content()` | Commit teaching content | P3 | -| `_git_push_current_branch()` | Push with upstream | P2 | -| `_git_create_deploy_pr()` | Create deployment PR | P3 | -| `_git_generate_pr_body()` | Generate PR description | P3 | -| `_git_detect_production_conflicts()` | Check for conflicts | P2 | -| `_git_rebase_onto_production()` | Rebase workflow | P2 | - -### Phase 2: Teaching Libraries (Priority: High) - -**Scope:** ~60 functions across 5 files -**Effort:** 6-8 hours -**Deliverable:** `docs/reference/MASTER-API-REFERENCE.md#teaching-libraries` - -| Library | Functions | Description | -|---------|-----------|-------------| -| `validation-helpers.zsh` | 19 | YAML, frontmatter validation | -| `backup-helpers.zsh` | 11 | Backup system | -| `cache-helpers.zsh` | 9 | Cache management | -| `index-helpers.zsh` | 11 | Index manipulation | -| `teaching-utils.zsh` | ~10 | Teaching utilities | - -### Phase 3: Integration Libraries (Priority: Medium) - -**Scope:** ~80 functions across 6 files -**Effort:** 8-10 hours -**Deliverable:** `docs/reference/MASTER-API-REFERENCE.md#teaching-libraries` - -| Library | Functions | Description | -|---------|-----------|-------------| -| `atlas-bridge.zsh` | 23 | Atlas state engine | -| `plugin-loader.zsh` | 23 | Plugin system | -| `config.zsh` | 16 | Configuration | -| `keychain-helpers.zsh` | ~8 | macOS Keychain | -| `project-detector.zsh` | ~8 | Project type detection | -| `project-cache.zsh` | ~6 | Project caching | - -### Phase 4: Specialized Libraries (Priority: Low) - -**Scope:** ~155 functions across remaining files -**Effort:** 10-12 hours -**Deliverable:** Individual API sections or appendix - ---- - -## Documentation Format - -### Inline Documentation Standard - -```zsh -# ============================================================================= -# Function: _flow_example -# Purpose: Brief one-line description -# ============================================================================= -# Arguments: -# $1 - (required) First argument description -# $2 - (optional) Second argument description [default: "value"] -# -# Returns: -# 0 - Success -# 1 - Error (describe when) -# -# Output: -# stdout - What it prints (if any) -# stderr - Error messages (if any) -# -# Example: -# _flow_example "arg1" "arg2" -# result=$(_flow_example "arg1") -# -# Dependencies: -# - _flow_log (from core.zsh) -# - fzf (optional) -# -# Notes: -# - Additional context or gotchas -# ============================================================================= -_flow_example() { - local required_arg="$1" - local optional_arg="${2:-default}" - # implementation -} -``` - -### API Reference Format - -```markdown -## _flow_example - -Brief description of what the function does. - -### Signature - -\`\`\`zsh -_flow_example [optional_arg] -\`\`\` - -### Arguments - -| Argument | Type | Required | Default | Description | -|----------|------|----------|---------|-------------| -| `$1` | string | Yes | - | Description | -| `$2` | string | No | `"default"` | Description | - -### Returns - -| Code | Meaning | -|------|---------| -| `0` | Success | -| `1` | Error condition | - -### Example - -\`\`\`zsh -# Basic usage -_flow_example "value" - -# With optional arg -_flow_example "value" "custom" - -# Capture output -result=$(_flow_example "value") -\`\`\` - -### See Also - -- `_flow_related` - Related function -- `core.zsh` - Parent library -``` - ---- - -## Implementation Plan - -### Wave 1: Foundation (2-3 hours) - -1. **Create API reference template** - - `docs/reference/API-TEMPLATE.md` - - Consistent structure for all libraries - -2. **Document core.zsh** (14 functions) - - Add inline docstrings - - Create `CORE-API-REFERENCE.md` - -3. **Update mkdocs.yml** - - Add API Reference section - -### Wave 2: TUI Library (2 hours) - -1. **Document tui.zsh** (16 functions) - - Focus on user-facing utilities - - Include visual examples - -2. **Add to CORE-API-REFERENCE.md** - -### Wave 3: Git Helpers (2-3 hours) - -1. **Document git-helpers.zsh** (17 functions) - - Separate teaching-specific from general - - Include workflow examples - -2. **Add to CORE-API-REFERENCE.md** - -### Wave 4: Testing & Review (1-2 hours) - -1. **Verify documentation accuracy** - - Test each example - - Check for outdated info - -2. **Add to DOCUMENTATION-COVERAGE.md** - - Update metrics - - Mark Phase 1 complete - ---- - -## Success Criteria - -### Phase 1 Complete When: ✅ COMPLETE (2026-01-22) - -- [x] `docs/reference/MASTER-API-REFERENCE.md#core-library` exists (~2,000 lines) → **1,661 lines** -- [x] 47 functions documented (core.zsh + tui.zsh + git-helpers.zsh) → **47 functions** -- [x] All P1 functions have inline docstrings → **All 47 functions** -- [x] mkdocs.yml updated with API Reference section → **Added** -- [x] DOCUMENTATION-COVERAGE.md updated to reflect progress → **Updated** - -### Quality Checklist: ✅ VERIFIED - -- [x] Every function has: purpose, args, returns, example -- [x] Examples are copy-pasteable and tested -- [x] Cross-references between related functions -- [x] Consistent formatting throughout - ---- - -## Estimated Timeline - -| Phase | Scope | Effort | Priority | -|-------|-------|--------|----------| -| Phase 1 | Core libraries (47 funcs) | 4-6 hours | Critical | -| Phase 2 | Teaching libraries (~60 funcs) | 6-8 hours | High | -| Phase 3 | Integration libraries (~80 funcs) | 8-10 hours | Medium | -| Phase 4 | Specialized libraries (~155 funcs) | 10-12 hours | Low | -| **Total** | **342 functions** | **28-36 hours** | - | - -### Recommended Approach - -1. **Start with Phase 1** - Maximum impact, minimal effort -2. **Defer Phase 2-4** - Do incrementally as you touch those files -3. **Automate where possible** - Consider docstring extraction script - ---- - -## Alternatives Considered - -### 1. Auto-generate from Source - -**Pros:** Fast, always up-to-date -**Cons:** No semantic understanding, poor examples -**Decision:** Rejected - quality matters more than coverage - -### 2. Document Only Public API - -**Pros:** Less work, cleaner interface -**Cons:** Need to define "public" vs "internal" -**Decision:** Partially adopted - mark internal functions as such - -### 3. Use TypeScript/JSDoc Style - -**Pros:** Tooling support, IDE integration -**Cons:** ZSH has no standard, would be custom -**Decision:** Rejected - stick to shell conventions - ---- - -## Dependencies - -- None blocking -- Nice to have: docstring extraction tool (future) - ---- - -## Risks - -| Risk | Mitigation | -|------|------------| -| Documentation gets stale | Add to PR checklist | -| Too much effort | Start small (Phase 1 only) | -| Inconsistent style | Create and enforce template | - ---- - -## Related Documents - -- `docs/reference/DOCUMENTATION-COVERAGE.md` - Current metrics -- `do../reference/MASTER-ARCHITECTURE.md` - System context -- `CLAUDE.md` - Project guidelines (documentation standards section) - ---- - -## Appendix: Full Function Inventory - -### lib/core.zsh (14 functions) - -``` -_flow_log() -_flow_log_success() -_flow_log_warning() -_flow_log_error() -_flow_log_info() -_flow_log_muted() -_flow_log_debug() -_flow_status_icon() -_flow_project_name() -_flow_find_project_root() -_flow_in_project() -_flow_format_duration() -_flow_time_ago() -_flow_confirm() -``` - -### lib/tui.zsh (16 functions) - -``` -_flow_progress_bar() -_flow_sparkline() -_flow_table() -_flow_box() -_flow_has_fzf() -_flow_pick_project() -_flow_show_project_preview() -_flow_has_gum() -_flow_input() -_flow_confirm_styled() -_flow_choose() -_flow_widget_status() -_flow_widget_timer() -_flow_spinner_start() -_flow_spinner_stop() -_flow_with_spinner() -``` - -### lib/git-helpers.zsh (17 functions) - -``` -_git_in_repo() -_git_current_branch() -_git_remote_branch() -_git_is_clean() -_git_is_synced() -_git_has_unpushed_commits() -_git_get_commit_count() -_git_get_commit_list() -_git_teaching_files() -_git_teaching_commit_message() -_git_interactive_commit() -_git_commit_teaching_content() -_git_push_current_branch() -_git_create_deploy_pr() -_git_generate_pr_body() -_git_detect_production_conflicts() -_git_rebase_onto_production() -``` - -### All Libraries Summary - -| Library | Functions | -|---------|-----------| -| dotfile-helpers.zsh | 27 | -| atlas-bridge.zsh | 23 | -| plugin-loader.zsh | 23 | -| validation-helpers.zsh | 19 | -| git-helpers.zsh | 17 | -| tui.zsh | 16 | -| config.zsh | 16 | -| core.zsh | 14 | -| render-queue.zsh | 11 | -| index-helpers.zsh | 11 | -| backup-helpers.zsh | 11 | -| ai-recipes.zsh | 11 | -| performance-monitor.zsh | 10 | -| parallel-helpers.zsh | 10 | -| date-parser.zsh | 10 | -| r-helpers.zsh | 9 | -| profile-helpers.zsh | 9 | -| parallel-progress.zsh | 9 | -| cache-helpers.zsh | 9 | -| ai-usage.zsh | 9 | -| custom-validators.zsh | 8 | -| cache-analysis.zsh | 8 | -| config-validator.zsh | 7 | -| help-browser.zsh | 7 | -| project-detector.zsh | 7 | -| inventory.zsh | 6 | -| status-dashboard.zsh | 6 | -| keychain-helpers.zsh | 5 | -| project-cache.zsh | 5 | -| hook-installer.zsh | 5 | -| teaching-utils.zsh | 4 | -| renv-integration.zsh | 4 | -| **Total** | **342** | - ---- - -**Last Updated:** 2026-01-21 -**Author:** Claude Opus 4.5 diff --git a/docs/specs/SPEC-dotfile-integration-2026-01-08.md b/docs/specs/SPEC-dotfile-integration-2026-01-08.md deleted file mode 100644 index c85ae0805..000000000 --- a/docs/specs/SPEC-dotfile-integration-2026-01-08.md +++ /dev/null @@ -1,626 +0,0 @@ -# SPEC: Dotfile Management Integration via dot Dispatcher - -**Status:** Completed (Implemented in v5.3.0) -**Created:** 2026-01-08 -**Updated:** 2026-01-13 (Documentation cleanup) -**Target Release:** v5.0.0 (Delivered in v5.3.0) -**Estimated Effort:** 26 hours over 3-4 weeks - ---- - -## Overview - -Integration of chezmoi + Bitwarden + Mise dotfile management tools into flow-cli via a new **`dot`** (dotfile) dispatcher. This enhancement enables users to manage their dotfiles, secrets, and development tool versions seamlessly within the flow-cli workflow. - -**Note:** Originally designed as `dot` (2 letters), changed to `dot` (3 letters) per user preference for clearer intent, despite breaking the 2-letter dispatcher convention. - -**Key Benefits:** -- **Consistency:** All dotfile operations in one place -- **Security:** Secrets managed via Bitwarden (no hardcoded API keys) -- **Cross-machine sync:** iMac ↔ MacBook dotfile sync in seconds -- **ADHD-friendly:** Fast, forgiving, discoverable interface - ---- - -## Primary User Story - -**As a** developer using flow-cli on multiple machines (iMac + MacBook), -**I want to** manage my dotfiles, secrets, and tool versions through flow-cli, -**So that** I can keep my development environment in sync without manual chezmoi/bitwarden commands. - -**Acceptance Criteria:** -1. `dot` command shows sync status in < 0.5 seconds -2. `dot edit .zshrc` edits dotfile with preview and auto-apply -3. `dot sync` pulls changes with safety preview before applying -4. `dot secret ` retrieves secrets without echoing to terminal -5. Dashboard shows dotfile sync state (1 line, non-intrusive) -6. Work command checks for dotfile updates on session start -7. Flow doctor includes dotfile health checks -8. All core operations complete in < 3 seconds -9. Zero hardcoded secrets in dotfiles after setup -10. Both machines stay in sync with < 5 minute sync workflow - ---- - -## Secondary User Stories - -### 1. Quick File Editing - -**As a** user making frequent shell config changes, -**I want to** edit dotfiles with automatic preview and apply, -**So that** changes take effect immediately without manual steps. - -**Acceptance:** `dot edit .zshrc` → modify → auto-preview → apply → active in < 30 seconds - -### 2. Safe Syncing - -**As a** user working on multiple machines, -**I want to** see what will change before syncing, -**So that** I don't accidentally overwrite important customizations. - -**Acceptance:** `dot sync` shows diff, requires confirmation, handles conflicts gracefully - -### 3. Secret Management - -**As a** user with API keys and credentials, -**I want to** store secrets in Bitwarden and inject them into dotfiles, -**So that** no secrets are committed to version control. - -**Acceptance:** Secrets retrieved via `dot secret`, injected into templates, never echoed - ---- - -## Technical Requirements - -### Architecture - -**Pattern:** Dispatcher (consistent with `g`, `mcp`, `cc`, `r`, `qu`) - -**File Structure:** - -``` -lib/ -├── dispatchers/ -│ └── dot-dispatcher.zsh # Main dispatcher logic -└── dotfile-helpers.zsh # Helper functions - -completions/ -└── _dot # ZSH completions - -tests/ -└── dot-dispatcher.test.zsh # Test suite - -docs/ -├── reference/ -│ └── DOT-DISPATCHER-REFERENCE.md -└── tutorials/ - └── dotfile-setup.md -``` - -**Dependencies (Optional):** -- chezmoi (Homebrew) -- bitwarden-cli (Homebrew) -- mise (Homebrew) - future - -**Dependency Strategy:** -- All tools optional (graceful degradation) -- Lazy loading (zero startup overhead) -- Feature detection (like Atlas integration) - -### Core Components - -#### 1. dot-dispatcher.zsh - -Main command dispatcher with subcommands: - -```zsh -df() { - case "$1" in - # Core operations - ""|status) _dot_status ;; - edit) shift; _dot_edit "$@" ;; - sync) shift; _dot_sync "$@" ;; - push) shift; _dot_push "$@" ;; - diff) shift; _dot_diff "$@" ;; - apply) shift; _dot_apply "$@" ;; - - # Secret management - unlock) _dot_unlock ;; - secret) shift; _dot_secret "$@" ;; - - # Troubleshooting - doctor) _dot_doctor ;; - undo) _dot_undo ;; - init) shift; _dot_init "$@" ;; - - # Meta - help) _dot_help ;; - *) _dot_passthrough "$@" ;; - esac -} -``` - -#### 2. dotfile-helpers.zsh - -Helper functions: - -```zsh -# Tool detection -_dot_has_chezmoi() { ... } -_dot_has_bw() { ... } -_dot_require_tool() { ... } - -# Status checks -_dot_get_sync_status() { ... } -_dot_get_modified_files() { ... } -_dot_get_last_sync_time() { ... } - -# Path resolution -_dot_resolve_file_path() { ... } # Fuzzy matching for file names - -# Secret management -_dot_bw_session_valid() { ... } -_dot_retrieve_secret() { ... } - -# UI helpers -_dot_format_status() { ... } -_dot_show_diff() { ... } -_dot_confirm_action() { ... } -``` - -#### 3. Integration Points - -**Dashboard Integration:** - -```zsh -# In commands/dash.zsh -if _dot_has_chezmoi; then - local df_status=$(_dot_get_sync_status) - echo "Dotfiles: $df_status" -fi -``` - -**Work Command Integration:** - -```zsh -# In commands/work.zsh -if [[ "$FLOW_DOTFILE_CHECK_ON_START" != "no" ]]; then - if _dot_has_chezmoi && _dot_has_updates; then - echo "🔄 Dotfile updates available. Run 'df sync' to update." - fi -fi -``` - -**Flow Doctor Integration:** - -```zsh -# In commands/doctor.zsh -echo "📁 DOTFILES" -_doctor_check_cmd "chezmoi" "brew" "optional" -_doctor_check_cmd "bw" "brew:bitwarden-cli" "optional" - -if _dot_has_chezmoi; then - _dot_doctor # Run dotfile-specific checks -fi -``` - ---- - -## Data Models - -### Dotfile Sync State - -```zsh -# Internal representation -typeset -gA _DF_STATE=( - sync_status "synced|modified|behind|ahead|conflict" - last_sync "timestamp" - modified_files "array of file paths" - untracked_files "array of file paths" - remote_behind "count" - remote_ahead "count" -) -``` - -### Status Display Format - -``` -State: 🟢 Synced | 🟡 Modified | 🔴 Behind | ⚠️ Conflict -Last sync: -Modified: files -``` - -### Bitwarden Session - -```zsh -# Environment variable -export BW_SESSION="" - -# Session validation -bw unlock --check # Returns 0 if valid -``` - ---- - -## Dependencies - -### Required - -- ZSH (5.8+) -- fzf (for interactive file picker) - -### Optional (Graceful Degradation) - -- **chezmoi** (core dotfile sync) - - Install: `brew install chezmoi` - - Fallback: df commands show install message - -- **bitwarden-cli** (secret management) - - Install: `brew install bitwarden-cli` - - Fallback: df secret commands show install message - -- **mise** (version management) - - Install: `brew install mise` - - Fallback: version commands unavailable - -### Optional (Enhanced UX) - -- **bat** (syntax-highlighted diffs) -- **delta** (better git diffs) -- **eza** (file listing with icons) - ---- - -## UI/UX Specifications - -### User Flow: Edit Dotfile - -```mermaid -graph TD - A[df edit .zshrc] --> B{File exists in chezmoi?} - B -->|Yes| C[Open in $EDITOR] - B -->|No| D[Show error with suggestion] - - C --> E[User edits file] - E --> F{Changes made?} - - F -->|No| G[Exit without action] - F -->|Yes| H[Show diff preview] - - H --> I{Apply changes?} - I -->|Yes| J[chezmoi apply] - I -->|No| K[Save to chezmoi only] - I -->|Diff| L[Show full diff] - - J --> M[Changes active] - L --> I - - M --> N[Show success message] - N --> O[Suggest next action: df push] -``` - -### User Flow: Sync from Remote - -```mermaid -graph TD - A[df sync] --> B[Fetch from remote] - B --> C{Changes detected?} - - C -->|No| D[Already up-to-date] - C -->|Yes| E[Show diff preview] - - E --> F{Conflicts?} - F -->|No| G[Prompt: Apply changes?] - F -->|Yes| H[Show conflict resolution options] - - G -->|Yes| I[chezmoi apply] - G -->|No| J[Cancel sync] - - H --> K{User choice} - K -->|Keep local| L[Discard remote] - K -->|Keep remote| M[Discard local] - K -->|Merge| N[Open in editor] - - I --> O[Success: Changes applied] - L --> O - M --> O - N --> O - - O --> P[Suggest: df push if needed] -``` - -### User Flow: Retrieve Secret - -```mermaid -graph TD - A[df secret github-token] --> B{BW_SESSION valid?} - - B -->|No| C[Prompt: df unlock first] - B -->|Yes| D{Secret exists?} - - D -->|No| E[Show error with add instructions] - D -->|Yes| F[Retrieve password field] - - F --> G{Output destination} - G -->|Variable| H[Return to caller no echo] - G -->|Stdout| I[Echo to stdout no terminal display] - - H --> J[Secret available for use] - I --> J -``` - -### Wireframes (ASCII) - -#### df (Status Display) - -``` -╭───────────────────────────────────────────────────╮ -│ 📁 Dotfiles Status │ -├───────────────────────────────────────────────────┤ -│ │ -│ State: 🟢 Synced │ -│ Last sync: 2 hours ago (from iMac) │ -│ Tracked files: 12 │ -│ │ -│ Quick actions: │ -│ df edit .zshrc Edit shell config │ -│ df sync Pull latest changes │ -│ df help Show all commands │ -│ │ -╰───────────────────────────────────────────────────╯ -``` - -#### df edit .zshrc (Success) - -``` -✓ .zshrc edited successfully - -Changes: - + export NEW_ALIAS="gst='git status'" - - # old comment - -💡 Tip: Run 'df push' to sync to other machines -``` - -#### df sync (With Changes) - -``` -🔄 Syncing dotfiles from remote... - -Changes from iMac (3 hours ago): - ~/.config/zsh/.zshrc - + export ANTHROPIC_API_KEY="" - - export OLD_VAR="value" - - ~/.gitconfig - + [user] - + email = "updated@example.com" - -Apply these changes? [Y/n/d] - Y = Apply now (recommended) - n = Cancel sync - d = Show full diff first -``` - -#### df secret github-token (Session Expired) - -``` -╭───────────────────────────────────────────────────╮ -│ 🔒 Bitwarden Session Expired │ -├───────────────────────────────────────────────────┤ -│ │ -│ What: Cannot retrieve secrets │ -│ Why: Session timeout (1 hour default) │ -│ How: Unlock your vault to continue │ -│ │ -│ Run: df unlock │ -│ Enter your master password when prompted │ -│ │ -╰───────────────────────────────────────────────────╯ -``` - -#### df doctor (Diagnostics) - -``` -📁 Dotfile Health Check - -✓ chezmoi installed (v2.45.0) -✓ Bitwarden CLI installed (v2024.1.0) -✓ Dotfile repository connected -✓ Last sync: 2 hours ago (iMac) -⚠ 3 modified files (not pushed) -✓ No merge conflicts - -Recommendations: - → Run 'df push' to sync modified files - → Run 'df sync' to check for remote updates -``` - -### Accessibility - -N/A - CLI only (no web interface) - ---- - -## Open Questions - -1. **Auto-apply behavior:** - - Should `dot edit` always auto-apply changes after preview? - - Or should it require explicit `dot apply`? - - **Recommendation:** Auto-apply with preview (can cancel) - -2. **Dashboard integration intensity:** - - Always show dotfile status in dashboard? - - Or only when out-of-sync? - - **Recommendation:** Always show (1 line, minimal) - -3. **Work command integration:** - - Opt-in or opt-out for dotfile update checks? - - **Recommendation:** Opt-out (check by default, can disable with `FLOW_DOTFILE_CHECK_ON_START=no`) - -4. **Conflict resolution default:** - - Default to "keep local", "keep remote", or "merge manually"? - - **Recommendation:** No default - force user to choose (3 clear options) - -5. **Secret injection timing:** - - When to inject secrets into templates? - - On every `dot sync`? On demand? - - **Recommendation:** On-demand via `dot secret`, automatic during `chezmoi apply` if template references Bitwarden - ---- - -## Review Checklist - -### Design Review - -- [x] Command naming approved (`dot`) -- [x] Dispatcher pattern confirmed -- [x] Integration points identified -- [x] User flows documented -- [x] Visual mockups created -- [ ] Open questions resolved - -### Implementation Review - -- [ ] Core commands implemented -- [ ] Secret management functional -- [ ] Dashboard integration complete -- [ ] Work command integration complete -- [ ] Flow doctor integration complete -- [ ] Error handling comprehensive -- [ ] Performance targets met - -### Testing Review - -- [ ] Unit tests passing -- [ ] Integration tests passing -- [ ] Performance benchmarks met -- [ ] Security audit complete -- [ ] No secrets in logs/history - -### Documentation Review - -- [ ] Reference docs complete -- [ ] Tutorial written -- [ ] Examples provided -- [ ] Troubleshooting section added -- [ ] Migration guide (if needed) - -### Release Review - -- [ ] Version bumped (v5.0.0) -- [ ] Changelog updated -- [ ] Breaking changes documented -- [ ] Deployment tested on both machines - ---- - -## Implementation Notes - -### Phase 1: Foundation (4 hours) - -**Goal:** Basic dispatcher skeleton with help and status - -**Tasks:** -1. Create `lib/dispatchers/dot-dispatcher.zsh` -2. Implement basic structure (`dot`, `help`, `status`) -3. Add tool detection (`_dot_has_chezmoi`, `_dot_require_tool`) -4. Integrate into `flow.plugin.zsh` -5. Test: `dot help` shows help, `dot` shows status - -**Deliverables:** -- Working `dot help` command -- Basic `dot` status display -- Tool detection logic - -### Phase 2: Core Workflows (8 hours) - -**Goal:** Edit and sync workflows operational - -**Tasks:** -1. Implement `dot edit ` with smart path resolution -2. Add `dot diff` for preview -3. Implement `dot sync` with safety preview -4. Add `dot push` for publishing changes -5. Implement `dot apply` for applying changes -6. Add `dot undo` for rollback -7. Test: Complete edit/sync workflows - -**Deliverables:** -- Edit workflow (edit → preview → apply) -- Sync workflow (sync → preview → apply) -- Push workflow (push to remote) -- Undo functionality - -### Phase 3: Secret Management (6 hours) - -**Goal:** Bitwarden integration functional - -**Tasks:** -1. Implement `dot unlock` (BW session management) -2. Implement `dot secret ` (retrieve secrets) -3. Implement `dot secret list` (show available secrets) -4. Add secret injection into templates -5. Security audit (no leaks, no logs) -6. Test: Secret retrieval workflows - -**Deliverables:** -- Working Bitwarden unlock -- Secret retrieval (no echo) -- Template injection -- Security verified - -### Phase 4: Integration (4 hours) - -**Goal:** Seamless integration with existing commands - -**Tasks:** -1. Dashboard integration (dotfile status line) -2. Work command integration (check for updates) -3. Flow doctor integration (health checks) -4. Test: All integrations work together - -**Deliverables:** -- Dashboard shows dotfile status -- Work command checks for updates -- Flow doctor includes dotfile checks - -### Phase 5: Polish (4 hours) - -**Goal:** Production-ready release - -**Tasks:** -1. ZSH completions (`completions/_dot`) -2. Comprehensive test suite -3. Documentation (reference + tutorial) -4. Performance optimization -5. Final testing on both machines - -**Deliverables:** -- Working completions -- All tests passing -- Complete documentation -- Performance benchmarks met - ---- - -## History - -| Date | Author | Change | -|------|--------|--------| -| 2026-01-08 | Backend Architect Agent | Initial architecture design | -| 2026-01-08 | UX Designer Agent | Complete UX design with 21 mockups | -| 2026-01-08 | Claude (Sonnet 4.5) | Synthesized findings into formal spec | - ---- - -## Related Documents - -- [DOT Dispatcher Reference](../reference/MASTER-DISPATCHER-GUIDE.md#dot-dispatcher) -- [DOT Dispatcher Tutorial](../tutorials/12-dot-dispatcher.md) -- [Implementation Checklist](dot-dispatcher-implementation-checklist.md) (archived) -- [Quick Reference Card](dot-dispatcher-refcard.md) (archived) - ---- - -**Implementation Complete:** v5.3.0 - See [DOT Dispatcher Reference](../reference/MASTER-DISPATCHER-GUIDE.md#dot-dispatcher) for current documentation. diff --git a/docs/specs/SPEC-flow-alias-enhancement-2026-01-12.md b/docs/specs/SPEC-flow-alias-enhancement-2026-01-12.md deleted file mode 100644 index e5ef74883..000000000 --- a/docs/specs/SPEC-flow-alias-enhancement-2026-01-12.md +++ /dev/null @@ -1,308 +0,0 @@ -# SPEC: Flow Alias Enhancement - -**Status:** complete -**Created:** 2026-01-12 -**Completed:** 2026-01-12 -**From Brainstorm:** Deep brainstorm session - ---- - -## Overview - -Expand `flow alias` command from a read-only reference tool to a full alias management suite with validation, creation, removal, testing, and health checking capabilities. Primary goal: eliminate broken/conflicting aliases through comprehensive validation. - ---- - -## Primary User Story - -**As a** developer using flow-cli -**I want to** validate, create, and manage shell aliases safely -**So that** I don't have broken aliases, conflicts with system commands, or duplicates - -### Acceptance Criteria - -- [x] `flow alias doctor` checks all aliases for issues -- [x] `flow alias add` creates validated aliases -- [x] `flow alias rm` safely removes aliases (comment out + backup) -- [x] `flow alias test` validates and dry-runs aliases -- [x] `flow alias find` searches aliases by pattern -- [x] `flow alias edit` opens .zshrc at alias section - ---- - -## Secondary User Stories - -### Story 2: Alias Conflict Detection - -**As a** power user with many aliases -**I want to** know when an alias shadows a system command -**So that** I don't accidentally break expected behavior - -### Story 3: Safe Alias Removal - -**As a** user cleaning up old aliases -**I want to** remove aliases without risk of data loss -**So that** I can easily undo if something breaks - ---- - -## Architecture - -``` -┌─────────────────────────────────────────────────────────────┐ -│ commands/alias.zsh │ -├─────────────────────────────────────────────────────────────┤ -│ │ -│ flow_alias() # Main dispatcher (extend) │ -│ ├── (existing) # Show categories │ -│ ├── add) # → _flow_alias_add │ -│ ├── rm|remove) # → _flow_alias_remove │ -│ ├── doctor) # → _flow_alias_doctor │ -│ ├── test) # → _flow_alias_test │ -│ ├── find) # → _flow_alias_find │ -│ └── edit) # → _flow_alias_edit │ -│ │ -│ # Core functions │ -│ _flow_alias_add() # Create with validation │ -│ _flow_alias_remove() # Safe removal (comment + backup) │ -│ _flow_alias_doctor() # Health check all aliases │ -│ _flow_alias_test() # Validate → dry-run → execute │ -│ _flow_alias_find() # Pattern search │ -│ _flow_alias_edit() # Open in $EDITOR │ -│ │ -│ # Validation helpers │ -│ _flow_alias_validate() # Core validation logic │ -│ _flow_alias_check_shadow()# Check command conflicts │ -│ _flow_alias_check_target()# Check target exists │ -│ _flow_alias_parse_zshrc() # Parse aliases from file │ -│ │ -└─────────────────────────────────────────────────────────────┘ -``` - ---- - -## API Design - -### Command Interface - -| Command | Description | Example | -|---------|-------------|---------| -| `flow alias doctor` | Health check all aliases | `flow alias doctor` | -| `flow alias add [def]` | Create alias (interactive or one-liner) | `flow alias add bcl='brew list --cask'` | -| `flow alias rm ` | Safe removal (comment out) | `flow alias rm bcl` | -| `flow alias test ` | Validate and dry-run | `flow alias test bcl` | -| `flow alias find ` | Search aliases | `flow alias find brew` | -| `flow alias edit` | Open .zshrc at alias section | `flow alias edit` | - -### Doctor Output Format - -``` -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -🩺 Alias Health Check -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - -Scanning: ~/.config/zsh/.zshrc -Found: 27 aliases - -❌ ERRORS (n) - ='' - └─ - └─ - -⚠️ WARNINGS (n) - ='' - └─ - -✅ HEALTHY (n) - - -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -Summary: n errors, n warnings, n healthy -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -``` - ---- - -## Data Models - -### Alias Validation Result - -```zsh -# Returned by _flow_alias_validate() -# Format: "status:message" -# status: ok|error|warning -# message: description of issue - -# Examples: -# "ok:valid" -# "error:shadows /bin/cat" -# "warning:long command, consider function" -``` - -### Parsed Alias Entry - -```zsh -# Format from _flow_alias_parse_zshrc() -# line_number:alias_name:alias_value - -# Example: -# "143:bcl:brew list --cask" -``` - ---- - -## Dependencies - -- **Required:** ZSH, standard Unix tools (grep, sed) -- **Optional:** None (pure ZSH implementation) -- **Files:** `~/.config/zsh/.zshrc` (alias storage location) - ---- - -## UI/UX Specifications - -### User Flow: Doctor - -``` -User: flow alias doctor - │ - ├─→ Parse .zshrc for all aliases - ├─→ For each alias: - │ ├─→ Check if shadows system command - │ ├─→ Check if target exists - │ ├─→ Check syntax validity - │ └─→ Categorize: error/warning/healthy - │ - └─→ Display formatted report -``` - -### User Flow: Add (Interactive) - -``` -User: flow alias add - │ - ├─→ Prompt: "Alias name:" - ├─→ Prompt: "Command:" - ├─→ Validate (shadow, target, syntax) - │ ├─→ If error: show issue, ask to proceed anyway - │ └─→ If ok: continue - ├─→ Append to .zshrc - └─→ Show: "Added. Run: source ~/.config/zsh/.zshrc" -``` - -### User Flow: Remove - -``` -User: flow alias rm bcl - │ - ├─→ Find alias in .zshrc - │ └─→ If not found: error + exit - ├─→ Show: "Found: alias bcl='...' (line 143)" - ├─→ Confirm: "Remove? [y/N]" - ├─→ Create backup: .zshrc.alias-backup - ├─→ Comment out line (not delete) - └─→ Show: "Done. Undo: flow alias undo bcl" -``` - -### Wireframe: Doctor Output - -``` -┌─────────────────────────────────────────────────────────────┐ -│ 🩺 Alias Health Check │ -├─────────────────────────────────────────────────────────────┤ -│ Scanning: ~/.config/zsh/.zshrc │ -│ Found: 27 aliases │ -│ │ -│ ❌ ERRORS │ -│ ┌─────────────────────────────────────────────────────────┐ │ -│ │ cat='bat' │ │ -│ │ └─ Shadows: /bin/cat │ │ -│ └─────────────────────────────────────────────────────────┘ │ -│ │ -│ ⚠️ WARNINGS │ -│ ┌─────────────────────────────────────────────────────────┐ │ -│ │ nexus='cd ... && npm start' │ │ -│ │ └─ Long command - consider function │ │ -│ └─────────────────────────────────────────────────────────┘ │ -│ │ -│ ✅ HEALTHY: 24 aliases │ -│ bi, bci, bl, bcl, bs, bo, bu, bup, bdr, ... │ -│ │ -├─────────────────────────────────────────────────────────────┤ -│ Summary: 2 errors, 1 warning, 24 healthy │ -└─────────────────────────────────────────────────────────────┘ -``` - -### Accessibility Checklist - -- [x] Color-coded output (red=error, yellow=warning, green=healthy) -- [x] Text labels alongside colors (❌, ⚠️, ✅) -- [x] Summary line for quick scan -- [x] Suggestions for each issue - ---- - -## Open Questions - -1. **Undo feature:** Should `flow alias undo` be implemented to uncomment removed aliases? -2. **Unused detection:** Include shell history analysis in doctor, or separate command? -3. **Category for brew:** Add `brew` category to existing `flow alias` reference? - ---- - -## Review Checklist - -- [x] Architecture approved -- [x] API design approved -- [x] Implementation plan approved -- [x] Ready for implementation -- [x] Implementation complete -- [x] Tests written (42 tests) -- [x] Documentation updated - ---- - -## Implementation Notes - -### Phase 1: Doctor (~45 min) - -- Core validation engine -- Shadow detection using `command -v` and `which` -- Target existence check -- Formatted output with colors - -### Phase 2: Find + Edit (~15 min) - -- Simple grep wrapper for find -- `$EDITOR +` for edit - -### Phase 3: Add (~50 min) - -- One-liner parsing: `name='command'` format -- Interactive mode with prompts -- Append to .zshrc with comment header - -### Phase 4: Remove (~30 min) - -- Find line in .zshrc -- Backup file before modification -- Comment out, don't delete - -### Phase 5: Test (~30 min) - -- Reuse validation from doctor -- Dry-run using `echo` expansion -- Optional execute with confirmation - -### Estimated Total: ~3 hours, ~310 new lines - ---- - -## History - -| Date | Change | -|------|--------| -| 2026-01-12 | Initial spec from deep brainstorm | -| 2026-01-12 | Implementation complete - all 6 commands working | -| 2026-01-12 | 42 tests written (test-alias-management.zsh) | -| 2026-01-12 | Documentation complete (reference + workflow guide) | diff --git a/docs/specs/SPEC-flow-doctor-dot-enhancement-2026-01-23.md b/docs/specs/SPEC-flow-doctor-dot-enhancement-2026-01-23.md deleted file mode 100644 index 15b7de485..000000000 --- a/docs/specs/SPEC-flow-doctor-dot-enhancement-2026-01-23.md +++ /dev/null @@ -1,1027 +0,0 @@ -# SPEC: flow doctor DOT Token Enhancement - -## Metadata - -**Status:** phase-1-approved -**Created:** 2026-01-23 -**Approved for Implementation:** 2026-01-23 (Phase 1 only) -**From Brainstorm:** Deep analysis with 18 expert questions -**Feature Branch:** feature/token-automation (to be merged to dev) -**Target Version:** v5.17.0 -**Effort Estimate:** 12 hours Phase 1 (approved) + 30 hours Phases 2-4 (deferred) -**Priority:** High (Security & UX improvement) - ---- - -## Overview - -Enhance `flow doctor` command with DOT token automation capabilities. **Phase 1** (approved for implementation) focuses on core enhancements: isolated checks, category-based fixes, and unified validation logic. Phases 2-4 (safety/reporting, user experience, advanced features) are deferred to future development cycles. - -This transforms flow doctor from a general health checker into a sophisticated token lifecycle management tool while maintaining its ADHD-friendly design philosophy. - -**Phase 1 Enhancements (12 hours - APPROVED):** -1. ✅ Isolated token checks (`--dot` flag) -2. ✅ Category-based fix selection -3. ✅ Unified token validation logic (delegate to `dot token expiring`) -4. ✅ Verbosity levels (--quiet, --verbose) -5. ✅ 5-minute cache manager (avoid API rate limits) - -**Future Enhancements (30 hours - DEFERRED):** -- Phase 2: Atomic fixes with rollback, inline git fixes, token reports, multi-token support, history tracking -- Phase 3: Gamification integration, macOS notifications, event hooks -- Phase 4: Custom validation rules, CI/CD exit codes, additional event hooks - ---- - -## Primary User Story - -**As a** flow-cli user with GitHub tokens -**I want** to quickly check and fix token issues in isolation -**So that** I can maintain security without running full dependency checks - -**Acceptance Criteria:** -1. ✅ `flow doctor --dot` checks only token health (< 3 seconds) -2. ✅ `flow doctor --fix-token` fixes only token issues (< 60 seconds) -3. ✅ Failed fixes rollback automatically with recovery instructions -4. ✅ `g push` detects expired tokens and offers inline fix -5. ✅ Token rotations log as "Security maintenance" wins -6. ✅ All token operations cached for 5 minutes (avoid API rate limits) - -**User Journey:** - -```bash -# Morning routine -$ flow doctor --dot -🔑 GITHUB TOKEN - ⚠ Expiring in 5 days - -# Quick fix -$ flow doctor --fix-token -🔄 Rotating github-token... (30s estimated) - ✓ Backup saved - ✓ New token generated - ✓ Synced with gh CLI -✅ Done (28s) -🎉 Win logged: Security maintenance - -# Later that day -$ g push -✓ Token validated -[pushes normally] -``` - ---- - -## Secondary User Stories - -### Story 2: CI/CD Integration - -**As a** DevOps engineer -**I want** machine-readable exit codes from token checks -**So that** I can integrate flow doctor into CI/CD pipelines - -**Acceptance Criteria:** -- Exit code 0 = all tokens healthy -- Exit code 1-5 = specific token issues (documented) -- `--format json` outputs structured data -- Runs in non-interactive mode (`--fix -y`) - -### Story 3: Security Auditing - -**As a** security-conscious developer -**I want** comprehensive token health reports -**So that** I can audit token lifecycle and security posture - -**Acceptance Criteria:** -- `flow doctor --token-report` generates detailed report -- Report includes rotation history, scope audit, security recommendations -- Reports saved to `~/.flow/reports/token-health-YYYY-MM-DD.txt` -- History tracked in `~/.flow/history/token-health.log` - -### Story 4: Multi-Token Management - -**As a** developer managing multiple tokens (GitHub, NPM, PyPI) -**I want** to check and fix all DOT tokens at once -**So that** I maintain security across all my credentials - -**Acceptance Criteria:** -- Checks all DOT-managed tokens (github, npm, pypi) -- Sequential fix with progress indicator (1/3... 2/3... 3/3) -- Can filter to specific token: `flow doctor --dot=github-token` - ---- - -## Technical Requirements - -### Architecture - -#### Current State (v5.16.0) - -```mermaid -graph TD - A[flow doctor] --> B[Check Dependencies] - A --> C[Check Aliases] - A --> D[Check GitHub Token] - D --> E[Inline Validation Logic] - E --> F[GitHub API] - E --> G[Check gh CLI] - E --> H[Check Claude MCP] -``` - -**Problem:** Duplicate validation logic in `doctor.zsh` and `dot-dispatcher.zsh` - -#### Enhanced Architecture (v5.17.0) - -```mermaid -graph TD - A[flow doctor] --> B{Mode?} - B -->|--dot| C[Token Check Only] - B -->|default| D[Full Health Check] - B -->|--fix| E[Category Selection] - - C --> F[dot token expiring] - D --> F - E --> G{Category?} - - G -->|Token| H[Atomic Token Fix] - G -->|Tools| I[Install Tools] - G -->|Aliases| J[Fix Aliases] - - F --> K[Cache Manager] - K -->|5min TTL| L[GitHub API] - - H --> M[Backup] - M --> N[Rotate] - N --> O{Success?} - O -->|Yes| P[Commit] - O -->|No| Q[Rollback] - Q --> R[Recovery Instructions] - - P --> S[Sync Services] - S --> T[gh CLI] - S --> U[Claude MCP] - S --> V[Env Vars] - - P --> W[History Log] - P --> X[Gamification] -``` - -#### Component Diagram - -```mermaid -graph LR - A[flow doctor] --> B[Doctor Core] - B --> C[Token Manager] - B --> D[Fix Engine] - B --> E[Report Generator] - - C --> F[dot token expiring] - C --> G[Cache Manager] - - D --> H[Atomic Fix] - D --> I[Rollback Handler] - - E --> J[History Logger] - E --> K[Report Formatter] - - F --> L[GitHub API] - G --> L - - H --> M[Backup Manager] - H --> N[Service Sync] - - J --> O[~/.flow/history/] - K --> P[~/.flow/reports/] -``` - ---- - -### CLI Interface Design - -#### New Commands - -| Command | Description | Execution Time | -|---------|-------------|----------------| -| `flow doctor --dot` | Check only DOT tokens | < 3s | -| `flow doctor --dot=TOKEN` | Check specific token | < 2s | -| `flow doctor --fix-token` | Fix only token issues | < 60s | -| `flow doctor --fix-token -y` | Auto-fix tokens (no prompts) | < 60s | -| `flow doctor --token-report` | Generate audit report | < 5s | -| `flow doctor --token-report --save` | Save report to file | < 5s | -| `flow doctor --dry-run` | Preview fixes without applying | < 3s | -| `flow doctor --quiet` | Minimal output (errors only) | < 3s | -| `flow doctor --verbose` | Detailed output + history | < 5s | - -#### Enhanced Existing Commands - -| Command | Enhancement | -|---------|-------------| -| `flow doctor` | Now includes all DOT tokens, not just GitHub | -| `flow doctor --fix` | Category selection menu before fixing | -| `flow doctor --fix -y` | Auto-fixes all categories | - -#### Exit Codes - -| Code | Meaning | Use Case | -|------|---------|----------| -| 0 | All tokens healthy | CI/CD success | -| 1 | Token(s) missing | Prompt user to configure | -| 2 | Token(s) expired | Trigger rotation workflow | -| 3 | Token(s) invalid | Regenerate tokens | -| 4 | Token(s) expiring soon (< 7 days) | Warning, schedule rotation | -| 5 | Service sync issues (gh CLI, etc.) | Fix integration | - ---- - -### Data Models - -#### Token Health History Log - -**File:** `~/.flow/history/token-health.log` - -**Format:** TSV (Tab-Separated Values) - -``` -timestamp action token_name status details -2026-01-23T12:30:00Z CHECK github-token valid 5 days remaining -2026-01-23T12:35:00Z ROTATE github-token success 28s -2026-01-23T12:35:28Z SYNC gh-cli success 2s -2026-01-23T12:36:00Z CHECK github-token valid 90 days remaining -2026-01-23T18:00:00Z CHECK npm-token valid 180 days remaining -``` - -**Schema:** -- `timestamp`: ISO 8601 format -- `action`: CHECK | ROTATE | SYNC | FIX | BACKUP | ROLLBACK -- `token_name`: Secret identifier (e.g., github-token, npm-token) -- `status`: valid | expired | invalid | expiring | success | failed -- `details`: Human-readable context - -#### Token Health Report - -**File:** `~/.flow/reports/token-health-YYYY-MM-DD.txt` - -**Sections:** -1. Report header (timestamp, version) -2. Token status summary (per token) -3. Security recommendations -4. Actions needed -5. Historical rotation log (last 5) - -**Example Structure:** - -``` -TOKEN HEALTH REPORT -Generated: 2026-01-23 12:30:00 -flow-cli version: v5.17.0 - -=== TOKEN STATUS === - -GitHub Token: github-token - Status: ⚠ Expiring - Created: 2025-10-15 (100 days ago) - Expires: 2026-01-13 (5 days remaining) - Type: fine-grained - User: @username - Last validated: 2026-01-23 12:30:00 - -NPM Token: npm-token - Status: ✓ Valid - Expires: 2026-06-20 (180 days remaining) - -=== SECURITY RECOMMENDATIONS === - -Token Rotation Schedule: - • Rotate tokens every 90 days - • github-token: Rotate in next 5 days - -Scope Audit: - ⚠ github-token has 'delete_repo' scope - Consider removing if not needed - -Environment Variable Exposure: - ✗ GITHUB_TOKEN not exported in shell - -=== ACTIONS NEEDED === - -1. Rotate github-token (expires in 5 days) - Command: flow doctor --fix-token - -2. Export GITHUB_TOKEN environment variable - Add to ~/.config/zsh/.zshrc: - export GITHUB_TOKEN=$(dot secret github-token) - -=== ROTATION HISTORY === - -2026-01-08: Rotated github-token (85 days old) -2025-11-20: Rotated github-token (36 days old) -2025-10-15: Created github-token - -=== END REPORT === -``` - -#### Cache File - -**File:** `~/.flow/cache/token-check-TOKENNAME.cache` - -**Format:** JSON - -```json -{ - "token_name": "github-token", - "cached_at": "2026-01-23T12:30:00Z", - "expires_at": "2026-01-23T12:35:00Z", - "ttl_seconds": 300, - "status": "valid", - "days_remaining": 45, - "username": "your-username", - "services": { - "gh_cli": "authenticated", - "claude_mcp": "configured", - "env_var": "missing" - } -} -``` - -#### Custom Validation Rules Config - -**File:** `~/.flow/doctor-rules.yaml` - -```yaml -# Token validation rules for flow doctor -version: 1 - -token_rules: - github: - min_days_remaining: 14 # Warn if < 14 days - max_age_days: 90 - required_scopes: - - repo - - workflow - forbidden_scopes: - - delete_repo - - delete_packages - check_2fa: true - - npm: - min_days_remaining: 30 - max_age_days: 180 - check_registry: true - - pypi: - min_days_remaining: 30 - check_trusted_publishing: true - -# Notification settings -notifications: - enabled: true - critical_only: true # Only notify on expired/invalid - channels: - - macos_notification - - terminal_bell - -# History settings -history: - enabled: true - retention_days: 365 - path: "~/.flow/history/token-health.log" - -# Cache settings -cache: - enabled: true - ttl_seconds: 300 # 5 minutes - path: "~/.flow/cache/" -``` - ---- - -### Dependencies - -#### Required (Existing) - -- ✅ `dot` command (DOT dispatcher) - v5.16.0+ -- ✅ `dot token expiring` - v5.16.0+ -- ✅ `_dot_token_age_days()` helper function -- ✅ `_dot_token_rotate()` function -- ✅ `_dot_token_sync_gh()` function -- ✅ `jq` - JSON parsing -- ✅ `curl` - GitHub API calls -- ✅ macOS `security` - Keychain access -- ✅ Bitwarden CLI (`bw`) - Secret storage - -#### New Dependencies - -- ❌ **None** - All enhancements use existing tools - -#### Optional Integrations - -- `gh` CLI - Enhanced sync (already integrated) -- `osascript` - macOS notifications (already integrated) -- Atlas - State management (future consideration) - ---- - -## UI/UX Specifications - -### Terminal Output Design - -#### Default Output (Normal Verbosity) - -``` -🔑 DOT TOKENS - - GitHub Token (github-token) - ✓ Valid (@username) - ⚠ Expiring in 5 days - - NPM Token (npm-token) - ✓ Valid - ✓ 180 days remaining - - PyPI Token (pypi-flow-cli) - ✗ Expired 10 days ago - -─────────────────────────────────────────────── - -Quick actions: - flow doctor --fix-token Fix token issues (30s) - flow doctor --token-report Generate audit report -``` - -#### Quiet Output (`--quiet`) - -``` -🔑 GITHUB TOKEN - ✓ Valid -``` - -#### Verbose Output (`--verbose`) - -``` -🔑 GITHUB TOKEN - ✓ Valid (@username) - - Metadata: - Created: 2025-10-15 - Age: 100 days - Type: fine-grained - Expires: 2026-01-13 (5 days remaining) - - Token-Dependent Services: - ✓ gh CLI authenticated (v2.40.1) - ✓ Claude MCP configured - ✗ GITHUB_TOKEN env var not exported - - Rotation History: - • 2026-01-08: Rotated (85 days old) - • 2025-11-20: Rotated (36 days old) - • 2025-10-15: Created - - API Rate Limit: - Used: 142/5000 (2.8%) - Resets: 2026-01-23 13:00:00 (23 min) -``` - -### Interactive Fix Flow - -#### Category Selection (Single Choice) - -``` -△ Found issues in 3 categories: - -Which category should I fix first? - - 1. GitHub Token (2 issues, ~30s) - • github-token expiring in 5 days - • pypi-flow-cli expired 10 days ago - - 2. Missing Tools (5 tools, ~5 min) - • jq, gh, atlas, ... - - 3. Aliases (1 issue, ~10s) - • pick alias not configured - - 4. Fix all categories (~5.5 min) - - 0. Cancel - -Enter selection [1-4, 0]: -``` - -#### Atomic Fix Progress - -``` -🔄 Rotating github-token... (30s estimated) - - [1/5] Creating backup... ✓ (2s) - [2/5] Generating new token... ✓ (8s) - [3/5] Validating via GitHub API... ✓ (3s) - [4/5] Storing in vault... ✓ (5s) - [5/5] Syncing services... ✓ (10s) - -✅ Token rotated successfully (28s) - -Services synced: - ✓ Bitwarden vault updated - ✓ Keychain updated (Touch ID) - ✓ gh CLI authenticated - ✓ Claude MCP configured - -Old token backed up to: - ~/.flow/backups/github-token-backup-2026-01-23 - -🎉 Win logged: Security maintenance - Streak: 3 days | Goal: 2/3 wins today -``` - -#### Rollback on Failure - -``` -🔄 Rotating github-token... (30s estimated) - - [1/5] Creating backup... ✓ (2s) - [2/5] Generating new token... ✓ (8s) - [3/5] Validating via GitHub API... ✗ Failed - -✗ Rotation failed: GitHub API unreachable - -⚠ Rolling back changes... - ✓ Backup preserved - ✓ Old token still valid - ✓ No services affected - -Manual recovery (if needed): - Old token saved at: ~/.flow/backups/github-token-backup-2026-01-23 - - Restore with: - dot secret github-token < ~/.flow/backups/github-token-backup-2026-01-23 - -Next steps: - • Check GitHub API status: https://www.githubstatus.com - • Retry in a few minutes: flow doctor --fix-token - • Or rotate manually: dot token rotate -``` - -### Inline Git Fix Flow - -```bash -$ g push - -ℹ Validating GitHub token... -✗ GitHub token expired or invalid - -Token rotation takes ~30s. Fix now? [y/n] y - -🔄 Rotating github-token... - [Progress as shown above] - -✅ Token rotated successfully (28s) - -ℹ Retrying push... -✓ Pushed to origin/feature-branch -``` - -### Dry Run Output - -```bash -$ flow doctor --dry-run - -🔍 DRY RUN MODE (no changes will be applied) - -─────────────────────────────────────────────── - -🔑 DOT TOKENS - - GitHub Token (github-token) - ⚠ Expiring in 5 days - - Would fix: - • Rotate token (~30s) - • Sync gh CLI (~2s) - • Update Claude MCP (~1s) - - PyPI Token (pypi-flow-cli) - ✗ Expired 10 days ago - - Would fix: - • Rotate token (~20s) - -─────────────────────────────────────────────── - -Summary: - • 2 tokens would be rotated - • 3 services would be synced - • Estimated time: ~53 seconds - -Run without --dry-run to apply changes -``` - ---- - -## Open Questions - -### Implementation Details - -1. **Cache invalidation strategy:** - - Q: Should cache be invalidated on `dot token rotate` success? - - A: Yes - add cache invalidation hook after successful rotation - -2. **Rollback granularity:** - - Q: If gh CLI sync fails but token rotated, should we roll back the token? - - A: No - token rotation is atomic unit. Service sync failures are non-fatal warnings. - -3. **Multi-token fix order:** - - Q: Should we prioritize expired over expiring when fixing multiple tokens? - - A: Yes - sort by severity: expired → expiring soon (< 3 days) → expiring (< 7 days) - -4. **Notification frequency:** - - Q: How often should we notify about same expired token? - - A: Once per day maximum, tracked in `~/.flow/cache/notifications.json` - -### Future Enhancements - -1. **Auto-heal mode:** - - User answer: No for v5.17.0 - - Revisit in v5.18.0 based on user feedback - -2. **Parallel token fixes:** - - Current: Sequential with progress - - Future: Consider parallel with promise-like async handling - -3. **Integration with flow setup:** - - Should `flow setup` configure token health checks? - - Potential: Add to setup wizard as optional step - ---- - -## Review Checklist - -### Functionality - -- [ ] `flow doctor --dot` isolates token checks (< 3s) -- [ ] `flow doctor --dot=TOKEN` filters to specific token -- [ ] `flow doctor --fix-token` fixes only token issues -- [ ] Category selection shows estimated times -- [ ] Atomic fixes create backups before changes -- [ ] Failed fixes rollback with recovery instructions -- [ ] Inline git fixes prompt with time estimate -- [ ] All DOT tokens checked (github, npm, pypi) -- [ ] Sequential multi-token fixes show progress (1/3... 2/3... 3/3) -- [ ] Token reports include all sections (metadata, services, recommendations) -- [ ] Verbosity levels work (--quiet, default, --verbose) -- [ ] Dry run mode shows what would be fixed - -### Integration - -- [ ] Delegates to `dot token expiring` (unified logic) -- [ ] 5-minute cache avoids duplicate GitHub API calls -- [ ] gh CLI synced after token rotation -- [ ] Claude MCP updated after token rotation -- [ ] GITHUB_TOKEN env var checked -- [ ] Gamification: rotations log as "Security maintenance" wins -- [ ] History tracked in `~/.flow/history/token-health.log` -- [ ] Event hooks: finish command checks token health - -### UX - -- [ ] ADHD-friendly: clear categories, time estimates, progress indicators -- [ ] Error messages include recovery steps -- [ ] Success messages include next steps -- [ ] macOS notifications for critical issues only -- [ ] Terminal colors consistent with flow-cli theme -- [ ] Help text updated with new flags -- [ ] Examples in help show common workflows - -### Testing - -- [ ] Unit tests for cache manager (TTL, invalidation) -- [ ] Unit tests for atomic fix logic (backup, rollback) -- [ ] Integration tests for category selection -- [ ] E2E tests for full fix workflow -- [ ] Mock GitHub API for rate limit testing -- [ ] Test rollback with simulated failures -- [ ] Test multi-token fix sequence -- [ ] Test inline git fix integration - -### Documentation - -- [ ] CLAUDE.md updated with new flags -- [ ] DOT-DISPATCHER-REFERENCE.md updated -- [ ] New guide: `docs/guides/TOKEN-LIFECYCLE-MANAGEMENT.md` -- [ ] Update CHANGELOG.md with v5.17.0 features -- [ ] Update README.md examples -- [ ] Add tutorial: `docs/tutorials/23-doctor-token-management.md` - -### Performance - -- [ ] Token checks complete in < 3s (with cache) -- [ ] Token checks complete in < 10s (without cache) -- [ ] Token rotation completes in < 60s (typical) -- [ ] Multi-token fixes respect time estimates (±10%) -- [ ] Cache reduces GitHub API calls by 80%+ - -### Security - -- [ ] Backups stored with restricted permissions (600) -- [ ] Recovery instructions never expose token values -- [ ] History log doesn't include sensitive data -- [ ] Reports don't expose token values -- [ ] Cache files have proper permissions -- [ ] macOS notifications don't show token details - ---- - -## Implementation Notes - -### Phase 1: Core Enhancement (P0 - 12 hours) ✅ APPROVED FOR IMPLEMENTATION - -**Status:** Ready to implement (approved 2026-01-23) -**Target:** v5.17.0 -**Timeline:** 1.5 days - -**Files to modify:** -- `commands/doctor.zsh` - Add flags, category selection, verbosity -- `lib/core.zsh` - Add cache manager functions -- `commands/flow.zsh` - Update help text - -**New files:** -- `lib/doctor-cache.zsh` - Cache management logic -- `lib/doctor-atomic.zsh` - Atomic fix + rollback logic - -**Tasks:** -1. Add `--dot`, `--dot=TOKEN`, `--fix-token` flags (2h) -2. Implement category selection menu (3h) -3. Delegate to `dot token expiring` (2h) -4. Add verbosity levels (--quiet, --verbose) (2h) -5. Implement cache manager (5-minute TTL) (3h) - -**Tests:** -- `tests/test-doctor-token-flags.zsh` (30 tests) -- `tests/test-doctor-cache.zsh` (20 tests) - ---- - -### Phase 2: Safety & Reporting (P1 - 15 hours) 🔜 DEFERRED TO FUTURE - -**Status:** Deferred pending Phase 1 completion and user feedback -**Dependencies:** Phase 1 must be complete and stable - -**Files to modify:** -- `lib/doctor-atomic.zsh` - Rollback logic -- `lib/dispatchers/g-dispatcher.zsh` - Inline fix prompt -- `commands/doctor.zsh` - Report generation - -**New files:** -- `lib/doctor-report.zsh` - Report formatter -- `lib/doctor-history.zsh` - History logger - -**Tasks:** -1. Implement atomic fixes with backup (4h) -2. Add rollback on failure (2h) -3. Inline git fix integration (2h) -4. Token report generation (3h) -5. Multi-token support (3h) -6. History tracking (1h) - -**Tests:** -- `tests/test-doctor-atomic.zsh` (40 tests) -- `tests/test-doctor-rollback.zsh` (25 tests) -- `tests/test-doctor-report.zsh` (20 tests) - ---- - -### Phase 3: User Experience (P2 - 5 hours) 🔜 DEFERRED TO FUTURE - -**Status:** Deferred pending Phase 1 completion and user feedback -**Dependencies:** Phase 1 must be complete and stable - -**Files to modify:** -- `commands/adhd.zsh` - Win logging integration -- `commands/doctor.zsh` - Notifications - -**Tasks:** -1. Gamification integration (2h) -2. macOS notifications (1h) -3. Event hooks (finish command) (2h) - -**Tests:** -- `tests/test-doctor-gamification.zsh` (15 tests) -- `tests/test-doctor-notifications.zsh` (10 tests) - ---- - -### Phase 4: Advanced Features (P3 - 6 hours) 🔜 DEFERRED TO FUTURE - -**Status:** Deferred pending Phase 1 completion and user feedback -**Dependencies:** Phase 1 must be complete and stable - -**New files:** -- `lib/doctor-rules.zsh` - Custom rules parser -- `lib/doctor-exit-codes.zsh` - Exit code manager - -**Tasks:** -1. Custom validation rules (`~/.flow/doctor-rules.yaml`) (3h) -2. Exit codes for CI/CD (1h) -3. Additional event hooks (2h) - -**Tests:** -- `tests/test-doctor-rules.zsh` (25 tests) -- `tests/test-doctor-exit-codes.zsh` (10 tests) - ---- - -### Documentation Tasks (4 hours per phase) - -**Phase 1:** -- Update help text -- Quick reference card -- Basic examples - -**Phase 2:** -- Comprehensive guide: TOKEN-LIFECYCLE-MANAGEMENT.md -- Tutorial: 23-doctor-token-management.md -- Update API reference - -**Phase 3:** -- Gamification examples -- Event hooks documentation -- Troubleshooting guide - -**Phase 4:** -- Custom rules schema reference -- CI/CD integration examples -- Advanced workflows guide - ---- - -## Implementation Strategy - -### Approved Approach: Phase 1 Only - -**Phase 1 Implementation** (12h / 1.5 days) ✅ APPROVED -- Delivers immediate value -- Low risk (additive changes) -- Easy to validate -- Establishes foundation for future enhancements - -**Phases 2-4 Deferred** (30h total) -- Pending Phase 1 completion -- User feedback will inform priorities -- Can be implemented incrementally in future versions - -### Phase 1 Rollout Plan - -1. **Day 1 (8h):** Implement flags, category selection, delegation -2. **Day 2 (4h):** Implement cache manager, verbosity levels -3. **Post-implementation:** Testing, documentation, PR to dev -4. **Target:** Include in v5.17.0 release - -### Future Phases Decision Points - -After Phase 1 ships, evaluate based on: -- User adoption and feedback -- Security incident frequency -- Feature requests and pain points -- Development capacity and priorities - -**Potential triggers for Phase 2+:** -- High demand for token reports -- Security incidents requiring faster response -- CI/CD integration requests from teams -- Multi-token management becomes priority - ---- - -## Success Metrics - -### Quantitative - -- Token check execution time: < 3s (with cache) -- Token rotation time: < 60s (95th percentile) -- GitHub API call reduction: > 80% (via caching) -- User-reported token expiration incidents: -90% -- `flow doctor` usage frequency: +50% - -### Qualitative - -- Users report "easier token management" -- Fewer support issues related to expired tokens -- Positive feedback on inline git fixes -- Adoption of `--fix-token` flag (faster workflow) - ---- - -## History - -### 2026-01-23 - Initial Specification - -**Created by:** Deep brainstorm with 18 expert questions -**Status:** Draft -**From:** feature/token-automation branch - -**Key Decisions:** -1. Add `--dot` flag (not subcommand) for isolation -2. Category selection always prompts (single choice) -3. Full delegation to `dot token expiring` (unified logic) -4. Verbosity levels: quiet/normal/verbose -5. Atomic fixes with rollback on failure -6. Inline git fixes with time estimates -7. Token reports with security recommendations -8. History tracking in `~/.flow/history/` -9. Gamification integration (security maintenance wins) -10. macOS notifications for critical issues only - -**User Preferences (18 questions):** -- Isolation: `--dot` flag (Recommended) -- Fix control: Category selection always ask -- Integration: Full delegation to dot token expiring -- Verbosity: Quiet/Normal/Verbose levels -- Fast fix: Add `--fix-token` flag -- Safety: Full atomic with rollback -- Inline fix: Prompt immediately with time estimate -- Reporting: `--token-report` flag with --save option -- Cache: 5-minute TTL for API efficiency -- Multi-token: Check all DOT tokens -- Gamification: Track security maintenance wins -- Notifications: macOS for critical issues -- History: Store in `~/.flow/history/` -- Dry run: Yes - useful for testing -- Exit codes: Detailed codes for CI/CD -- Event hooks: finish command + weekly check (opted out of auto-heal) - -### 2026-01-23 - Phase 1 Approved for Implementation - -**Decision:** Implement Phase 1 only (12 hours), defer Phases 2-4 (30 hours) to future - -**Rationale:** -- Delivers immediate value (isolated checks, category selection, cache) -- Low risk (additive changes to existing doctor command) -- Establishes foundation for future enhancements -- Allows gathering user feedback before investing in advanced features - -**Phase 1 Scope (APPROVED):** -1. ✅ Add `--dot`, `--dot=TOKEN`, `--fix-token` flags -2. ✅ Implement category selection menu (ADHD-friendly) -3. ✅ Delegate to `dot token expiring` (unified logic) -4. ✅ Add verbosity levels (--quiet, --verbose) -5. ✅ Implement 5-minute cache manager - -**Phases 2-4 (DEFERRED):** -- Phase 2: Atomic fixes, rollback, reports, multi-token, history -- Phase 3: Gamification, notifications, event hooks -- Phase 4: Custom rules, CI/CD exit codes, advanced hooks - -**Next Review:** After Phase 1 ships in v5.17.0 and user feedback collected - ---- - -## Next Steps - -### Phase 1 Implementation (APPROVED) - -**Immediate Actions:** - -1. ✅ **Spec approved** for Phase 1 implementation (2026-01-23) -2. 📝 **Create GitHub issue** with link to this spec -3. 📋 **Break down into 5 tasks** (see Phase 1 section above): - - Task 1: Add flags (2h) - - Task 2: Category selection menu (3h) - - Task 3: Delegate to dot token expiring (2h) - - Task 4: Verbosity levels (2h) - - Task 5: Cache manager (3h) -4. 🧪 **Set up test infrastructure** (fixtures for Phase 1 only) -5. 📚 **Create documentation skeleton** (help text, quick reference) - -**Implementation Checklist:** - -- [ ] Add `--dot`, `--dot=TOKEN`, `--fix-token` flags to `commands/doctor.zsh` -- [ ] Implement category selection menu (single choice, ADHD-friendly) -- [ ] Delegate token validation to `dot token expiring` -- [ ] Add verbosity levels (--quiet, --verbose) -- [ ] Create `lib/doctor-cache.zsh` with 5-minute TTL -- [ ] Write tests: `tests/test-doctor-token-flags.zsh` (30 tests) -- [ ] Write tests: `tests/test-doctor-cache.zsh` (20 tests) -- [ ] Update help text in `commands/flow.zsh` -- [ ] Create quick reference documentation -- [ ] Validate with manual testing (use interactive dog test pattern) -- [ ] Create PR to dev branch -- [ ] Update CHANGELOG.md with Phase 1 features - -### Related Work - -- **Prerequisite:** ✅ Token automation v5.16.0 (merged) -- **Dependency:** ✅ DOT dispatcher enhancements (complete) -- **Follow-up:** Phases 2-4 pending user feedback and prioritization -- **Future:** NPM/PyPI token providers (if demand exists) - -### Future Phase Decision Point - -After Phase 1 ships in v5.17.0: -- Gather user feedback (1-2 weeks) -- Measure usage metrics (token check frequency, cache hit rate) -- Prioritize Phases 2-4 based on actual needs -- Schedule next phase if there's clear demand - ---- - -**Phase 1 specification approved. Ready for implementation.** diff --git a/docs/specs/SPEC-github-token-automation-2026-01-23.md b/docs/specs/SPEC-github-token-automation-2026-01-23.md deleted file mode 100644 index 092097235..000000000 --- a/docs/specs/SPEC-github-token-automation-2026-01-23.md +++ /dev/null @@ -1,455 +0,0 @@ -# SPEC: GitHub Token Security & Automation - -**Status:** In Progress -**Created:** 2026-01-23 -**Target Release:** v5.18.0 -**Estimated Effort:** 4 hours (1.5h Phase 1 + 2h Phase 2 + 0.5h testing/docs) -**Worktree:** `~/.git-worktrees/flow-cli/feature-token-automation` -**Branch:** `feature/token-automation` - ---- - -## Overview - -Semi-automated GitHub Personal Access Token (PAT) lifecycle management with macOS Keychain integration, expiration detection, and ADHD-optimized workflow integration across 9 flow-cli dispatchers. - -**Critical Security Context:** -- **RESOLVED:** Exposed GitHub token found in `~/.claude/settings.json` (line 205) -- **Token:** `gho_[REDACTED]` (to be revoked immediately) -- **Root Cause:** Manual token management, no expiration tracking, plain text storage - -**Key Benefits:** -- **Security:** Keychain storage with Touch ID, no plain text tokens in config -- **Automation:** 90% automated rotation (only browser token generation manual) -- **Proactive:** 7-day expiration warning (at 83 days, before 90-day expiration) -- **ADHD-Friendly:** One-command fixes, visual indicators, non-blocking checks -- **Comprehensive:** Integrated into 9 dispatchers (g, dash, work, finish, teach, doctor, gh CLI, git, MCP) - ---- - -## Primary User Story - -**As a** developer using GitHub frequently across multiple services (git, gh CLI, Claude Code, MCP servers), -**I want to** have my GitHub tokens automatically managed with expiration warnings, -**So that** I never experience authentication failures or security vulnerabilities from expired/exposed tokens. - -**Acceptance Criteria:** -1. GitHub token stored in macOS Keychain (not plain text config) ✓ -2. Token expiration detected 7 days before 90-day lifecycle ends ✓ -3. One-command token rotation: `dot token rotate github` ✓ -4. Automatic token validation before git operations (push/pull/fetch) ✓ -5. Visual status in `dash dev` showing token health ✓ -6. Token auto-synced to gh CLI configuration ✓ -7. Weekly async health check (non-blocking shell startup) ✓ -8. Complete audit trail in `~/.claude/logs/token-rotation.log` ✓ -9. Zero manual configuration after initial setup ✓ -10. Token metadata tracking (creation date, rotation history) ✓ - ---- - -## Secondary User Stories - -### 1. Proactive Expiration Warnings - -**As a** developer starting a work session, -**I want to** be notified when my GitHub token is expiring soon, -**So that** I can rotate it before it causes authentication failures. - -**Acceptance:** -- Warning appears in `work` command when token < 7 days to expiration -- Non-intrusive: single line with one-command fix -- Example: `⚠️ GitHub token expires in 5 days — Run: dot token rotate github` - -### 2. Safe Pre-Push Validation - -**As a** developer pushing code, -**I want to** be warned if my GitHub token is invalid before the push fails, -**So that** I don't waste time on failed operations. - -**Acceptance:** -- `g push`, `g pull`, `g fetch` validate token silently -- If invalid: prompt with one-command fix -- User can bypass validation if desired -- < 200ms validation overhead - -### 3. One-Command Token Rotation - -**As a** developer rotating my GitHub token, -**I want to** complete the process with a single command, -**So that** I don't need to remember multi-step procedures. - -**Acceptance:** -- `dot token rotate github` handles entire lifecycle: - 1. Backs up old token - 2. Opens browser to GitHub token generation page - 3. Waits for user to paste new token - 4. Validates new token via GitHub API - 5. Stores in Keychain with metadata - 6. Syncs to gh CLI - 7. Prompts to revoke old token - 8. Updates audit log -- Total time: ~2 minutes (30s generation + 90s automation) - -### 4. Dashboard Token Health - -**As a** developer checking project status, -**I want to** see GitHub token health in the dashboard, -**So that** I know if action is needed without running separate commands. - -**Acceptance:** -- `dash dev` shows token status line: - - `✓ GitHub token: Valid (expires in 45 days)` - - `⚠️ GitHub token: Expiring soon (5 days) — dot token rotate github` - - `✗ GitHub token: Expired — dot token rotate github` -- Color-coded: green (>7 days), yellow (≤7 days), red (expired/invalid) -- Non-blocking: dashboard loads even if token check fails - ---- - -## Technical Requirements - -### Architecture - -**Pattern:** Extension of existing `dot` dispatcher with token management subcommands - -**Design Decisions:** - -| Decision | Choice | Rationale | -|----------|--------|-----------| -| **Storage** | macOS Keychain | Instant access (<1ms) vs Bitwarden (2-5s), Touch ID support | -| **Automation Level** | Semi-automated (90%) | GitHub API doesn't support programmatic PAT generation | -| **Expiration Warning** | 7 days (83 days after creation) | Balance between proactive and non-annoying | -| **Metadata Storage** | JSON in Keychain notes field | Encrypted with token, no separate DB | -| **Health Checks** | Weekly async (non-blocking) | Avoid shell startup lag | -| **Revocation** | User approval required | Safety: prevent accidental service disruption | - -**File Structure:** - -``` -lib/ -├── dispatchers/ -│ └── dot-dispatcher.zsh # Extended with token commands (lines ~2145+) -└── keychain-helpers.zsh # Already exists, no changes needed - -hooks/ -└── token-health-check.zsh # New: Weekly async health check - -docs/ -├── reference/ -│ └── DOT-DISPATCHER-REFERENCE.md # Update with token commands -└── tutorials/ - └── github-token-setup.md # New: Setup guide - -tests/ -└── test-dot-token.zsh # New: Token management tests -``` - -### GitHub API Limitations (Critical) - -**What GitHub API CAN do:** -- ✅ Validate tokens (`GET /user`) -- ✅ Check token scopes (`GET /user` → headers) -- ✅ Revoke tokens (`DELETE /applications/{client_id}/token`) -- ✅ List user's authorized apps - -**What GitHub API CANNOT do:** -- ❌ Generate new PATs programmatically (security by design) -- ❌ Rotate tokens automatically -- ❌ Check token expiration date directly (must track via metadata) - -**Implication:** Token generation MUST be manual (open browser, copy/paste) - -### Token Lifecycle - -``` -┌─────────────────────────────────────────────────────────────┐ -│ Token Lifecycle (90 days) │ -├─────────────────────────────────────────────────────────────┤ -│ Day 0: Created, stored in Keychain with metadata │ -│ Day 1-82: Valid, no warnings │ -│ Day 83: Expiration warning triggered (7 days remaining) │ -│ Day 83+: Warnings in work/dash/finish commands │ -│ Day 90: Expired, validation fails │ -│ Day 90+: All git operations blocked until rotation │ -└─────────────────────────────────────────────────────────────┘ -``` - -**Metadata Format (Keychain notes field):** - -```json -{ - "created_at": "2026-01-23T04:30:00Z", - "expires_at": "2026-04-23T04:30:00Z", - "rotation_history": [ - {"date": "2026-01-23", "reason": "initial_setup"}, - {"date": "2026-04-16", "reason": "proactive_rotation"} - ], - "last_validated": "2026-01-23T10:15:00Z", - "scopes": ["repo", "workflow", "read:org"] -} -``` - ---- - -## Implementation Phases - -### Phase 1: Core Token Automation (1.5 hours) - -**Goal:** Semi-automated token lifecycle with keychain integration - -**Tasks:** - -1. **Task 1.1: Token Expiration Detector (15 min)** - - File: `lib/dispatchers/dot-dispatcher.zsh` (after line ~2145) - - Functions: `_dot_token_expiring()`, `_dot_token_age_days()` - - Logic: Check all `github*` secrets in keychain, validate via API, calculate age - -2. **Task 1.2: Token Metadata Tracking (15 min)** - - File: `lib/dispatchers/dot-dispatcher.zsh` - - Functions: `_dot_token_save_metadata()`, `_dot_token_get_metadata()` - - Logic: Store/retrieve JSON from Keychain notes field - -3. **Task 1.3: Semi-Automated Token Rotation (30 min)** - - File: `lib/dispatchers/dot-dispatcher.zsh` - - Function: `_dot_token_rotate_github()` - - Logic: Backup → Browser → Validate → Store → Sync → Revoke prompt - - User approval at 2 checkpoints: (1) Before opening browser, (2) Before revocation - -4. **Task 1.4: gh CLI Auto-Sync (15 min)** - - File: `lib/dispatchers/dot-dispatcher.zsh` - - Function: `_dot_token_sync_gh()` - - Logic: Export to `$GITHUB_TOKEN`, run `gh auth login --with-token` - -5. **Task 1.5: Weekly Health Check Hook (15 min)** - - File: `hooks/token-health-check.zsh` - - Trigger: Weekly (check last run timestamp) - - Logic: Async check, log to `~/.claude/logs/token-rotation.log` - -### Phase 2: flow-cli Integration (2 hours) - -**Goal:** Integrate token health into existing workflows - -**Tasks:** - -1. **Task 2.1: g Dispatcher Validation (20 min)** - - File: `lib/dispatchers/g-dispatcher.zsh` - - Hook: Before `g push`, `g pull`, `g fetch` (if GitHub remote) - - Logic: Silent validation, prompt only if invalid - -2. **Task 2.2: dash Integration (20 min)** - - File: `commands/dash.zsh` (in `_dash_show_dev()`) - - Display: Token status line in dev category - - Format: Color-coded status + expiration countdown - -3. **Task 2.3: work Command Integration (20 min)** - - File: `commands/work.zsh` - - Hook: Session start (non-blocking) - - Logic: Check expiration, show warning if < 7 days - -4. **Task 2.4: finish Command Integration (15 min)** - - File: `commands/work.zsh` (in `finish()`) - - Hook: Session end (if git operations during session) - - Logic: Reminder to rotate if expiring - -5. **Task 2.5: flow doctor Integration (30 min)** - - File: `commands/doctor.zsh` - - Category: "GitHub Integration" - - Checks: Token exists, valid, not expiring, gh CLI synced - -6. **Task 2.6: flow token Alias (5 min)** - - File: `commands/flow.zsh` - - Alias: `flow token` → `dot token` - - Rationale: Discoverability for new users - ---- - -## Integration Points (9 Dispatchers) - -| Dispatcher | Integration | Trigger | Action | -|------------|-------------|---------|--------| -| **dot** | Token commands | `dot token *` | Full token lifecycle management | -| **g** | Pre-operation validation | Before push/pull/fetch | Validate token, prompt if invalid | -| **dash** | Status display | `dash dev` | Show token health with expiration | -| **work** | Session start check | `work ` | Warn if token expiring soon | -| **finish** | Session end reminder | `finish` (if git used) | Remind to rotate if expiring | -| **teach** | Scholar MCP dependency | Before Scholar commands | Validate token (Scholar uses GitHub API) | -| **flow doctor** | Health check | `flow doctor` | Comprehensive token diagnostics | -| **gh CLI** | Auto-sync | After token rotation | Sync token to gh CLI config | -| **git credentials** | Environment export | Shell startup | Export `$GITHUB_TOKEN` | - ---- - -## Dependencies - -### Required - -- **macOS Keychain** (via `security` command) - Already available -- **curl** - For GitHub API calls - Already available -- **jq** - For JSON parsing - Already available -- **Existing `dot secret` commands** - Already implemented in keychain-helpers.zsh - -### Optional - -- **gh CLI** - For GitHub CLI integration (already a flow-cli dependency) -- **git** - For detecting GitHub remotes (already a flow-cli dependency) - -### No New Dependencies - -All required tools are already part of flow-cli's existing dependencies. - ---- - -## Security Considerations - -### Threat Model - -| Threat | Mitigation | -|--------|------------| -| **Token exposure in config** | Store in Keychain (encrypted at rest) | -| **Token exposure in logs** | Never log token value, only validation status | -| **Token exposure in shell history** | Use stdin for token input, not command args | -| **Token exposure in process list** | Pass via pipe, not environment variable (for input) | -| **Expired token usage** | Proactive validation before operations | -| **Accidental revocation** | Require user approval before revocation | -| **Token theft from Keychain** | Keychain protected by user password + Touch ID | - -### Safe Practices - -1. **Never echo tokens** - Use `read -s` for input -2. **Never log tokens** - Log validation status only -3. **Never pass in args** - Use stdin or environment variables -4. **Always validate before use** - Check GitHub API before operations -5. **Always backup before rotation** - Store old token in Keychain (30-day retention) -6. **Always audit** - Log all token operations to audit file - ---- - -## Testing Strategy - -### Unit Tests - -- Token age calculation (various dates) -- Metadata storage/retrieval (JSON parsing) -- Expiration detection (edge cases: 0 days, 7 days, 90 days) -- GitHub API validation (mock responses) - -### Integration Tests - -- Full rotation workflow (with mock GitHub API) -- g dispatcher validation (with mock token) -- dash display (various token states) -- work/finish warnings (expiration scenarios) - -### Manual Testing Checklist - -1. Initial setup: `dot token github` (create new token) -2. Validation: `dot token status` (check expiration) -3. Rotation: `dot token rotate github` (full lifecycle) -4. Integration: `g push` (validation), `dash dev` (display) -5. Health check: Wait 7 days, verify warning triggers -6. Expiration: Set metadata to 89 days ago, verify blocked operations - ---- - -## Documentation Plan - -### Reference Documentation - -- **DOT-DISPATCHER-REFERENCE.md** - Update with token commands section -- **API Reference** - Document all `_dot_token_*()` functions - -### Tutorial - -- **github-token-setup.md** - Step-by-step setup guide: - 1. Initial token creation - 2. First-time setup - 3. Rotation workflow - 4. Troubleshooting - -### Quick Reference Card - -- Update `REFCARD-*.md` with token commands -- Add token troubleshooting section - ---- - -## Rollout Plan - -### Phase 1: Core Automation (Week 1) - -- Implement token lifecycle functions -- Test with mock data -- Document API - -### Phase 2: Integration (Week 1) - -- Integrate into 9 dispatchers -- Test workflows -- Update documentation - -### Phase 3: Testing & Docs (Week 1) - -- Write test suites -- Create tutorial -- Update reference docs - -### Phase 4: Release (Week 2) - -- Merge to dev -- Create PR to main -- Release v5.18.0 - ---- - -## Success Metrics - -| Metric | Target | Measurement | -|--------|--------|-------------| -| **Time to setup** | < 5 minutes | Initial `dot token github` | -| **Time to rotate** | < 2 minutes | Full rotation workflow | -| **Validation overhead** | < 200ms | g dispatcher pre-check | -| **False positive warnings** | 0% | No warnings when token valid | -| **Authentication failures** | 0% after setup | No git/gh CLI failures | -| **User satisfaction** | No complaints | ADHD-friendly, non-intrusive | - ---- - -## Related Documents - -### Brainstorm Documents (External) - -- `~/BRAINSTORM-github-token-security-2026-01-23.md` (19KB) - Initial security analysis -- `~/BRAINSTORM-automated-token-management-2026-01-23.md` (36KB) - Automation design with 18 Q&A decisions -- `~/BRAINSTORM-flow-github-integration-2026-01-23.md` (22KB) - Integration design with 12 Q&A decisions - -### Implementation Plan - -- `~/.git-worktrees/flow-cli/feature-token-automation/IMPLEMENTATION-PLAN.md` - Detailed orchestration plan - ---- - -## Risks & Mitigations - -| Risk | Impact | Probability | Mitigation | -|------|--------|-------------|------------| -| **GitHub API rate limits** | Validation failures | Low | Cache validation results (5 min TTL) | -| **Keychain access denied** | Setup failures | Low | Clear error messages, fallback instructions | -| **User forgets to approve rotation** | Expired token | Medium | Multiple warning levels (7d, 3d, 1d, 0d) | -| **Token revocation breaks services** | Service disruption | Low | Backup old token, 30-day retention | -| **Shell startup lag** | Poor UX | Low | Async health checks, non-blocking | - ---- - -## Future Enhancements (v5.19.0+) - -1. **Multi-token support** - Separate tokens for different services -2. **Token scope validation** - Warn if insufficient permissions -3. **Remote sync** - Sync tokens across machines via encrypted channel -4. **Integration with other services** - GitLab, Bitbucket, etc. -5. **Token usage analytics** - Track which services use which tokens - ---- - -**Last Updated:** 2026-01-23 -**Status:** In Progress - Implementation started -**Next Step:** Begin Phase 1, Task 1.1 (Token Expiration Detector) diff --git a/docs/specs/SPEC-keychain-default-phase-1-2026-01-24.md b/docs/specs/SPEC-keychain-default-phase-1-2026-01-24.md deleted file mode 100644 index 47687ff54..000000000 --- a/docs/specs/SPEC-keychain-default-phase-1-2026-01-24.md +++ /dev/null @@ -1,341 +0,0 @@ -# SPEC: Keychain Default - Phase 1 - -**Date:** 2026-01-24 -**Status:** Complete -**Branch:** feature/keychain-default-phase-1 -**Effort:** 🔧 Medium-High (3-4 hours) -**Risk:** Medium (Architectural change to secret storage) - ---- - -## Overview - -Make macOS Keychain the **default** secret backend, with Bitwarden as an **optional** sync/backup target. This removes the friction of `dot unlock` for basic secret operations while preserving cloud backup capabilities for users who want them. - -### Background - -The current dual-storage architecture stores secrets in BOTH backends: -- **Bitwarden:** Cloud backup, cross-device sync (requires unlock) -- **Keychain:** Local cache, instant access (Touch ID) - -**Problem:** All token operations require `dot unlock` first, even for local-only use. - -**Solution:** Flip the default - Keychain is primary, Bitwarden is optional sync target. - ---- - -## User Stories - -### US-1: Basic Secret Storage (No Bitwarden) -```bash -# Before: Required Bitwarden -dot unlock # ← REQUIRED -dot token github # Store in both backends - -# After: Just works -dot token github # Stores in Keychain only (default) -``` - -### US-2: Optional Cloud Backup -```bash -# Configure Bitwarden sync -export FLOW_SECRET_BACKEND="both" # or: flow config set secret_backend both - -# Now both backends are used -dot token github # Stores in Keychain + Bitwarden -``` - -### US-3: Manual Sync -```bash -dot secret sync # Sync Keychain ↔ Bitwarden -dot secret sync --to-bitwarden # Push Keychain → Bitwarden -dot secret sync --from-bitwarden # Pull Bitwarden → Keychain -``` - -### US-4: Backend Selection -```bash -# Check current backend -dot secret status -# Backend: keychain (default) -# Bitwarden: not configured - -# Configure backend -export FLOW_SECRET_BACKEND="both" -dot secret status -# Backend: keychain + bitwarden -# Bitwarden: unlocked (session active) -``` - ---- - -## Configuration - -### Environment Variable - -```bash -# Backend options: -export FLOW_SECRET_BACKEND="keychain" # Default - Keychain only (no Bitwarden) -export FLOW_SECRET_BACKEND="bitwarden" # Bitwarden only (legacy mode) -export FLOW_SECRET_BACKEND="both" # Both backends (sync mode) -``` - -### Priority Matrix - -| Backend | `dot secret add` | `dot secret get` | `dot token *` | Requires Unlock | -|---------|------------------|------------------|---------------|-----------------| -| `keychain` (default) | Keychain only | Keychain only | Keychain only | No | -| `bitwarden` | Bitwarden only | Bitwarden only | Bitwarden only | Yes | -| `both` | Both backends | Keychain first, fallback Bitwarden | Both backends | For write ops | - -### Configuration File (Future) - -```yaml -# ~/.config/flow/config.yml (future enhancement) -secrets: - backend: keychain - bitwarden_sync: false -``` - ---- - -## API Changes - -### New Functions - -#### `_dot_secret_backend()` -Returns the configured backend ("keychain", "bitwarden", or "both"). - -```zsh -_dot_secret_backend() { - echo "${FLOW_SECRET_BACKEND:-keychain}" -} -``` - -#### `_dot_secret_sync()` -Syncs secrets between Keychain and Bitwarden. - -```zsh -dot secret sync # Interactive sync -dot secret sync --to-bw # Push Keychain → Bitwarden -dot secret sync --from-bw # Pull Bitwarden → Keychain -dot secret sync --status # Show sync status -``` - -#### `_dot_secret_status()` -Shows current backend configuration and status. - -```zsh -_dot_secret_status() { - local backend=$(_dot_secret_backend) - echo "Backend: $backend" - - case $backend in - keychain) - echo "Keychain: $(security list-keychains | head -1)" - echo "Bitwarden: not configured" - ;; - bitwarden) - echo "Keychain: not used" - echo "Bitwarden: $([[ -n "$BW_SESSION" ]] && echo "unlocked" || echo "locked")" - ;; - both) - echo "Keychain: $(security list-keychains | head -1)" - echo "Bitwarden: $([[ -n "$BW_SESSION" ]] && echo "unlocked" || echo "locked")" - ;; - esac -} -``` - -### Modified Functions - -#### `_dot_token_add_impl()` (lines 2017-2190) -- Check backend config before Bitwarden operations -- Skip Bitwarden if backend is "keychain" -- Add Bitwarden operations if backend is "both" - -#### `_dot_token_github()`, `_dot_token_npm()`, `_dot_token_pypi()` -- Remove mandatory `bw` requirement check when backend is "keychain" -- Keep Bitwarden logic for "bitwarden" and "both" modes - -#### `_dot_secret()` dispatcher -- Add `status` subcommand -- Add `sync` subcommand -- Route based on backend configuration - ---- - -## Implementation Plan - -### Increment 1: Backend Configuration (30 min) - -**Files to modify:** -- `lib/core.zsh` - Add `_dot_secret_backend()` function -- `lib/dispatchers/dot-dispatcher.zsh` - Add backend check at start - -**Deliverables:** -- `FLOW_SECRET_BACKEND` environment variable support -- Default to "keychain" when not set -- Backend detection function - -### Increment 2: Refactor `dot secret` (1 hour) - -**Files to modify:** -- `lib/dispatchers/dot-dispatcher.zsh` - `_dot_secret()` function - -**Changes:** -- Add `status` subcommand -- Route `add`/`get`/`list`/`delete` based on backend -- For "keychain" backend: use `_dot_kc_*` functions directly -- For "bitwarden" backend: use existing `_dot_secret_bw_*` functions -- For "both" backend: use Keychain primary, sync to Bitwarden - -### Increment 3: Refactor Token Workflows (1.5 hours) - -**Files to modify:** -- `lib/dispatchers/dot-dispatcher.zsh` - Token functions - -**Changes to `_dot_token_github()` (lines 2017-2190):** -```zsh -# Before: Always requires Bitwarden -if ! _dot_require_tool "bw" "brew install bitwarden-cli"; then - return 1 -fi - -# After: Check backend first -local backend=$(_dot_secret_backend) -if [[ "$backend" == "bitwarden" ]] || [[ "$backend" == "both" ]]; then - if ! _dot_require_tool "bw" "brew install bitwarden-cli"; then - return 1 - fi -fi -``` - -**Similar changes for:** -- `_dot_token_npm()` (lines 2532-2700) -- `_dot_token_pypi()` (lines 2711-2870) -- `_dot_token_status()` (lines 2902-3000) -- `_dot_env_*()` functions (lines 3100-3500) - -### Increment 4: Add Sync Command (45 min) - -**Files to create/modify:** -- `lib/dispatchers/dot-dispatcher.zsh` - Add `_dot_secret_sync()` - -**Functionality:** -- `dot secret sync` - Interactive comparison and sync -- `dot secret sync --to-bw` - Push all Keychain secrets to Bitwarden -- `dot secret sync --from-bw` - Pull all Bitwarden secrets to Keychain -- `dot secret sync --status` - Show differences - -### Increment 5: Update Documentation (30 min) - -**Files to update:** -- `docs/reference/REFCARD-TOKEN-SECRETS.md` -- `docs/guides/TOKEN-MANAGEMENT-COMPLETE.md` -- `lib/keychain-helpers.zsh` (help text) - -### Increment 6: Tests (30 min) - -**Files to create/update:** -- `tests/test-keychain-default.zsh` - New test suite for backend switching -- `tests/test-dot-secret-keychain.zsh` - Update existing tests - ---- - -## Migration Path - -### For New Users -- No action needed - Keychain is default -- Optional: Set `FLOW_SECRET_BACKEND=both` for cloud backup - -### For Existing Users (Bitwarden) -1. Existing secrets remain in Bitwarden -2. Run `dot secret sync --from-bitwarden` to copy to Keychain -3. Set `FLOW_SECRET_BACKEND=keychain` (now default) -4. Optional: Keep `FLOW_SECRET_BACKEND=both` for continued sync - -### Backward Compatibility -- `FLOW_SECRET_BACKEND=bitwarden` preserves old behavior -- Existing `dot unlock` workflow still works -- No breaking changes to API - ---- - -## Testing Strategy - -### Unit Tests -- Backend configuration detection -- Routing based on backend -- Keychain-only operations - -### Integration Tests -- Token add with Keychain-only backend -- Token add with both backends -- Sync operations - -### Manual Testing -```bash -# Test 1: Keychain-only (default) -unset FLOW_SECRET_BACKEND -dot token github # Should NOT require bw unlock -dot secret list # Should show Keychain secrets - -# Test 2: Bitwarden-only -export FLOW_SECRET_BACKEND=bitwarden -dot unlock -dot token github # Should require bw unlock -dot secret list # Should show Bitwarden secrets - -# Test 3: Both backends -export FLOW_SECRET_BACKEND=both -dot unlock -dot token github # Stores in both -dot secret sync --status # Shows sync status -``` - ---- - -## Success Metrics - -1. ✅ `dot token github` works without `dot unlock` (default mode) -2. ✅ `FLOW_SECRET_BACKEND` environment variable configures behavior -3. ✅ `dot secret sync` syncs between backends -4. ✅ `dot secret status` shows current configuration -5. ✅ All existing tests pass -6. ✅ New tests for backend switching pass -7. ✅ Documentation updated - ---- - -## Risks & Mitigations - -| Risk | Impact | Mitigation | -|------|--------|------------| -| Breaking existing workflows | High | `FLOW_SECRET_BACKEND=bitwarden` preserves old behavior | -| Token sync conflicts | Medium | Sync command shows diff before overwriting | -| Users confused by change | Low | Clear docs, migration guide, status command | - ---- - -## Out of Scope (Future Phases) - -1. Configuration file (`~/.config/flow/config.yml`) -2. Auto-sync on session start -3. Conflict resolution UI -4. Multi-device Keychain sync (iCloud Keychain integration) -5. Export/import for backup - ---- - -## Approval - -- [x] Spec reviewed -- [x] Implementation started -- [x] Tests passing (67 tests: 20 unit + 47 automated) -- [x] Documentation updated -- [x] PR created to dev (PR #295) - ---- - -**Created:** 2026-01-24 -**Author:** Claude (Orchestrator) diff --git a/docs/specs/SPEC-lesson-plan-extraction-2026-01-27.md b/docs/specs/SPEC-lesson-plan-extraction-2026-01-27.md deleted file mode 100644 index 4caba09f0..000000000 --- a/docs/specs/SPEC-lesson-plan-extraction-2026-01-27.md +++ /dev/null @@ -1,353 +0,0 @@ -# SPEC: Lesson Plan Extraction (#298) - -**Date:** 2026-01-27 -**Issue:** https://github.com/Data-Wise/flow-cli/issues/298 -**Status:** Ready for Implementation -**Effort:** ~4 hours -**Branch:** `feature/lesson-plan-extraction` - ---- - -## Executive Summary - -Extract embedded lesson plans from `teach-config.yml` into a separate `lesson-plans.yml` file for cleaner separation of concerns. - -**What we're doing:** -- Extract `semester_info.weeks[]` → `.flow/lesson-plans.yml` -- Add reference pointer in `teach-config.yml` -- Create `teach migrate-config` command - -**What we're NOT doing:** -- ❌ Rename teach-config.yml -- ❌ Teaching style integration -- ❌ Per-week file support - ---- - -## Design Decisions - -| Decision | Choice | Rationale | -|----------|--------|-----------| -| Data model | Reference-only | Clean separation, pointer in config | -| Migration | Explicit command | User controls when to migrate | -| File structure | Single file | Simpler to maintain, full semester view | -| Backup | Create .bak | Safe rollback option | -| Errors | Clear + hint | "Run: teach migrate-config" actionable | - ---- - -## File Structure - -### Before Migration - -``` -.flow/ -└── teach-config.yml # 657 lines (course + 14 weeks embedded) -``` - -### After Migration - -``` -.flow/ -├── teach-config.yml # ~50 lines (course meta + reference) -├── teach-config.yml.bak # Backup of original -└── lesson-plans.yml # ~600 lines (all weeks extracted) -``` - ---- - -## Implementation Tasks - -### Task 1: `teach migrate-config` Command (~1.5 hours) - -**File:** `commands/teach-migrate.zsh` (new) - -**Functions:** -- `_teach_migrate_config()` - Main migration logic -- `_teach_extract_weeks()` - Extract weeks array using yq -- `_teach_update_config()` - Remove weeks, add reference -- `_teach_migration_preview()` - Show what will change - -**UX Flow:** -```bash -$ teach migrate-config - -📦 Migrating teach-config.yml... - -Found: 14 weeks in semester_info.weeks[] -Creating: .flow/lesson-plans.yml -Backup: .flow/teach-config.yml.bak - -Preview: - - Week 1: Introduction to Experimental Design - - Week 2: CRD and One-Way ANOVA - ... (12 more) - -✓ Migration complete! - Config: .flow/teach-config.yml (657 → 52 lines) - Plans: .flow/lesson-plans.yml (14 weeks) - Backup: .flow/teach-config.yml.bak -``` - -**Flags:** -- `--dry-run` - Preview without changes -- `--force` - Skip confirmation -- `--no-backup` - Don't create .bak file - ---- - -### Task 2: Update `_teach_load_lesson_plan()` (~1 hour) - -**File:** `lib/dispatchers/teach-dispatcher.zsh` (line 491) - -**Current:** Reads from `.flow/lesson-plans/week-N.yml` (per-week files) - -**New:** Read from `.flow/lesson-plans.yml` (single file) - -```zsh -_teach_load_lesson_plan() { - local week="$1" - local plans_file=".flow/lesson-plans.yml" - - # Check if lesson plans file exists - if [[ ! -f "$plans_file" ]]; then - # Check for embedded weeks (backward compat) - if _teach_has_embedded_weeks; then - _teach_warn "Using embedded weeks in teach-config.yml" \ - "Run: teach migrate-config" - return $(_teach_load_embedded_week "$week") - fi - _teach_error "lesson-plans.yml not found" \ - "Run: teach migrate-config" - return 1 - fi - - # Parse week from single file using yq - local week_data - week_data=$(yq ".weeks[] | select(.number == $week)" "$plans_file" 2>/dev/null) - - if [[ -z "$week_data" ]]; then - _teach_error "Week $week not found in lesson-plans.yml" - return 1 - fi - - # Extract fields - TEACH_PLAN_TOPIC=$(echo "$week_data" | yq '.topic // ""') - TEACH_PLAN_STYLE=$(echo "$week_data" | yq '.style // ""') - TEACH_PLAN_OBJECTIVES=$(echo "$week_data" | yq '.objectives[]' 2>/dev/null | paste -sd '|' -) - TEACH_PLAN_SUBTOPICS=$(echo "$week_data" | yq '.subtopics[]' 2>/dev/null | paste -sd '|' -) - TEACH_PLAN_KEY_CONCEPTS=$(echo "$week_data" | yq '.key_concepts[]' 2>/dev/null | paste -sd '|' -) - TEACH_PLAN_PREREQUISITES=$(echo "$week_data" | yq '.prerequisites[]' 2>/dev/null | paste -sd '|' -) - - return 0 -} -``` - -**Helper Functions:** -- `_teach_has_embedded_weeks()` - Check if teach-config.yml has weeks[] -- `_teach_load_embedded_week()` - Backward compat loader - ---- - -### Task 3: Error Handling Updates (~30 min) - -**Files:** Various locations in `teach-dispatcher.zsh` - -**Pattern:** When lesson plan data is needed: -1. Check for `.flow/lesson-plans.yml` first -2. Fall back to embedded weeks with warning -3. Clear error if neither exists - -**Error Messages:** -``` -❌ lesson-plans.yml not found - Run: teach migrate-config - -⚠️ Using embedded weeks in teach-config.yml - Consider migrating: teach migrate-config -``` - ---- - -### Task 4: Demo Course Fixture (~30 min) - -**File:** `tests/fixtures/demo-course/.flow/teach-config.yml` (new) - -Create teach-config.yml with 5 embedded weeks matching existing lectures: - -```yaml -course: - name: "STAT-101" - full_name: "STAT 101 - Introduction to Statistics" - semester: "fall" - year: 2026 - -semester_info: - start_date: "2026-08-26" - end_date: "2026-12-15" - - weeks: - - number: 1 - topic: "Introduction to Statistics" - style: "conceptual" - objectives: - - "Understand measures of central tendency" - - "Distinguish between data types" - key_concepts: - - "descriptive-stats" - - "data-types" - - "distributions" - - - number: 2 - topic: "Probability and Inference" - style: "computational" - objectives: - - "Apply probability rules" - - "Understand sampling methods" - prerequisites: - - "Week 1: Introduction to Statistics" - - - number: 3 - topic: "Correlation and Regression" - style: "rigorous" - objectives: - - "Calculate correlation coefficients" - - "Fit linear regression models" - prerequisites: - - "Week 2: Probability and Inference" - - # Add 2 more for testing edge cases - - number: 4 - topic: "Hypothesis Testing" - style: "applied" - - - number: 5 - topic: "Course Review" - style: "applied" -``` - ---- - -### Task 5: Tests (~1 hour) - -**File:** `tests/test-lesson-plan-extraction.zsh` (new) - -**Test Categories:** - -1. **Migration Command Tests** (10 tests) - - Extracts weeks correctly - - Creates backup file - - Adds reference to config - - Handles missing config - - Dry-run mode works - - Force flag skips confirmation - - Idempotent (re-running is safe) - -2. **Loader Tests** (8 tests) - - Loads from lesson-plans.yml - - Handles missing file with error - - Falls back to embedded weeks with warning - - Parses all fields correctly - - Handles missing week number - -3. **Integration Tests** (5 tests) - - Full workflow: migrate → load → verify - - teach slides uses new loader - - teach exam uses new loader - -**Test Using Demo Course:** -```zsh -# Setup -cd tests/fixtures/demo-course -[[ -d .flow ]] || mkdir .flow - -# Test migration -teach migrate-config --dry-run -teach migrate-config - -# Verify -[[ -f .flow/lesson-plans.yml ]] || fail "lesson-plans.yml not created" -[[ -f .flow/teach-config.yml.bak ]] || fail "backup not created" - -# Test loading -source ../../lib/dispatchers/teach-dispatcher.zsh -_teach_load_lesson_plan 1 -[[ "$TEACH_PLAN_TOPIC" == "Introduction to Statistics" ]] || fail "Week 1 topic wrong" -``` - ---- - -## Orchestration Plan (Parallel Tasks) - -``` -┌────────────────────────────────────────────────────────────────┐ -│ PARALLEL IMPLEMENTATION │ -├────────────────────────────────────────────────────────────────┤ -│ │ -│ Agent 1: Migration Command Agent 2: Loader Update │ -│ ───────────────────────── ──────────────────────── │ -│ • commands/teach-migrate.zsh • Update _teach_load_... │ -│ • _teach_migrate_config() • _teach_has_embedded... │ -│ • _teach_extract_weeks() • Error handling │ -│ • Preview/dry-run • Backward compat │ -│ │ -│ Est: 1.5 hours Est: 1.5 hours │ -│ │ -├────────────────────────────────────────────────────────────────┤ -│ │ -│ Agent 3: Fixtures + Tests │ -│ ──────────────────────── │ -│ • Demo course teach-config.yml │ -│ • test-lesson-plan-extraction.zsh │ -│ • 23 tests total │ -│ │ -│ Est: 1 hour (depends on Agents 1 & 2) │ -│ │ -└────────────────────────────────────────────────────────────────┘ -``` - -**Dependency Graph:** -``` -Agent 1 (migrate cmd) ──┐ - ├──► Agent 3 (tests) -Agent 2 (loader) ──┘ -``` - ---- - -## Acceptance Criteria - -- [ ] `teach migrate-config` extracts weeks to lesson-plans.yml -- [ ] `teach migrate-config --dry-run` shows preview without changes -- [ ] Backup file created (teach-config.yml.bak) -- [ ] Loader reads from lesson-plans.yml -- [ ] Loader falls back to embedded weeks with warning -- [ ] Clear error message when lesson-plans.yml missing -- [ ] Demo course fixture has teach-config.yml with 5 weeks -- [ ] 23+ tests passing -- [ ] Existing teach commands still work - ---- - -## Files Changed - -| File | Change | -|------|--------| -| `commands/teach-migrate.zsh` | NEW - Migration command | -| `lib/dispatchers/teach-dispatcher.zsh` | UPDATE - Loader, error handling | -| `tests/fixtures/demo-course/.flow/teach-config.yml` | NEW - Test fixture | -| `tests/test-lesson-plan-extraction.zsh` | NEW - 23 tests | - ---- - -## Rollback Plan - -If issues arise: -1. Remove lesson-plans.yml -2. Restore from teach-config.yml.bak -3. Code automatically falls back to embedded weeks - ---- - -**Last Updated:** 2026-01-27 -**Author:** Claude (with DT) diff --git a/docs/specs/SPEC-nvim-documentation-2026-01-16.md b/docs/specs/SPEC-nvim-documentation-2026-01-16.md deleted file mode 100644 index 77eafea0e..000000000 --- a/docs/specs/SPEC-nvim-documentation-2026-01-16.md +++ /dev/null @@ -1,343 +0,0 @@ -# Implementation Spec: Nvim/LazyVim Documentation & Integration - -**Status:** Approved ✅ -**Created:** 2026-01-16 -**Target Release:** v5.11.0 -**Effort Estimate:** 18-22 hours -**Priority:** High -**Worktree:** `~/.git-worktrees/flow-cli/nvim-documentation` - ---- - -## Executive Summary - -Add comprehensive nvim/LazyVim documentation to flow-cli targeting complete beginners. - -**Key Insight:** nvim is already the default editor (line 54 of `commands/work.zsh`) but completely undocumented, creating a major onboarding gap. - -**User Requirements:** -- Target: Beginners (never used vim) -- Formats: Progressive tutorials + Interactive shell command + Quick reference cards -- Features: Core editing, file navigation, LSP (minimal R integration) -- Integration: work command, config edits, Git workflows -- R Integration: Minimal (skip dedicated R tutorial, users figure it out) -- Dispatcher: Not needed (current integration sufficient) - -**Total Effort:** 18-22 hours (4 tutorials, honor system checkpoints, no dispatcher) - ---- - -## Core Deliverables - -### 1. Progressive Tutorial Series (4 tutorials, 8-10 hours) - -**Tutorial 15: Nvim Quick Start** (~10 min) -- **Focus:** Survival + Basic Editing -- Absolute survival: ESC, i, :wq, :q! -- Basic insert mode editing -- Simple navigation and saving -- Integration with flow commands (work, mcp edit, dot edit) -- GIFs: opening-nvim.gif, basic-edit-save.gif, panic-exit.gif -- **File:** `docs/tutorials/15-nvim-quick-start.md` - -**Tutorial 16: Vim Motions** (~15 min) -- **Focus:** Master efficient vim navigation -- Word motions (w/b/e, W/B/E) -- Paragraph/block navigation ({/}, gg/G) -- Search and jump (/, ?, f/F, t/T) -- Text objects (ciw, di", yap, vit) -- Exercises: Navigation challenge, refactoring practice -- GIFs: word-motions.gif, text-objects.gif, search-jump.gif -- **File:** `docs/tutorials/16-vim-motions.md` - -**Tutorial 17: LazyVim Basic Features** (~15 min) -- **Focus:** Essential LazyVim plugins -- File navigation (Neo-tree, Telescope) -- Window management and splits -- Which-key helper -- Basic Git integration (gitsigns) -- Terminal integration -- Exercises: Find file with Telescope, split windows, open terminal -- GIFs: neo-tree.gif, telescope.gif, window-splits.gif, lazygit.gif -- **File:** `docs/tutorials/17-lazyvim-basics.md` - -**Tutorial 18: LazyVim Feature Showcase** (~20-30 min) -- **Focus:** Comprehensive LazyVim tour -- LazyVim vs vanilla nvim (what's different, 58 plugins) -- LSP features (code intelligence, diagnostics, auto-completion) -- Plugin ecosystem (Lazy.nvim, Mason, Treesitter) -- Advanced Git (LazyGit UI, blame, hunks) -- Customization (keymaps.lua, options.lua, plugins/) -- Language extras system (lazyvim.json) -- Flow integration (work command, config editing) -- Exercises: Install language server, custom keybinding, enable extra -- GIFs: which-key-guide.gif, lsp-workflow.gif, mason-install.gif, customization.gif -- **File:** `docs/tutorials/18-lazyvim-showcase.md` - -### 2. Quick Reference Card (2-3 hours) - -**NVIM-QUICK-REFERENCE.md** -- 1-page printable landscape reference -- Sections: Survival, Navigation, Editing, Text Objects, Search, LazyVim, Windows, LSP, Terminal, Flow Integration -- Grouped by task, bold for common commands -- R.nvim section REMOVED (minimal R integration per user feedback) -- **File:** `docs/reference/NVIM-QUICK-REFERENCE.md` - -### 3. Interactive Shell Command (6-8 hours) - -**Command:** `flow nvim-tutorial` - -**Features:** -- 5 progressive lessons (5-10 min each) -- **Honor system checkpoints** (trust user, ask "Did you do it? [y/n]") -- Real file editing practice -- Progress tracking (resume capability) -- ADHD-friendly pacing -- Integration with flow commands - -**Architecture:** - -```zsh -# File: commands/nvim-tutorial.zsh -flow-nvim-tutorial() - → _nvim_lesson_1_survival() # ESC, i, :wq, :q! + basic editing - → _nvim_lesson_2_navigation() # hjkl, w/b, gg/G - → _nvim_lesson_3_motions() # Text objects, search, jump - → _nvim_lesson_4_lazyvim_basics() # Telescope, Neo-tree, splits - → _nvim_lesson_5_lazyvim_advanced() # LSP, Git, customization - → _nvim_checkpoint_honor() # "Did you complete this? [y/n]" - → _nvim_show_progress() # Track completion -``` - -**Validation:** Honor system - no strict file checking, trust user - -**Usage:** - -```bash -flow nvim-tutorial # Start lesson 1 -flow nvim-tutorial 3 # Jump to lesson 3 -flow nvim-tutorial status # Show progress -flow nvim-tutorial reset # Start over -``` - -### 4. Documentation Updates (1-2 hours) - -**Fix work.md** -- Line 136 incorrectly says VS Code is default -- Update editor table showing nvim is default -- Add nvim configuration section with tutorial links -- **File:** `docs/commands/work.md` - -**Update installation.md** -- Add optional nvim installation section -- LazyVim setup instructions -- **File:** `docs/getting-started/installation.md` - -**Update mkdocs.yml** -- Add 4 new tutorials (15-18) to Tutorials section -- Add 1 new reference (NVIM) to Reference section - -### 5. GIF Demonstrations (4-6 hours) - -**Location:** `docs/assets/gifs/nvim/` -**Total:** 14 GIFs - -- Tutorial 15: 3 GIFs (opening, basic editing, panic exit) -- Tutorial 16: 3 GIFs (word motions, text objects, search/jump) -- Tutorial 17: 4 GIFs (neo-tree, telescope, splits, lazygit) -- Tutorial 18: 4 GIFs (which-key, LSP, mason, customization) - -**Process:** VHS script → Generate → Optimize with Gifski → Embed in docs - ---- - -## Critical Files - -### Must Create (7 new files) - -1. `docs/tutorials/15-nvim-quick-start.md` (Survival + Basic Editing) -2. `docs/tutorials/16-vim-motions.md` (Efficient Navigation) -3. `docs/tutorials/17-lazyvim-basics.md` (Essential Plugins) -4. `docs/tutorials/18-lazyvim-showcase.md` (Comprehensive Tour) -5. `docs/reference/NVIM-QUICK-REFERENCE.md` -6. `commands/nvim-tutorial.zsh` -7. `docs/assets/gifs/nvim/` (directory with 14 GIFs) - -### Must Update (3 files) - -1. `docs/commands/work.md` - Fix default editor documentation -2. `docs/getting-started/installation.md` - Add nvim/LazyVim installation -3. `mkdocs.yml` - Add navigation for tutorials and references - ---- - -## Implementation Phases - -### Phase 1: Tutorial 15 (Proof of Concept) - 3-4 hours - -- Create first tutorial following template -- 3 GIFs for basic workflow -- Test with beginners -- Iterate based on feedback - -### Phase 2: Complete Tutorial Series - 4-6 hours - -- Tutorials 16 (Vim Motions), 17 (LazyVim Basics), 18 (LazyVim Showcase) -- 11 additional GIFs -- Cross-link tutorials with clear progression -- Exercises and honor system checkpoints - -### Phase 3: Reference Card & Interactive Tutorial - 5-7 hours - -- 1 quick reference card (general nvim, no R-specific) -- Interactive shell command with guided tour and checkpoints -- Progress tracking and validation system - -### Phase 4: Documentation Updates & Deployment - 2-3 hours - -- Fix work.md -- Update installation.md and mkdocs.yml -- Build and deploy docs -- Update CHANGELOG.md - ---- - -## Verification Steps - -### 1. Documentation Testing - -```bash -# Code examples run without errors -cd docs/tutorials && grep -r '```bash' *.md | wc -l - -# Links work (internal and external) -# Manual check or use linkchecker - -# GIFs load correctly -ls -lh docs/assets/gifs/nvim/*.gif - -# Tutorial progression makes sense -cat docs/tutorials/1{5,6,7,8}-*.md | head -20 -``` - -### 2. Interactive Tutorial Testing - -```bash -# Fresh user test -flow nvim-tutorial reset -flow nvim-tutorial - -# Resume test -flow nvim-tutorial status - -# Skip test -flow nvim-tutorial 3 -``` - -### 3. User Acceptance Testing - -- 3-5 beginners (never used vim) -- Follow Tutorial 15 -- Report confusion, missing info, panic moments -- Integrate feedback (1 hour) - -### 4. Integration Testing - -```bash -# work command opens nvim correctly -work test-project - -# Editor aliases work (if any) -e ~/.zshrc - -# R dispatcher integration -cd ~/projects/r-packages/active/some-package -r test -# (nvim opens for failed tests) -``` - ---- - -## Key Exploration Findings - -### Current State - -- nvim is default editor (line 54 of commands/work.zsh) but undocumented -- 58 LazyVim plugins installed including R.nvim -- R LSP configured but languageserver R package missing -- Empty user configs (relies on LazyVim defaults) -- No editor configuration system exists in flow-cli -- `_flow_open_editor()` handles nvim with blocking mode (correct) - -### Documentation Patterns - -- ADHD-friendly: checkpoints, exercises, short paragraphs -- Tutorial template: 3-part structure (Foundation/Core/Advanced) -- Reference cards: 1-page, grouped by task -- GIF demonstrations required -- Numbered tutorials (01-14 currently exist, we add 15-18) - -### LazyVim vs Vanilla Nvim - -- LazyVim = opinionated distribution (like Ubuntu) -- 100+ sensible keybindings by default -- lazy.nvim plugin manager integrated -- Mason auto-configures LSP -- Sub-100ms startup with lazy loading -- "Extras" system for language packs (lazyvim.json) - ---- - -## Success Criteria - -1. ✅ Beginner with zero vim experience can complete Tutorial 15 in 10 minutes -2. ✅ All 4 tutorials follow ADHD-friendly conventions -3. ✅ Interactive tutorial tracks progress and allows resume -4. ✅ Reference card is 1-page printable -5. ✅ All code examples run without errors -6. ✅ GIFs demonstrate key workflows visually -7. ✅ Documentation deployed to https://Data-Wise.github.io/flow-cli/ -8. ✅ work.md correctly documents nvim as default editor -9. ✅ R package developers can use nvim with r dispatcher -10. ✅ Users can find tutorials from Getting Started page - ---- - -## Scope Finalized - -**Included:** -- ✅ 4 tutorials total (15: Survival+Editing, 16: Motions, 17: LazyVim Basics, 18: Showcase) -- ✅ Interactive shell command with 5 lessons and honor system checkpoints -- ✅ Quick reference card (general nvim) -- ✅ 14 GIF demonstrations -- ✅ Documentation fixes (work.md, installation.md) - -**Excluded (per user specifications):** -- ❌ R-specific tutorial - REMOVED (minimal R integration) -- ❌ R-NVIM-REFERENCE.md - REMOVED -- ❌ nvim dispatcher - REMOVED (not needed) - ---- - -## Tutorial Structure Rationale - -- **Tutorial 15:** Quick survival for absolute beginners (10 min) -- **Tutorial 16:** Vim motions for efficient editing (15 min) -- **Tutorial 17:** LazyVim essential plugins (15 min) -- **Tutorial 18:** Comprehensive LazyVim feature tour (30 min) -- **Total learning time:** ~70 minutes progressive path - ---- - -## Next Steps - -1. ✅ Spec created and saved -2. ✅ .STATUS updated -3. ⏳ Create feature branch worktree -4. ⏳ Start NEW session in worktree for implementation - ---- - -**Created by:** Claude Code -**Approved by:** User -**Implementation:** To be done in feature branch worktree diff --git a/docs/specs/SPEC-quarto-workflow-enhancements-2026-01-20.md b/docs/specs/SPEC-quarto-workflow-enhancements-2026-01-20.md deleted file mode 100644 index f8313c62a..000000000 --- a/docs/specs/SPEC-quarto-workflow-enhancements-2026-01-20.md +++ /dev/null @@ -1,971 +0,0 @@ -# SPEC: Quarto Workflow Enhancements for Teaching Sites - -**Status:** Draft -**Priority:** Medium -**Target:** flow-cli v4.6.0+ -**Created:** 2026-01-20 -**From Brainstorm:** FEATURE-REQUEST-QUARTO-WORKFLOW-ENHANCEMENTS.md - ---- - -## Overview - -Integrate Quarto performance optimizations and validation workflows developed for STAT 545 into flow-cli's teaching system. This enhances the `teach` dispatcher with: -- Automatic Quarto freeze caching (10-100x faster rendering) -- Git hooks for pre-commit/pre-push validation -- Interactive cache management commands -- Teaching-specific validation workflows - -**Value Proposition:** Faster development, earlier error detection, cleaner git history, best practices codified. - ---- - -## Primary User Story - -**As an instructor** managing Quarto-based course materials, -**I want** automatic freeze caching and git hooks that catch errors before commits, -**So that** I can iterate quickly (5-30s renders vs 5-10min) and never push broken content to production. - -**Acceptance Criteria:** -1. ✅ `teach init` prompts for freeze setup and hook installation -2. ✅ Pre-commit hook validates and renders changed .qmd files with interactive error handling -3. ✅ Freeze caching works transparently (10-100x speedup on subsequent renders) -4. ✅ Existing projects can upgrade via auto-detect in `teach init` -5. ✅ All features documented in comprehensive guide before implementation - ---- - -## Secondary User Stories - -### 2. Migration Path - -**As an instructor** with an existing STAT 545-style project, -**I want** to run `teach init` in my project and have it detect + add missing features, -**So that** I don't need to manually configure freeze/hooks. - -**Acceptance:** -- `teach init` in existing project detects _quarto.yml, .git/ -- Prompts: "Add freeze caching? Install hooks? Update docs?" -- Non-destructive: preserves existing config, adds missing pieces - -### 3. Error Recovery Workflow - -**As an instructor** who hits a render error during commit, -**I want** clear error output with line numbers and an interactive "Commit anyway?" prompt, -**So that** I can decide to fix now, commit with `--no-verify`, or abort. - -**Acceptance:** -- Pre-commit shows full error output with file/line context -- Offers: Fix and retry, Commit anyway (--no-verify), Abort -- Error persists in terminal after abort (for debugging) - -### 4. Cache Management - -**As an instructor** working on a long course (50+ .qmd files), -**I want** an interactive `teach cache` menu to refresh/clear/inspect cache, -**So that** I can troubleshoot cache issues without memorizing Quarto internals. - -**Acceptance:** -- `teach cache` shows menu: refresh/clear/stats/info -- `teach cache stats` shows cache size, file count, last updated -- `teach cache clear` prompts for confirmation (destructive) - ---- - -## Architecture - -```mermaid -flowchart TB - subgraph "teach Dispatcher" - init["teach init"] - hooks["teach hooks install"] - validate["teach validate"] - cache["teach cache"] - end - - subgraph "Templates (flow-cli/templates/)" - hook_pre["pre-commit.template"] - hook_push["pre-push.template"] - quarto_yml["_quarto.yml.template"] - teaching_yml["teaching.yml.template"] - end - - subgraph "Config Files" - user_config["~/.config/flow/teaching.yml"] - project_config["_quarto.yml"] - git_hooks[".git/hooks/*"] - end - - subgraph "Quarto" - render["quarto render"] - inspect["quarto inspect"] - freeze_cache["_freeze/"] - end - - init -->|prompt: freeze?| quarto_yml - init -->|prompt: hooks?| hooks - init -->|extend| user_config - - hooks -->|copy + customize| hook_pre - hooks -->|copy + customize| hook_push - hook_pre --> git_hooks - hook_push --> git_hooks - - validate -->|inspect first| inspect - validate -->|then render| render - render -->|uses| freeze_cache - - cache -->|manage| freeze_cache - - user_config -->|read config| init - user_config -->|read config| validate - project_config -->|freeze: auto| render -``` - -**Key Components:** - -1. **teach-dispatcher.zsh** - Enhanced with new subcommands -2. **teach-hooks-impl.zsh** - Hook installation logic -3. **templates/hooks/** - Git hook templates -4. **templates/quarto/_quarto.yml.template** - Freeze config -5. **teaching.yml** - Extended with quarto/hooks sections -6. **tests/fixtures/teaching-quarto-test/** - Mock project for testing - ---- - -## API Design - -### teach init (Enhanced) - -**Behavior:** -- Detects if project is new or existing -- Prompts for freeze setup (if _quarto.yml exists or will be created) -- Prompts for hook installation (if .git/ exists) -- Auto-detect upgrade scenario - -**Prompts:** - -```bash -teach init stat-545 - -# New project: -→ "Enable Quarto freeze caching? [Y/n]" -→ "Install git hooks for validation? [Y/n]" - -# Existing project (has _quarto.yml but no freeze): -→ "Detected existing Quarto project. Upgrade to freeze caching? [Y/n]" -→ "Install git hooks? [Y/n]" -``` - -**Config:** - -```yaml -# ~/.config/flow/teaching.yml (extended) -quarto: - freeze_enabled: true - freeze_auto: true # freeze: auto in _quarto.yml - -hooks: - auto_install: true - pre_commit_render: true - pre_push_full_site: true - interactive_errors: true -``` - -### teach hooks install - -**Behavior:** -- Copies templates from `templates/hooks/` to `.git/hooks/` -- Makes hooks executable -- Reads config from `teaching.yml` - -**Implementation:** - -```bash -teach hooks install - -# Steps: -1. Check .git/ exists -2. Copy pre-commit.template → .git/hooks/pre-commit -3. Copy pre-push.template → .git/hooks/pre-push -4. chmod +x .git/hooks/* -5. Echo success + instructions -``` - -**Output:** - -``` -✅ Git hooks installed: - • pre-commit: Validates + renders changed .qmd files - • pre-push: Full site render (production only) - -Disable anytime: - git commit --no-verify - teach hooks uninstall -``` - -### teach validate - -**Behavior:** -- Staged validation: `quarto inspect` first, then `quarto render` if needed -- Only validates changed files (git status --porcelain) -- Uses freeze cache for speed - -**Syntax:** - -```bash -teach validate # All changed .qmd files -teach validate lectures # Only lectures/*.qmd -teach validate --full # Full site render -``` - -**Implementation:** - -```bash -teach validate - -# Steps: -1. Find changed .qmd files: git diff --name-only --cached -2. For each file: - a. Run: quarto inspect - b. If inspect passes → quarto render - c. Collect pass/fail status -3. Show summary: - ✓ OK: 3 files - ✗ FAILED: 1 file (lectures/week-05.qmd) - Error: object 'data' not found (line 127) -``` - -### teach cache (Interactive Menu) - -**Behavior:** -- Shows menu with cache operations -- Uses fzf or simple numbered menu - -**Menu:** - -``` -teach cache - -[1] Refresh cache - Re-render all files (uses freeze) -[2] Clear cache - Delete _freeze/ directory -[3] Show stats - Size, file count, last updated -[4] Show info - Explain freeze caching - -Select [1-4]: -``` - -**Commands:** - -```bash -teach cache refresh # Direct invocation -teach cache clear # With confirmation prompt -teach cache stats # Size, count, mtime -``` - ---- - -## Data Models - -### teaching.yml Schema (Extended) - -```yaml -# ~/.config/flow/teaching.yml -version: "5.14.0" - -# Existing sections (unchanged) -semester: - current: "Spring 2025" - courses: - - stat-545 - -# NEW: Quarto section -quarto: - freeze_enabled: true - freeze_auto: true # Adds "freeze: auto" to _quarto.yml - freeze_method: "auto" # auto | true | false - - # Validation - validate_on_commit: true - validate_show_output: true - -# NEW: Hooks section -hooks: - auto_install: true # Prompt on teach init - pre_commit_render: true - pre_push_full_site: true - interactive_errors: true # Ask "Commit anyway?" on error - - # Bypass env vars (documented) - env: - disable_render: "QUARTO_PRE_COMMIT_RENDER=0" - disable_hooks: "git commit --no-verify" -``` - -### _quarto.yml Template - -```yaml -project: - type: website - output-dir: _site - - # Quarto freeze caching - execute: - freeze: auto # Only re-execute changed files - -format: - html: - theme: cosmo - css: styles.css - toc: true -``` - -### Hook Templates - -**templates/hooks/pre-commit.template:** - -```bash -#!/usr/bin/env zsh -# Installed by: teach hooks install -# Disable: QUARTO_PRE_COMMIT_RENDER=0 git commit -m "..." - -# Read config -source ~/.config/flow/teaching.yml - -# Find changed .qmd files -CHANGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep '\.qmd$') - -if [[ -z "$CHANGED_FILES" ]]; then - exit 0 # No .qmd files changed -fi - -echo "Validating changed Quarto files..." - -FAILED=() - -for file in ${(f)CHANGED_FILES}; do - echo " Checking: $file" - - # Step 1: Syntax check - if ! quarto inspect "$file" &>/dev/null; then - echo " ✗ Syntax error" - FAILED+=("$file") - continue - fi - - # Step 2: Render (uses freeze cache) - if ! quarto render "$file" --quiet; then - echo " ✗ Render failed" - FAILED+=("$file") - continue - fi - - echo " ✓ OK" -done - -if [[ ${#FAILED[@]} -gt 0 ]]; then - echo "" - echo "❌ Validation failed for ${#FAILED[@]} file(s):" - for file in $FAILED; do - echo " • $file" - done - echo "" - echo "Options:" - echo " 1. Fix errors and retry commit" - echo " 2. Bypass validation: git commit --no-verify" - echo "" - read "response?Commit anyway? [y/N] " - - if [[ "$response" =~ ^[Yy]$ ]]; then - exit 0 - else - exit 1 - fi -fi - -exit 0 -``` - -**templates/hooks/pre-push.template:** - -```bash -#!/usr/bin/env zsh -# Installed by: teach hooks install -# Runs full site render before pushing to production - -# Only run on production branch -BRANCH=$(git branch --show-current) -if [[ "$BRANCH" != "production" && "$BRANCH" != "main" ]]; then - exit 0 -fi - -echo "Running full site render (production push)..." - -if ! quarto render; then - echo "❌ Full site render failed" - echo "Fix errors or push to draft branch for testing" - exit 1 -fi - -echo "✅ Full site render passed" -exit 0 -``` - ---- - -## Dependencies - -### Required - -| Dependency | Version | Purpose | -|-----------|---------|---------| -| Quarto CLI | >= 1.3 | Rendering + freeze | -| Git | >= 2.0 | Hooks | -| ZSH | >= 5.8 | flow-cli runtime | -| yq | >= 4.0 | YAML config parsing | - -### Optional - -| Dependency | Purpose | -|-----------|---------| -| fzf | Interactive menus | -| bat | Syntax highlighting in diffs | - ---- - -## UI/UX Specifications - -### User Flow: New Project Setup - -```mermaid -flowchart TD - Start["teach init stat-545"] --> Prompt1{Enable freeze?} - - Prompt1 -->|Yes| AddFreeze["Add freeze: auto to _quarto.yml"] - Prompt1 -->|No| SkipFreeze["Skip freeze config"] - - AddFreeze --> Prompt2{Install hooks?} - SkipFreeze --> Prompt2 - - Prompt2 -->|Yes| InstallHooks["teach hooks install"] - Prompt2 -->|No| SkipHooks["Skip hooks"] - - InstallHooks --> CreateDocs["Create README + CLAUDE.md"] - SkipHooks --> CreateDocs - - CreateDocs --> Summary["Show summary"] - Summary --> End["Ready to work"] -``` - -### User Flow: Existing Project Upgrade - -```mermaid -flowchart TD - Start["teach init (in existing project)"] --> Detect{Detect config} - - Detect -->|Has _quarto.yml| CheckFreeze{Has freeze?} - Detect -->|No _quarto.yml| NewProject["Treat as new"] - - CheckFreeze -->|No| PromptFreeze["Upgrade to freeze? [Y/n]"] - CheckFreeze -->|Yes| CheckHooks{Has hooks?} - - PromptFreeze -->|Yes| AddFreeze["Add freeze: auto"] - PromptFreeze -->|No| CheckHooks - - AddFreeze --> CheckHooks - - CheckHooks -->|No| PromptHooks["Install hooks? [Y/n]"] - CheckHooks -->|Yes| UpdateDocs["Update docs only"] - - PromptHooks -->|Yes| InstallHooks["teach hooks install"] - PromptHooks -->|No| UpdateDocs - - InstallHooks --> UpdateDocs - UpdateDocs --> Summary["Show upgrade summary"] - Summary --> End["Upgraded"] -``` - -### teach cache Menu (Interactive) - -``` -┌─────────────────────────────────────────────────┐ -│ teach cache - Manage Quarto Freeze Cache │ -├─────────────────────────────────────────────────┤ -│ │ -│ Cache: _freeze/ │ -│ Size: 147 MB (342 files) │ -│ Last updated: 2 hours ago │ -│ │ -│ [1] Refresh cache │ -│ Re-render all files (respects freeze) │ -│ │ -│ [2] Clear cache │ -│ Delete _freeze/ directory (destructive!) │ -│ │ -│ [3] Show stats │ -│ Detailed cache statistics │ -│ │ -│ [4] Show info │ -│ Explain how freeze caching works │ -│ │ -│ [q] Quit │ -│ │ -├─────────────────────────────────────────────────┤ -│ Select [1-4, q]: │ -└─────────────────────────────────────────────────┘ -``` - -### Error Output (Pre-commit Hook) - -``` -Validating changed Quarto files... - Checking: lectures/week-05_factorial-anova.qmd - ✓ OK - Checking: syllabus/syllabus-final.qmd - ✗ Render failed - -════════════════════════════════════════════════════════ - Error in syllabus/syllabus-final.qmd (line 127) -════════════════════════════════════════════════════════ - - Error: object 'exam_data' not found - - Context: - 125 | ## Exam Schedule - 126 | - > 127 | table(exam_data) - | ^~~~~~~~~ - 128 | - 129 | ### Midterm - -════════════════════════════════════════════════════════ - Options -════════════════════════════════════════════════════════ - - 1. Fix error and retry commit - 2. Bypass validation: git commit --no-verify - -Commit anyway? [y/N] -``` - -### Accessibility Checklist - -- ✅ All prompts have clear Y/n defaults -- ✅ Error messages include file/line context -- ✅ Interactive menus support keyboard navigation -- ✅ Colors use sufficient contrast (green/red/yellow) -- ✅ Non-interactive mode available (--yes, --no flags) -- ✅ Screen reader friendly (structured output, no ASCII art) - ---- - -## Open Questions - -### 1. Template Customization - -**Question:** Should hook templates support project-specific customization? - -**Options:** -- A. Copy once, users edit `.git/hooks/pre-commit` directly -- B. Store customizations in `teaching.yml`, regenerate hooks on teach hooks install -- C. Hybrid: Templates have `# CUSTOM START/END` blocks preserved across reinstalls - -**Recommendation:** Option A (simplest). Users who want custom logic can edit hooks directly. Document common patterns in guide. - ---- - -### 2. Multi-Author Repos - -**Question:** How should freeze caching work in multi-author teaching repos? - -**Context:** `_freeze/` is gitignored, so each author has their own cache. If Alice renders `lecture-01.qmd`, Bob won't have that cache. - -**Options:** -- A. Document limitation (current approach) -- B. Add `teach cache sync` to share cache via git-lfs or external storage -- C. Recommend single-author for teaching repos - -**Recommendation:** Option A + C. Teaching repos are typically single-author. Document as known limitation. - ---- - -## Review Checklist - -- [ ] All 8 user questions integrated into spec -- [ ] Architecture diagram matches implementation plan -- [ ] API design covers all commands (init, hooks, validate, cache) -- [ ] Data models include teaching.yml schema + hook templates -- [ ] User flows documented (new project, upgrade, error handling) -- [ ] Error output UX designed -- [ ] Accessibility checklist complete -- [ ] Open questions identified -- [ ] Implementation notes cover edge cases -- [ ] Test strategy defined (mock project) - ---- - -## Implementation Notes - -### Phase 1 Scope (v4.6.0) - -**From User Answers:** -- ✅ Priority: Freeze + Hooks first (core infrastructure) -- ✅ Freeze setup: Prompt on init (not auto-enable) -- ✅ Hook install: Auto-install with prompt (Y/n default) -- ✅ Pre-commit: Render by default (QUARTO_PRE_COMMIT_RENDER=0 to disable) -- ✅ Error handling: Interactive ("Commit anyway?") - -**Tasks:** - -1. **Create hook templates** (`templates/hooks/`) - - pre-commit.template (syntax check + render) - - pre-push.template (full site render) - - Use ZSH, not bash (flow-cli is ZSH-native) - -2. **Extend teach-dispatcher.zsh** - - `teach init` enhancements (freeze + hooks prompts) - - `teach hooks install` implementation - - Auto-detect upgrade logic - -3. **Extend teaching.yml schema** - - Add `quarto:` section - - Add `hooks:` section - - Validate with yq - -4. **Create teach-hooks-impl.zsh** - - Hook installation logic - - Template copying + chmod +x - - Uninstall function - -5. **Add _quarto.yml.template** - - Include `freeze: auto` config - - Document freeze in comments - -6. **Update teach init** - - Detect existing projects - - Prompt for freeze: "Enable Quarto freeze caching? [Y/n]" - - Prompt for hooks: "Install git hooks? [Y/n]" - - Non-destructive upgrades - -7. **Testing** - - Create `tests/fixtures/teaching-quarto-test/` mock project - - Unit tests for teach hooks install - - Integration test: full workflow (init → commit → render) - -8. **Documentation** - - Write `docs/guides/TEACHING-QUARTO-WORKFLOW.md` (comprehensive) - - Update `docs/reference/MASTER-DISPATCHER-GUIDE.md#teach-dispatcher` - - Add examples to README - -### Edge Cases - -**1. No .git/ directory:** -- `teach hooks install` → Error: "Not a git repository. Run git init first." - -**2. Hooks already exist:** -- Detect existing hooks -- Prompt: "Overwrite existing hooks? [y/N]" -- Backup existing hooks to `.git/hooks/pre-commit.backup` - -**3. No _quarto.yml:** -- `teach init` → Offer to create _quarto.yml with freeze -- If declined, skip freeze setup - -**4. Freeze cache corrupt:** -- `teach cache clear` → Delete _freeze/ and recommend `quarto render` -- `teach cache refresh` → Clear + full render - -**5. Pre-commit render takes > 5 minutes:** -- Show progress spinner -- Allow Ctrl-C to abort -- Suggest: "This is slow. Run teach cache refresh to populate cache." - -### Performance Targets - -| Scenario | Time Budget | Notes | -|----------|-------------|-------| -| teach hooks install | < 1s | Template copy + chmod | -| Pre-commit (1 file, cached) | < 5s | inspect + render with freeze | -| Pre-commit (5 files, cached) | < 15s | Parallel render if possible | -| teach cache clear | < 1s | rm -rf _freeze/ | -| teach cache stats | < 1s | du -sh + file count | - -### Testing Strategy - -**1. Unit Tests:** -- `test-teach-hooks-unit.zsh` - Hook installation logic -- `test-teach-quarto-config-unit.zsh` - YAML config parsing -- `test-teach-cache-unit.zsh` - Cache commands - -**2. Integration Tests:** -- Create mock project: `tests/fixtures/teaching-quarto-test/` -- Test full workflow: init → edit → commit → render -- Verify freeze caching works -- Test error scenarios (syntax error, render failure) - -**3. Manual Testing:** -- Run on real STAT 545 project (~/projects/teaching/stat-545) -- Verify backward compatibility -- Test upgrade path (existing project) - ---- - ---- - -## Advanced Features (Phase 2+) - -### Quarto Profiles (v4.7.0) - -**User Story:** As an instructor, I want dev builds to use freeze caching but production builds to always render fresh. - -**Implementation:** - -```yaml -# _quarto.yml -project: - type: website - -# Default (dev) -execute: - freeze: auto - ---- -# Production profile -profile: production -execute: - freeze: false # Always fresh -``` - -**teach deploy enhancement:** - -```zsh -_teach_deploy() { - quarto render --profile production - gh pr create --base production --head draft -} -``` - -**Config addition:** - -```yaml -# teaching.yml -quarto: - profiles: - dev: - freeze: auto - production: - freeze: false -``` - -**Rationale:** Dev needs speed (freeze), production needs correctness (no stale cache). - ---- - -### R Package Dependency Management (v4.7.0) - -**User Story:** As an instructor, I want pre-commit to detect missing R packages and offer to install them. - -**Pre-commit Hook Addition:** - -```zsh -check_r_dependencies() { - local file="$1" - local packages=($(grep -oP 'library\(\K[^)]+' "$file")) - local missing=() - - for pkg in $packages; do - Rscript -e "library($pkg)" &>/dev/null || missing+=("$pkg") - done - - if [[ ${#missing[@]} -gt 0 ]]; then - echo "⚠️ Missing R packages: ${missing[@]}" - read "response?Install now? [Y/n] " - if [[ "$response" =~ ^[Yy]?$ ]]; then - for pkg in $missing; do - Rscript -e "install.packages('$pkg')" - done - fi - fi -} -``` - -**Edge Case:** Skip if renv detected (`.Rprofile` or `renv.lock` exists). - ---- - -### teach doctor (v4.6.0) - -**User Story:** As an instructor, I want to verify my Quarto/freeze/hooks setup with one command. - -**Implementation:** - -```zsh -_teach_doctor() { - echo "┌─────────────────────────────────────────────┐" - echo "│ teach doctor - Health Check │" - echo "├─────────────────────────────────────────────┤" - - local issues=0 - - # Check Quarto - if ! command -v quarto &>/dev/null; then - echo "│ ✗ Quarto not found" - ((issues++)) - else - echo "│ ✓ Quarto $(quarto --version)" - fi - - # Check Git - if ! command -v git &>/dev/null; then - echo "│ ✗ Git not found" - ((issues++)) - else - echo "│ ✓ Git $(git --version | awk '{print $3}')" - fi - - # Check freeze config - if [[ -f _quarto.yml ]]; then - local freeze=$(yq '.project.execute.freeze' _quarto.yml) - if [[ "$freeze" == "auto" ]]; then - echo "│ ✓ Freeze caching enabled" - else - echo "│ ⚠ Freeze caching not enabled" - fi - fi - - # Check hooks - [[ -x .git/hooks/pre-commit ]] && echo "│ ✓ Pre-commit hook installed" || echo "│ ⚠ Pre-commit hook missing" - [[ -x .git/hooks/pre-push ]] && echo "│ ✓ Pre-push hook installed" || echo "│ ⚠ Pre-push hook missing" - - # Check cache - if [[ -d _freeze ]]; then - local size=$(du -sh _freeze | cut -f1) - echo "│ ✓ Freeze cache: $size" - else - echo "│ ℹ No freeze cache yet" - fi - - echo "└─────────────────────────────────────────────┘" - return $issues -} -``` - -**Usage:** - -```bash -teach doctor # Health check -teach doctor --fix # Auto-fix issues (future) -``` - ---- - -### _freeze/ Commit Prevention (v4.6.0) - -**User Story:** As an instructor, I want to be blocked from accidentally committing the 500MB _freeze/ cache. - -**Pre-commit Hook Addition:** - -```zsh -# Check if _freeze/ is staged -if git diff --cached --name-only | grep -q '^_freeze/'; then - echo "❌ ERROR: _freeze/ directory is staged" - echo "" - echo "Fix:" - echo " git restore --staged _freeze/" - echo " echo '_freeze/' >> .gitignore" - echo "" - exit 1 -fi -``` - -**Rationale:** Prevents catastrophic 500MB commits that bloat repo. - ---- - -### Custom Validation Scripts (v4.8.0) - -**User Story:** As an instructor, I want to run custom checks (e.g., "ensure all lectures have learning objectives"). - -**Config:** - -```yaml -# teaching.yml -validation: - commands: - - name: "Check learning objectives" - script: "./scripts/check-learning-objectives.sh" - when: "lectures/*.qmd" - - - name: "Lint YAML frontmatter" - script: "yamllint --strict" - when: "*.qmd" -``` - -**teach validate enhancement:** - -```zsh -_teach_validate() { - # 1. Run Quarto validation (existing) - # ... - - # 2. Run custom validators - local validators=($(yq '.validation.commands[].name' teaching.yml)) - for i in {1..${#validators[@]}}; do - local name=$(yq ".validation.commands[$((i-1))].name" teaching.yml) - local script=$(yq ".validation.commands[$((i-1))].script" teaching.yml) - - echo "Running: $name" - eval "$script" || ((failed++)) - done -} -``` - -**Example Custom Validator:** - -```bash -#!/bin/bash -# scripts/check-learning-objectives.sh - -for file in "$@"; do - grep -q "^## Learning Objectives" "$file" || { - echo "Missing 'Learning Objectives' in $file" - exit 1 - } -done -``` - -**Rationale:** Course-specific checks without modifying flow-cli. - ---- - -## History - -- **2026-01-20**: Initial spec from deep brainstorm (10 questions) -- **2026-01-20**: User answers integrated into spec (Q1-12) - - Freeze: Prompt on init - - Hooks: Auto-install with prompt - - Pre-commit: Render by default, interactive errors - - Templates in repo - - Validation: Staged (inspect → render) - - Config: Extend teaching.yml - - Cache: Interactive menu - - Testing: Mock project - - Docs: Comprehensive upfront - - Migration: Auto-detect upgrade - - Performance: Parallel rendering - - Integration: teach deploy --validate (opt-in) -- **2026-01-20**: Advanced features from deep dive (Q13-17) - - Profiles: Full profile support (dev vs production) → v4.7.0 - - Dependencies: Auto-install prompt for R packages → v4.7.0 - - Health check: teach doctor (full validation) → v4.6.0 - - Freeze conflicts: Pre-commit prevention → v4.6.0 - - Extensibility: Config-based custom validators → v4.8.0 - ---- - -**Ready for:** Implementation (v4.6.0 Phase 1) -**Next Steps:** -1. Review this spec -2. Create feature branch: `feature/quarto-workflow-v4.6.0` -3. Start with hook templates (Task 1) -4. Write comprehensive guide in parallel (Task 8) diff --git a/docs/specs/SPEC-scholar-teaching-2026-01-11.md b/docs/specs/SPEC-scholar-teaching-2026-01-11.md deleted file mode 100644 index eb8792a1d..000000000 --- a/docs/specs/SPEC-scholar-teaching-2026-01-11.md +++ /dev/null @@ -1,654 +0,0 @@ -# SPEC: Scholar Plugin Teaching Features - -**Feature:** Expand scholar plugin with teaching content generation -**Status:** draft -**Created:** 2026-01-11 -**From Brainstorm:** BRAINSTORM-scholar-teaching-2026-01-11.md -**Branch Strategy:** feature/scholar-teaching → scholar dev → scholar main - ---- - -## Metadata - -| Field | Value | -|-------|-------| -| **Status** | draft | -| **Priority** | High (parallel with flow-cli teaching-workflow) | -| **Complexity** | Medium-High (28-38 hours, 5 weeks) | -| **Risk Level** | Low (standalone feature, optional integration) | -| **Dependencies** | Node.js >= 18.0.0, Claude API, examark (optional) | -| **Related Specs** | SPEC-teaching-workflow-v2.md (flow-cli) | -| **Target Release** | scholar v2.0.0 | - ---- - -## Overview - -Expand the scholar plugin (currently research-focused) to support teaching workflows by adding a `/teaching:*` namespace with 4-6 commands for generating educational materials. Scholar becomes a unified academic plugin supporting both research and teaching use cases. - -**Key Features:** -- 5 teaching commands: `/teaching:exam`, `/teaching:quiz`, `/teaching:lecture`, `/teaching:assignment`, `/teaching:syllabus` -- 4 output formats: Markdown, Quarto, LaTeX, JSON/YAML -- Hybrid AI + template generation -- Critical quality validation before save -- Integration with flow-cli via `.flow/teach-config.yml` - -**Current State:** -- Scholar has 14 research commands, 17 research skills -- Package.json mentions teaching keywords (quiz, exam, syllabus, rubric) -- **ZERO teaching commands implemented** - -**Parallel Development:** -- Developed alongside flow-cli teaching-workflow (SPEC-teaching-workflow-v2.md) -- flow-cli wraps scholar commands for workflow automation -- Scholar provides content generation capabilities - ---- - -## Primary User Story - -**As a** course instructor using flow-cli for teaching workflow automation, -**I want** scholar to generate high-quality exam questions, lecture outlines, and assignments from topics, -**So that** I can create course materials efficiently without sacrificing quality or consistency. - -**Acceptance Criteria:** -- Scholar generates valid exam with 10 questions in < 30 seconds -- Generated content passes schema validation (100% success rate) -- Output format matches requested format (md/qmd/tex/json) -- Integration with flow-cli's `teach-exam` command works seamlessly -- User can edit generated content before final save - ---- - -## Secondary User Stories - -### User Story 2: Quality Assurance - -**As a** meticulous instructor, -**I want** scholar to validate generated content for completeness and format correctness, -**So that** I don't waste time fixing basic errors in AI-generated materials. - -**Acceptance Criteria:** -- Validation catches missing answer keys -- Validation detects unbalanced LaTeX delimiters -- Validation warns about incomplete generation -- `--no-validate` flag allows skipping for experienced users - -### User Story 3: Format Flexibility - -**As an** instructor publishing to multiple platforms, -**I want** scholar to output in Markdown, Quarto, LaTeX, or JSON, -**So that** I can use the same generated content for Canvas, course website, and PDF handouts. - -**Acceptance Criteria:** -- All 4 formats supported -- Format conversion maintains content fidelity -- Default format read from `.flow/teach-config.yml` - -### User Story 4: Context-Aware Generation - -**As an** instructor teaching at different levels, -**I want** scholar to read course context from flow-cli configuration, -**So that** generated materials match the course difficulty and style automatically. - -**Acceptance Criteria:** -- Scholar reads `.flow/teach-config.yml` -- Generation respects course level (undergraduate/graduate) -- Difficulty setting affects question complexity -- Style (formal/conversational) affects tone - ---- - -## Acceptance Criteria (Comprehensive) - -### Functional Requirements - -- [ ] `/teaching:exam` generates 10-question exam in < 30s -- [ ] `/teaching:quiz` generates 5-question quiz in < 15s -- [ ] `/teaching:lecture` generates lecture outline in < 20s -- [ ] `/teaching:assignment` generates problem set in < 25s -- [ ] `/teaching:syllabus` generates course syllabus in < 30s -- [ ] All commands support `--format md|qmd|tex|json` -- [ ] Validation catches 100% of schema violations -- [ ] Generated files have correct metadata (date, course, topic) - -### Integration Requirements - -- [ ] Scholar reads `.flow/teach-config.yml` successfully -- [ ] flow-cli's `teach-exam` wraps `/teaching:exam` correctly -- [ ] Generated markdown converts with examark (QTI validation) -- [ ] Generated Quarto renders without errors -- [ ] Generated LaTeX compiles to PDF - -### Quality Requirements - -- [ ] User rating of generated content > 8/10 -- [ ] Edit time < 10 min from generation to finalized material -- [ ] AI generation failure rate < 5% -- [ ] Validation false positive rate < 1% - ---- - -## Technical Requirements - -### Architecture - -**New Directory Structure:** - -``` -scholar/ -├── src/ -│ ├── teaching/ # NEW teaching namespace -│ │ ├── commands/ -│ │ │ ├── exam.js -│ │ │ ├── quiz.js -│ │ │ ├── lecture.js -│ │ │ ├── assignment.js -│ │ │ └── syllabus.js -│ │ ├── templates/ -│ │ │ ├── exam.json -│ │ │ ├── quiz.json -│ │ │ ├── lecture.json -│ │ │ ├── assignment.json -│ │ │ └── syllabus.json -│ │ ├── validators/ -│ │ │ ├── schema.js -│ │ │ ├── latex.js -│ │ │ └── markdown.js -│ │ ├── generators/ -│ │ │ ├── ai-generator.js -│ │ │ └── template-filler.js -│ │ └── formatters/ -│ │ ├── markdown.js -│ │ ├── quarto.js -│ │ ├── latex.js -│ │ └── json.js -│ └── research/ # Existing research commands -│ └── ... -``` - -**Data Flow:** - -```mermaid -graph TD - A[User: /teaching:exam ANOVA] --> B[Read .flow/teach-config.yml] - B --> C[Load exam.json template] - C --> D[AI generates content for template fields] - D --> E[Validate against schema] - E --> F{Valid?} - F -->|Yes| G[Format output md/qmd/tex/json] - F -->|No| H[Return errors to user] - G --> I[Save to exams/ directory] - I --> J[Return file path to flow-cli] -``` - -### API Design - -**Command Signature:** - -```bash -/teaching:exam [options] -``` - -**Options:** - -| Option | Type | Default | Description | -|--------|------|---------|-------------| -| `--format` | string | md | Output format (md\|qmd\|tex\|json) | -| `--duration` | number | 60 | Exam duration in minutes | -| `--points` | number | 100 | Total points | -| `--questions` | number | 10 | Number of questions | -| `--types` | string | mc,sa | Question types (mc=multiple-choice, sa=short-answer, essay, tf=true-false) | -| `--config` | path | .flow/teach-config.yml | Config file path | -| `--output` | path | exams/ | Output directory | -| `--no-validate` | flag | false | Skip validation | - -**Return Value:** - -```json -{ - "success": true, - "file_path": "exams/anova-exam-2026-01-11.md", - "metadata": { - "topic": "ANOVA concepts", - "questions": 10, - "points": 100, - "duration": 60, - "format": "markdown" - }, - "validation": { - "schema_valid": true, - "answer_key_complete": true, - "latex_valid": true, - "warnings": [] - } -} -``` - ---- - -## Data Models - -### Exam Template Schema (`src/teaching/templates/exam.json`) - -```json -{ - "schema_version": "1.0", - "template_type": "exam", - "metadata": { - "title": { - "type": "string", - "required": true, - "description": "Exam title (e.g., 'Midterm Exam - ANOVA')" - }, - "course": { - "type": "string", - "required": true, - "source": "config", - "description": "Course name from .flow/teach-config.yml" - }, - "date": { - "type": "string", - "required": true, - "default": "auto", - "description": "ISO 8601 date (auto-filled with today)" - }, - "duration": { - "type": "number", - "required": true, - "unit": "minutes", - "description": "Exam duration" - }, - "total_points": { - "type": "number", - "required": true, - "description": "Total points for the exam" - }, - "instructions": { - "type": "string", - "required": false, - "description": "General exam instructions" - } - }, - "questions": { - "type": "array", - "minLength": 1, - "items": { - "id": { - "type": "string", - "auto": true, - "pattern": "Q[0-9]+", - "description": "Question ID (Q1, Q2, ...)" - }, - "type": { - "type": "string", - "enum": ["multiple-choice", "short-answer", "essay", "true-false"], - "required": true - }, - "points": { - "type": "number", - "required": true, - "min": 1 - }, - "text": { - "type": "string", - "required": true, - "ai_generated": true, - "description": "Question text" - }, - "options": { - "type": "array", - "required_if": "type == multiple-choice", - "items": { - "type": "string" - }, - "description": "Answer options for MC questions" - }, - "answer": { - "type": "string", - "required": true, - "description": "Correct answer or answer key" - }, - "rubric": { - "type": "string", - "required_if": "type == essay", - "description": "Grading criteria for essay questions" - }, - "difficulty": { - "type": "string", - "enum": ["easy", "medium", "hard"], - "required": false - } - } - }, - "answer_key": { - "type": "object", - "required": true, - "description": "Map of question IDs to correct answers" - } -} -``` - -### Course Configuration Extension (`.flow/teach-config.yml`) - -```yaml -# Existing flow-cli config... - -# NEW: Scholar-specific section -scholar: - course_info: - level: "undergraduate" # or "graduate" - field: "statistics" - difficulty: "intermediate" # "beginner", "intermediate", "advanced" - - defaults: - exam_format: "markdown" # md, qmd, tex, json - lecture_format: "quarto" - question_types: - - "multiple-choice" - - "short-answer" - - "essay" - - style: - tone: "formal" # "formal", "conversational" - notation: "statistical" # LaTeX math notation style - examples: true # Include worked examples -``` - ---- - -## Dependencies - -### Required - -| Dependency | Version | Purpose | -|------------|---------|---------| -| Node.js | >= 18.0.0 | Runtime environment | -| Claude API | Latest | AI content generation | -| js-yaml | Latest | YAML config parsing | -| ajv | Latest | JSON schema validation | - -### Optional - -| Dependency | Version | Purpose | -|------------|---------|---------| -| examark | Latest | Markdown → Canvas QTI conversion | -| quarto | >= 1.4 | Quarto format rendering | -| texlive | Latest | LaTeX → PDF compilation | - -### Development Dependencies - -| Dependency | Version | Purpose | -|------------|---------|---------| -| jest | Latest | Unit testing | -| eslint | Latest | Code linting | -| prettier | Latest | Code formatting | - ---- - -## UI/UX Specifications - -### Command Output (Terminal) - -**Success Output:** - -``` -📝 Generating exam: ANOVA concepts - -Context (from config): - Course: STAT 545 - Level: Undergraduate - Difficulty: Intermediate - -Questions: - ✓ 5 multiple-choice (5 points each) - ✓ 2 short-answer (10 points each) - ✓ 1 essay (25 points) - -Validation: - ✓ Schema valid - ✓ Answer key complete - ✓ LaTeX syntax valid - -✅ Exam saved: exams/anova-exam-2026-01-11.md - -Next steps: - 1. Review: cat exams/anova-exam-2026-01-11.md - 2. Edit: $EDITOR exams/anova-exam-2026-01-11.md - 3. Convert: examark exams/anova-exam-2026-01-11.md -o exam.qti.zip -``` - -**Error Output:** - -``` -📝 Generating exam: ANOVA concepts - -Context (from config): - Course: STAT 545 - Level: Undergraduate - Difficulty: Intermediate - -Questions: - ✓ 5 multiple-choice (5 points each) - ✓ 2 short-answer (10 points each) - ✗ 1 essay (generation failed) - -Validation: - ✗ Schema invalid: Missing answer for Q8 - ⚠ LaTeX warning: Unbalanced $ delimiter in Q3 - -❌ Exam generation failed - -Errors: - 1. Question Q8: Missing answer key - 2. Question Q3: LaTeX math syntax error (line 45) - -Suggestions: - • Retry with --questions 9 to skip failed question - • Fix LaTeX manually after generation with --no-validate -``` - -### Generated File Format - -**Markdown Output (`exams/anova-exam-2026-01-11.md`):** - -```markdown -# Exam: ANOVA Concepts - -**Course:** STAT 545 - Design of Experiments -**Date:** 2026-01-11 -**Duration:** 60 minutes -**Total Points:** 100 - ---- - -## Instructions - -Answer all questions. Show your work for full credit. - ---- - -## Questions - -### Q1 (5 points) - Multiple Choice - -Which assumption is NOT required for one-way ANOVA? - -a) Independence of observations -b) Normality of residuals -c) Equal group sizes -d) Homogeneity of variance - -**Answer:** c - ---- - -### Q2 (10 points) - Short Answer - -Explain the purpose of post-hoc tests in ANOVA. Give an example of when you would use them. - -**Answer:** Post-hoc tests are used after a significant F-test to determine which specific group means differ. Example: After finding a significant difference in mean test scores across three teaching methods, a Tukey HSD test identifies which pairs of methods differ significantly. - ---- - -### Q3 (25 points) - Essay - -Design a complete ANOVA study to test whether three different study techniques affect exam performance. Include: -- Null and alternative hypotheses -- Study design (sample size, randomization) -- Analysis plan -- Interpretation strategy - -**Rubric:** -- Hypotheses clearly stated (5 points) -- Study design appropriate (10 points) -- Analysis plan detailed (5 points) -- Interpretation strategy sound (5 points) - ---- - -## Answer Key - -| Question | Answer | -|----------|--------| -| Q1 | c | -| Q2 | [See short answer above] | -| Q3 | [See essay rubric] | - ---- - -**Generated by:** scholar v2.0.0 -**Timestamp:** 2026-01-11T14:30:00Z -``` - ---- - -## Open Questions - -### Resolved - -1. **Integration model:** flow-cli wrapper ✅ -2. **Output formats:** All 4 (md/qmd/tex/json) ✅ -3. **Generation method:** Hybrid AI + templates ✅ -4. **Validation:** Critical, with --no-validate override ✅ -5. **Context storage:** Read from .flow/teach-config.yml ✅ - -### Still Open - -1. **AI Provider:** Use Claude API directly or abstract for multi-provider support? - - **Recommendation:** Claude only initially, abstract in v2.1.0 - - **Impact:** Low (internal implementation detail) - -2. **Question Bank:** Store generated questions for reuse across exams? - - **Recommendation:** Phase 2 feature (v2.1.0+) - - **Impact:** Medium (requires database/storage design) - -3. **Collaboration:** Multi-instructor question approval workflow? - - **Recommendation:** Out of scope for v2.0.0 - - **Impact:** High (requires auth, permissions, git workflow) - -4. **Licensing:** How to handle copyrighted exam content? - - **Recommendation:** User responsibility, add disclaimer in generated files - - **Impact:** Low (documentation only) - -5. **Question Difficulty Calibration:** Use IRT (Item Response Theory) for difficulty? - - **Recommendation:** Phase 3 feature (v3.0.0+) - - **Impact:** High (requires psychometric expertise) - ---- - -## Review Checklist - -- [ ] **Architecture:** Mermaid diagram shows data flow clearly -- [ ] **API Design:** All 5 commands have consistent interface -- [ ] **Data Models:** JSON schemas complete and validated -- [ ] **Dependencies:** All required deps listed with versions -- [ ] **UI/UX:** Terminal output examples comprehensive -- [ ] **Testing:** Test strategy covers unit + integration -- [ ] **Documentation:** All sections complete -- [ ] **Open Questions:** Critical questions resolved -- [ ] **Acceptance Criteria:** Measurable and testable -- [ ] **Integration:** flow-cli wrapper interface specified - ---- - -## Implementation Notes - -### Week 1: `/teaching:exam` Pilot (6-8 hours) - -**Critical Path:** -1. Create `src/teaching/commands/exam.js` -2. Implement `src/teaching/templates/exam.json` -3. Add `src/teaching/validators/schema.js` -4. Markdown formatter only -5. Unit tests (10+ tests) - -**Validation:** -- Generate 5 sample exams -- Test with .flow/teach-config.yml -- Verify examark conversion works - -### Week 2: Formats & Validation (6-8 hours) - -**Add:** -- Quarto formatter (`src/teaching/formatters/quarto.js`) -- LaTeX formatter (`src/teaching/formatters/latex.js`) -- JSON formatter (`src/teaching/formatters/json.js`) -- Comprehensive validation rules -- LaTeX syntax checker -- Integration tests - -**Validation:** -- Test all 4 formats with same exam -- Verify Quarto renders -- Verify LaTeX compiles - -### Week 3: Additional Commands (8-10 hours) - -**Implement:** -- `/teaching:quiz` (similar to exam, shorter) -- `/teaching:lecture` (outline structure) -- Refactor shared utilities -- Documentation (`docs/teaching-commands.md`) - -**Validation:** -- Quiz: 5-question quiz in < 15s -- Lecture: 3-section outline in < 20s - -### Week 4: Teaching Skills (4-6 hours) - -**Create:** -- Auto-activating skills (exam-designer, lecture-planner, etc.) -- Skill coordination logic -- Examples and activation keywords - -**Validation:** -- Skills activate on correct keywords -- Multiple skills coordinate properly - -### Week 5: Polish & Integration (4-6 hours) - -**Implement:** -- `/teaching:assignment` command -- `/teaching:syllabus` command -- flow-cli wrapper scripts -- E2E testing with flow-cli - -**Validation:** -- Full pipeline: flow-cli → scholar → examark -- User acceptance testing - ---- - -## History - -| Date | Version | Author | Changes | -|------|---------|--------|---------| -| 2026-01-11 | 1.0 | DT | Initial spec from deep brainstorm | - ---- - -**Spec Status:** ✅ Complete - Ready for Review -**Estimated Complexity:** Medium-High (28-38 hours) -**Risk Level:** Low (standalone feature, incremental delivery) -**Next Step:** Review spec → Create feature branch → Start Week 1 implementation diff --git a/docs/specs/SPEC-teach-analyze-optimization-2026-01-22.md b/docs/specs/SPEC-teach-analyze-optimization-2026-01-22.md deleted file mode 100644 index af313d4bd..000000000 --- a/docs/specs/SPEC-teach-analyze-optimization-2026-01-22.md +++ /dev/null @@ -1,374 +0,0 @@ -# SPEC: teach analyze PR #289 — Post-Merge Optimization - -**Status:** draft -**Created:** 2026-01-22 -**From Brainstorm:** PR #289 code review concerns -**Branch:** `feature/teach-analyze-optimize` (to be created from dev after #289 merge) -**Estimated Effort:** 2-3 hours -**Priority:** Low (quality/performance, no user-facing bugs) - ---- - -## Overview - -PR #289 introduces 6 new library files and a 1,203-line command file for `teach analyze`. While functionally correct, the integration has three structural issues: (1) libraries are sourced 2-3 times per shell startup due to missing load guards, (2) the display layer is embedded in the command file rather than extracted to a dedicated lib, and (3) the slide cache uses flat filenames that can collide across subdirectories. - ---- - -## Primary User Story - -**As a** flow-cli user with `teach analyze` installed, -**I want** the plugin to load efficiently without redundant file sourcing, -**so that** shell startup stays under 100ms and cache files never collide. - -### Acceptance Criteria - -- [ ] Each of the 6 new lib files is sourced exactly once per shell session -- [ ] Shell startup time is not regressed (verify with `time source flow.plugin.zsh`) -- [ ] Display functions extracted to `lib/analysis-display.zsh` (~270 lines) -- [ ] Slide cache mirrors source directory structure (no path collisions) -- [ ] All 362+ existing tests still pass -- [ ] No user-facing behavior changes - ---- - -## Problem Analysis - -### Issue 1: Triple-Sourcing (Performance) - -**Current load chain on shell startup:** - -``` -flow.plugin.zsh -├── L49-54: source lib/concept-extraction.zsh ← FIRST load -│ source lib/prerequisite-checker.zsh -│ source lib/analysis-cache.zsh -│ source lib/report-generator.zsh -│ source lib/ai-analysis.zsh -│ source lib/slide-optimizer.zsh -│ -├── L56: source commands/teach-analyze.zsh ← Sources 5 libs AGAIN -│ └── source lib/concept-extraction.zsh ← SECOND load -│ source lib/prerequisite-checker.zsh -│ source lib/report-generator.zsh -│ source lib/ai-analysis.zsh -│ source lib/slide-optimizer.zsh -│ -├── L63: for cmd_file in commands/*.zsh ← THIRD load (glob matches teach-analyze.zsh) -│ └── source commands/teach-analyze.zsh -│ └── (5 libs sourced AGAIN) -│ -└── L65: for disp_file in lib/dispatchers/*.zsh - └── source teach-dispatcher.zsh - └── Conditional source (3 libs with guards) - └── Guards check _FLOW_*_LOADED... but libs never set them! -``` - -**Impact:** ~5,500 lines of ZSH re-parsed 2-3 times on every shell startup. Functions are re-defined (harmless but wasteful). The `(( ${+VAR} )) || readonly` pattern prevents errors but doesn't prevent re-parsing. - -**Root causes:** -1. `flow.plugin.zsh` explicitly sources `commands/teach-analyze.zsh` (L56) AND also globs `commands/*.zsh` (L63) — so teach-analyze.zsh runs twice -2. `teach-analyze.zsh` unconditionally sources 5 libs (no guard checks) -3. The 6 lib files have NO self-protection guards (unlike teach-validate.zsh's pattern) -4. Dispatcher guards check variables that are never set by the libs themselves - -### Issue 2: Command File Size (Maintainability) - -**Current `commands/teach-analyze.zsh` structure (1,203 lines):** - -| Section | Lines | Function Count | Purpose | -|---------|-------|----------------|---------| -| Display layer | 27-299 | 7 functions | UI rendering | -| Main command | 301-632 | 1 function | Core logic | -| Help text | 633-758 | 1 function | --help output | -| Interactive mode | 759-863 | 1 function | Guided workflow | -| Interactive helpers | 864-1203 | 10 functions | Interactive support | - -The 7 display functions (272 lines) are pure presentation — they read a JSON results file and render colored tables. They have no business logic and are reusable by other commands (`teach status`, `teach validate --deep`). - -**Comparison with existing pattern:** -- `teach-validate.zsh`: 546 lines (command) + 1,024 lines (`lib/validation-helpers.zsh`) + 700 lines (`lib/custom-validators.zsh`) -- Pattern: Command file is thin orchestrator; display/logic in `lib/` - -### Issue 3: Slide Cache Path Collision - -**Current path construction:** - -```zsh -local slide_cache_file="$course_dir/.teach/slide-optimization-${file_path:t:r}.json" -``` - -`${file_path:t:r}` extracts just the filename stem. This collides when files in different directories share a name: - -| File Path | Cache Key | Collision? | -|-----------|-----------|------------| -| `lectures/week-05/lecture.qmd` | `slide-optimization-lecture.json` | ← | -| `labs/week-05/lecture.qmd` | `slide-optimization-lecture.json` | COLLISION | -| `lectures/week-05-regression.qmd` | `slide-optimization-week-05-regression.json` | OK | - ---- - -## Architecture - -### Load Guard Pattern (Self-Protecting Libraries) - -```mermaid -flowchart TD - A[flow.plugin.zsh] -->|source| B[lib/concept-extraction.zsh] - A -->|source| C[commands/teach-analyze.zsh] - C -->|source| B - D[teach-dispatcher.zsh] -->|conditional source| B - - B --> G{_FLOW_CONCEPT_EXTRACTION_LOADED?} - G -->|yes| H[return 0 immediately] - G -->|no| I[Define functions] - I --> J[typeset -g _FLOW_CONCEPT_EXTRACTION_LOADED=1] -``` - -### File Organization (After Refactor) - -``` -lib/ -├── analysis-display.zsh # NEW: 7 display functions (~270 lines) -├── concept-extraction.zsh # + load guard added -├── prerequisite-checker.zsh # + load guard added -├── analysis-cache.zsh # + load guard added -├── report-generator.zsh # + load guard added -├── ai-analysis.zsh # + load guard added -└── slide-optimizer.zsh # + load guard added - -commands/ -└── teach-analyze.zsh # Reduced: ~930 lines (removed display layer) -``` - -### Slide Cache Directory Structure - -``` -.teach/ -├── analysis-cache/ -│ ├── cache-index.json -│ ├── lectures/ # Mirrors source tree -│ │ ├── week-05-regression.json -│ │ └── week-05/ -│ │ └── lecture.json -│ └── labs/ -│ └── week-05/ -│ └── lecture.json # No collision -├── concepts.json -└── reports/ -``` - ---- - -## Implementation Plan - -### Task 1: Add Self-Protecting Load Guards to 6 Lib Files - -**Files to modify:** `lib/{concept-extraction,prerequisite-checker,analysis-cache,report-generator,ai-analysis,slide-optimizer}.zsh` - -**Pattern (add to top of each file, after shebang and comments):** - -```zsh -# Guard against double-sourcing -if [[ -n "$_FLOW_CONCEPT_EXTRACTION_LOADED" ]]; then - return 0 2>/dev/null || true -fi -typeset -g _FLOW_CONCEPT_EXTRACTION_LOADED=1 -``` - -**Guard variable names:** - -| File | Guard Variable | -|------|---------------| -| `concept-extraction.zsh` | `_FLOW_CONCEPT_EXTRACTION_LOADED` | -| `prerequisite-checker.zsh` | `_FLOW_PREREQUISITE_CHECKER_LOADED` | -| `analysis-cache.zsh` | `_FLOW_ANALYSIS_CACHE_LOADED` | -| `report-generator.zsh` | `_FLOW_REPORT_GENERATOR_LOADED` | -| `ai-analysis.zsh` | `_FLOW_AI_ANALYSIS_LOADED` | -| `slide-optimizer.zsh` | `_FLOW_SLIDE_OPTIMIZER_LOADED` | - -### Task 2: Remove Redundant Explicit Source from flow.plugin.zsh - -**Remove line 56:** - -```zsh -# REMOVE: source "$FLOW_PLUGIN_DIR/commands/teach-analyze.zsh" -``` - -The glob on line 63 (`for cmd_file in commands/*.zsh`) already sources all command files including teach-analyze.zsh. The explicit source was added to ensure load order, but with load guards in the libs, order no longer matters. - -**Also remove lines 49-54** (explicit lib sources): - -```zsh -# REMOVE: These are sourced by teach-analyze.zsh when it loads -# source "$FLOW_PLUGIN_DIR/lib/concept-extraction.zsh" -# source "$FLOW_PLUGIN_DIR/lib/prerequisite-checker.zsh" -# source "$FLOW_PLUGIN_DIR/lib/analysis-cache.zsh" -# source "$FLOW_PLUGIN_DIR/lib/report-generator.zsh" -# source "$FLOW_PLUGIN_DIR/lib/ai-analysis.zsh" -# source "$FLOW_PLUGIN_DIR/lib/slide-optimizer.zsh" -``` - -**Result:** Libs are sourced exactly once when `commands/teach-analyze.zsh` is loaded by the glob. Guards prevent re-sourcing if dispatcher also tries. - -### Task 3: Extract Display Layer to lib/analysis-display.zsh - -**New file:** `lib/analysis-display.zsh` (~270 lines) - -**Functions to move:** -1. `_display_analysis_header()` (lines 27-41) -2. `_display_concepts_section()` (lines 43-62) -3. `_display_prerequisites_section()` (lines 64-103) -4. `_display_violations_section()` (lines 104-126) -5. `_display_ai_section()` (lines 127-190) -6. `_display_slide_section()` (lines 191-234) -7. `_display_summary_section()` (lines 235-299) - -**Add to new file header:** - -```zsh -#!/usr/bin/env zsh -# lib/analysis-display.zsh -# Display formatting for teach analyze results -# Extracted from commands/teach-analyze.zsh for reuse - -# Guard -if [[ -n "$_FLOW_ANALYSIS_DISPLAY_LOADED" ]]; then - return 0 2>/dev/null || true -fi -typeset -g _FLOW_ANALYSIS_DISPLAY_LOADED=1 - -# Color scheme (use FLOW_COLORS from core.zsh if available) -: ${FLOW_GREEN:='\033[38;5;154m'} -: ${FLOW_BLUE:='\033[38;5;75m'} -: ${FLOW_YELLOW:='\033[38;5;221m'} -: ${FLOW_RED:='\033[38;5;203m'} -: ${FLOW_BOLD:='\033[1m'} -: ${FLOW_RESET:='\033[0m'} -``` - -**Update `commands/teach-analyze.zsh`:** - -```zsh -# Replace inline display functions with: -source "${0:A:h:h}/lib/analysis-display.zsh" -``` - -**Reuse opportunity:** `teach status`, `teach validate --deep` can also source this file for consistent analysis display. - -### Task 4: Fix Slide Cache Path to Mirror Source Directory - -**Current (in `_teach_analyze()`):** - -```zsh -local slide_cache_file="$course_dir/.teach/slide-optimization-${file_path:t:r}.json" -``` - -**New:** - -```zsh -# Mirror source directory structure in cache -local relative_path="${file_path#$course_dir/}" -local cache_subdir="${relative_path:h}" # directory portion -local cache_name="${relative_path:t:r}" # filename stem -local slide_cache_dir="$course_dir/.teach/analysis-cache/$cache_subdir" -mkdir -p "$slide_cache_dir" 2>/dev/null -local slide_cache_file="$slide_cache_dir/${cache_name}-slides.json" -``` - -**Examples after fix:** - -| Source File | Cache File | -|------------|------------| -| `lectures/week-05-regression.qmd` | `.teach/analysis-cache/lectures/week-05-regression-slides.json` | -| `lectures/week-05/lecture.qmd` | `.teach/analysis-cache/lectures/week-05/lecture-slides.json` | -| `labs/week-05/lecture.qmd` | `.teach/analysis-cache/labs/week-05/lecture-slides.json` | - -**Note:** This aligns with the existing `analysis-cache/` directory structure already defined in `lib/analysis-cache.zsh` (lines 21-27 of that file describe mirroring source structure). - -### Task 5: Remove Redundant Guards from teach-dispatcher.zsh - -**Remove lines 113-133** from the dispatcher (the conditional source blocks for analysis-cache, concept-extraction, prerequisite-checker). With self-protecting libs, these are unnecessary overhead — the guard logic runs in the lib files themselves. - -```zsh -# REMOVE these blocks: -if [[ -z "$_FLOW_ANALYSIS_CACHE_LOADED" ]]; then - local analysis_cache_path="${0:A:h:h}/analysis-cache.zsh" - [[ -f "$analysis_cache_path" ]] && source "$analysis_cache_path" - typeset -g _FLOW_ANALYSIS_CACHE_LOADED=1 -fi -# ... (same for concept-extraction, prerequisite-checker) -``` - -The libs will already be loaded by the time the dispatcher runs (loaded by flow.plugin.zsh → glob → teach-analyze.zsh → source libs). If somehow the dispatcher runs first (unlikely but possible in edge cases), the source in teach-analyze.zsh will handle it. - ---- - -## Dependencies - -- PR #289 must be merged to `dev` first -- No external dependencies -- Tests should pass without modification (behavior unchanged) - ---- - -## Verification - -```bash -# 1. Startup performance (before and after) -time (source flow.plugin.zsh) - -# 2. Load guard verification -source flow.plugin.zsh -echo $_FLOW_CONCEPT_EXTRACTION_LOADED # Should print 1 -echo $_FLOW_ANALYSIS_CACHE_LOADED # Should print 1 - -# 3. Source count verification (should be exactly 1 per lib) -FLOW_DEBUG=1 source flow.plugin.zsh 2>&1 | grep -c "concept-extraction" # Should be 1 - -# 4. Slide cache collision test -teach analyze lectures/week-05/lecture.qmd --slide-breaks -teach analyze labs/week-05/lecture.qmd --slide-breaks -ls .teach/analysis-cache/lectures/week-05/ # lecture-slides.json -ls .teach/analysis-cache/labs/week-05/ # lecture-slides.json (different!) - -# 5. Full test suite -./tests/run-all.sh -``` - ---- - -## Open Questions - -1. **Should `_teach_validate_deep` and `_teach_validate_concepts` also use the display lib?** — They currently have inline formatting. Could be a follow-up. -2. **Should we add FLOW_DEBUG tracing to load guards?** — e.g., `[[ "$FLOW_DEBUG" == "1" ]] && echo "Loading concept-extraction.zsh" >&2` - ---- - -## Review Checklist - -- [ ] All 6 lib files have self-protecting load guards -- [ ] `flow.plugin.zsh` no longer explicitly sources teach-analyze libs -- [ ] `commands/teach-analyze.zsh` sources `lib/analysis-display.zsh` instead of defining display functions inline -- [ ] `lib/analysis-display.zsh` has its own load guard -- [ ] Slide cache uses mirrored directory structure -- [ ] Dispatcher redundant guards removed -- [ ] No user-facing behavior changes -- [ ] All 362+ tests pass -- [ ] Shell startup time not regressed - ---- - -## Implementation Notes - -- The `return 0 2>/dev/null || true` pattern in guards is needed because `return` inside a sourced file exits the source, but outside a function it would error — the `2>/dev/null || true` handles both cases safely. -- The color variable pattern (`: ${FLOW_GREEN:=...}`) is already correct — it only sets if unset, which is the right behavior for a file that might be sourced before or after `core.zsh`. -- The glob pattern in `flow.plugin.zsh` (`commands/*.zsh(N)`) already handles the loading — the explicit source line was a belt-and-suspenders approach that's now unnecessary with guards. - ---- - -## History - -| Date | Action | -|------|--------| -| 2026-01-22 | Initial spec from PR #289 review concerns | diff --git a/docs/specs/SPEC-teach-dashboard-2026-01-18.md b/docs/specs/SPEC-teach-dashboard-2026-01-18.md deleted file mode 100644 index 9aa70e1f0..000000000 --- a/docs/specs/SPEC-teach-dashboard-2026-01-18.md +++ /dev/null @@ -1,413 +0,0 @@ -# Implementation Spec: Dynamic Dashboard Generation for Teaching Websites - -**Status:** approved -**Created:** 2026-01-18 -**Approved:** 2026-01-19 -**Related:** SPEC-teach-dates-automation-2026-01-16.md, STAT 545 website enhancement -**Target Release:** v5.15.0 -**Effort Estimate:** 11-16 hours (4 phases) -**Priority:** Medium - ---- - -## Overview - -Add `teach dashboard` subcommand to generate and manage dynamic website dashboard content for Quarto teaching sites. Generates `semester-data.json` from existing `teach-config.yml`, enabling client-side JavaScript to display current week, topic, deadlines, and announcements without requiring site rebuilds. - -**Key Value Proposition:** Eliminate weekly manual dashboard updates. Students always see accurate "This Week" content. Announcements auto-expire. Week numbers auto-calculate. - ---- - -## Primary User Story - -**As a course instructor with a Quarto website,** -**I want** the homepage dashboard to automatically show the current week's content, -**So that** students always see accurate information without me manually editing `index.qmd` each week. - -**Acceptance Criteria:** -1. `teach dashboard generate` creates `.flow/semester-data.json` from `teach-config.yml` -2. Generated JSON includes all 16 weeks with topics, lecture/lab URLs, assignment deadlines -3. JSON includes announcements with expiry dates -4. JSON includes break periods with "show next week" behavior -5. Existing `stat545.js` (or similar) can consume the JSON client-side - ---- - -## Secondary User Stories - -### User Story 2: Managing Announcements - -**As an instructor needing to post time-sensitive information,** -**I want** to add announcements that auto-expire, -**So that** outdated announcements don't clutter the homepage. - -**Acceptance Criteria:** -- `teach dashboard announce "Title" "Message" --expires 2026-01-26` -- Announcement added to `.flow/semester-data.json` -- Client-side JS hides expired announcements automatically - -### User Story 3: Preview Specific Week - -**As an instructor preparing content,** -**I want** to preview what the dashboard will show for any week, -**So that** I can verify content before students see it. - -**Acceptance Criteria:** -- `teach dashboard preview --week 5` shows week 5 content -- `teach dashboard preview` shows current week based on date -- Output includes hero banner, cards, and next up widget content - ---- - -## Technical Requirements - -### Architecture - -#### Component Diagram - -```mermaid -graph TB - subgraph "Dashboard Generation Layer (NEW)" - DG[teach dashboard generate] - DA[teach dashboard announce] - DP[teach dashboard preview] - end - - subgraph "Config Sources" - TC[teach-config.yml] - WK[semester_info.weeks] - BR[semester_info.breaks] - end - - subgraph "Output" - JSON[.flow/semester-data.json] - SITE[_site/.flow/semester-data.json] - end - - subgraph "Client-Side" - JS[stat545.js / theme JS] - DOM[Dashboard DOM] - end - - TC -->|weeks, topics| DG - WK -->|lecture/lab URLs| DG - BR -->|break dates| DG - DG -->|generate| JSON - JSON -->|Quarto copies| SITE - SITE -->|fetch()| JS - JS -->|update| DOM - - DA -->|append| JSON - DP -->|read| JSON -``` - -#### Data Flow - -``` -teach-config.yml - │ - ├─ semester_info.start_date - ├─ semester_info.weeks[].number - ├─ semester_info.weeks[].topic - ├─ semester_info.weeks[].lecture_url (NEW field) - ├─ semester_info.weeks[].lab_url (NEW field) - ├─ semester_info.weeks[].assignment_url (NEW field) - ├─ semester_info.weeks[].assignment_due (NEW field) - └─ semester_info.breaks[] - │ - ▼ - teach dashboard generate - │ - ▼ - .flow/semester-data.json - { - "semester": "Spring 2026", - "timezone": "America/Denver", - "start_date": "2026-01-19", - "weeks": [...], - "breaks": [...], - "announcements": [...] - } -``` - -### Extended teach-config.yml Schema - -Add new optional fields to `semester_info.weeks`: - -```yaml -semester_info: - start_date: "2026-01-19" - end_date: "2026-05-16" - timezone: "America/Denver" # NEW - weeks: - - number: 1 - topic: "Fundamentals of Experimental Design" - focus: "Introduction to randomization..." # NEW (optional) - lecture: # NEW (optional) - title: "Introduction to Design Principles" - url: "lectures/week-01_intro-design_part1.qmd" - lab: # NEW (optional) - title: "Getting Started with R & Quarto" - url: "r_help.qmd" - assignment: # NEW (optional) - title: "Assignment 1" - url: "assignments/assignment1.qmd" - due: "2026-01-29" - breaks: - - name: "Spring Break" - start: "2026-03-15" - end: "2026-03-22" - show_next: true # NEW (default: true) - -# NEW section (v5.15.0) -dashboard: - # Structure options (defaults shown) - show_labs: true # Include lab cards in dashboard - show_assignments: true # Include assignment deadlines - show_readings: false # Include reading assignments (for seminar courses) - - # Display options - card_style: "detailed" # detailed|simple - hero_style: "banner" # banner|minimal - - # Feature toggles - enable_announcements: true # Allow announcements - max_announcements: 5 # Maximum active announcements - - # Fallback content - fallback_message: "Check the Syllabus for current week information." - - # Announcements (optional, can also use `teach dashboard announce`) - announcements: - - id: "welcome-2026" - type: "note" # note|warning|info - title: "Welcome to Class!" - date: "2026-01-13" - content: "Please review the Syllabus..." - link: "syllabus/syllabus-final.qmd" - expires: "2026-01-26" -``` - -### API Design - -#### Command: `teach dashboard` - -```bash -teach dashboard # Show help -teach dashboard generate # Generate semester-data.json from config -teach dashboard generate --force # Overwrite existing JSON -teach dashboard preview # Preview current week -teach dashboard preview --week 5 # Preview specific week -teach dashboard announce # Interactive announcement wizard -teach dashboard announce "Title" "Message" --expires DATE --type note -``` - -#### Subcommand Details - -| Command | Description | Options | -|---------|-------------|---------| -| `generate` | Create `.flow/semester-data.json` from config | `--force`, `--output PATH` | -| `preview` | Show what dashboard will display | `--week N`, `--json` | -| `announce` | Add announcement to JSON | `--title`, `--message`, `--expires`, `--type`, `--link` | -| `status` | Show dashboard config status | (none) | - ---- - -## Dependencies - -| Dependency | Purpose | Required? | -|------------|---------|-----------| -| `yq` | YAML parsing | Yes | -| `jq` | JSON manipulation | Yes | -| Existing `teach-config.yml` | Source data | Yes | -| Quarto project | Target deployment | Yes | - ---- - -## UI/UX Specifications - -### Command Output Examples - -#### `teach dashboard generate` - -``` -🎯 Generating Dashboard Data -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - -Reading: .flow/teach-config.yml -Timezone: America/Denver -Semester: Spring 2026 (Jan 19 - May 16) - -Generating weeks... - ✓ Week 1: Fundamentals of Experimental Design - ✓ Week 2: Completely Randomized Design (CRD) - ... (14 more weeks) - ✓ Week 16: Finals Week - -Breaks configured: - • MLK Day (Jan 19) - show next week - • Spring Break (Mar 15-22) - show next week - -Announcements: - • welcome-2026: "Welcome to Class!" (expires Jan 26) - -✅ Generated: .flow/semester-data.json - -Next steps: - 1. Quarto will copy JSON to _site/ on render - 2. Client-side JS will fetch and display content - 3. Run `teach dashboard preview` to test -``` - -#### `teach dashboard preview --week 5` - -``` -📅 Dashboard Preview: Week 5 -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - -┌─ Hero Banner ─────────────────────────────────────┐ -│ Spring 2026 • Week 5 │ -│ Two-Factor Factorial Designs │ -│ Focus: Main effects, interactions, factorial ANOVA│ -└───────────────────────────────────────────────────┘ - -┌─ This Week Cards ─────────────────────────────────┐ -│ [LECTURE] Factorial ANOVA │ -│ lectures/week-05_factorial-anova.html │ -│ │ -│ [LAB] Interaction Plots │ -│ r_help.html │ -└───────────────────────────────────────────────────┘ - -┌─ Next Up Widget ──────────────────────────────────┐ -│ ! Assignment 5 │ -│ Due Feb 26 • URGENT │ -│ assignments/assignment5.html │ -└───────────────────────────────────────────────────┘ - -Active Announcements: (none - all expired) -``` - -#### `teach dashboard announce` - -``` -📢 Add Announcement -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - -Title: Exam 1 Next Week -Message: Exam covers Weeks 1-5. Review guide posted. -Type: warning -Expires: 2026-03-05 -Link (optional): syllabus/syllabus-final.html - -✅ Added announcement: exam1-2026 - -Updated: .flow/semester-data.json -``` - ---- - -## Implementation Phases - -### Phase 1: Core Generate Command (4-5 hours) ← +1h for config options - -- [ ] Add `_teach_dashboard_dispatcher()` to teach-dispatcher.zsh -- [ ] Implement `_teach_dashboard_generate()` -- [ ] Read `teach-config.yml` extended schema -- [ ] **NEW:** Read dashboard config options with defaults -- [ ] Generate base `.flow/semester-data.json` structure -- [ ] **NEW:** Conditionally include sections based on config (labs, assignments, readings) -- [ ] Add validation for required fields -- [ ] Add integration hook to `teach dates sync` - -### Phase 2: Preview Command (2-3 hours) - -- [ ] Implement `_teach_dashboard_preview()` -- [ ] Calculate current week from date -- [ ] Support `--week N` override -- [ ] **NEW:** Respect dashboard config in preview output -- [ ] Format output with ASCII boxes -- [ ] Handle break weeks (show next) - -### Phase 3: Announce Command (2-3 hours) - -- [ ] Implement `_teach_dashboard_announce()` -- [ ] Interactive mode with prompts -- [ ] Direct mode with flags -- [ ] Update JSON without regenerating -- [ ] Generate unique announcement IDs - -### Phase 4: Documentation & Tests (3-5 hours) ← +2h for config docs/tests - -- [ ] Add help text for all subcommands -- [ ] **NEW:** Document all dashboard config options -- [ ] **NEW:** Add config examples for different course styles (comprehensive, simple, seminar) -- [ ] Create test-teach-dashboard.zsh -- [ ] **NEW:** Test different config combinations -- [ ] Update docs/commands/teach.md -- [ ] Add example to tutorials - ---- - -## Decision Log - -All open questions resolved on 2026-01-19. - -| Question | Decision | Rationale | Impact | -|----------|----------|-----------|--------| -| **Auto-run on dates sync?** | ✅ Yes | Keeps dashboard in perfect sync with config dates. Consistent with teach workflow philosophy. | +0h (integration hook in teach-dates.zsh) | -| **JSON location?** | ✅ `.flow/` | Matches teach-config.yml location. Clear ownership, easy to .gitignore. | +0h (simple path) | -| **Template system?** | ⚠️ Configurable fields | Single JSON structure, behavior driven by dashboard config options in teach-config.yml. More flexible than hardcoded, simpler than full templating. | +3-4h (config parsing, conditional generation) | - -### Template Approach Details - -**Chosen:** Medium complexity - Configurable fields via teach-config.yml - -**Rationale:** -- Currently only STAT 545 needs dashboard -- Configurable fields provide flexibility without template engine complexity -- Can add full templating later if multiple distinct structures emerge -- Defaults work out-of-box for comprehensive courses - -**Implementation:** -- Single JSON generation function -- Read dashboard config options (show_labs, show_assignments, etc.) -- Conditionally include sections based on config -- Sensible defaults for all options - ---- - -## Review Checklist - -- [ ] Extended teach-config.yml schema documented -- [ ] All subcommands have help text -- [ ] JSON output matches client-side JS expectations -- [ ] Timezone handling tested -- [ ] Break week "show next" logic correct -- [ ] Announcement expiry uses config timezone -- [ ] Tests cover all subcommands - ---- - -## Integration with Client-Side JS - -The generated `semester-data.json` is consumed by theme JavaScript (e.g., `stat545.js`). The JS should: - -1. Fetch `.flow/semester-data.json` on page load -2. Calculate current week from `start_date` using `timezone` -3. Check if current date is within any `breaks[]` period -4. If break with `show_next: true`, show next week's content -5. Update DOM elements with week data -6. Filter announcements by `expires` date -7. Render active announcements - -See: `/Users/dt/projects/teaching/stat-545/docs/specs/SPEC-dynamic-dashboard-2026-01-18.md` - ---- - -## History - -| Date | Change | -|------|--------| -| 2026-01-18 | Initial spec from STAT 545 enhancement session | -| 2026-01-19 | Reviewed, approved with decisions. Updated target to v5.15.0. Added configurable fields approach. Effort revised to 11-16h. | diff --git a/docs/specs/SPEC-teach-dates-automation-2026-01-16.md b/docs/specs/SPEC-teach-dates-automation-2026-01-16.md deleted file mode 100644 index c976afbbf..000000000 --- a/docs/specs/SPEC-teach-dates-automation-2026-01-16.md +++ /dev/null @@ -1,706 +0,0 @@ -# Implementation Spec: Automated Date/Deadline Management for Teaching Workflows - -**Status:** draft -**Created:** 2026-01-16 -**From Brainstorm:** BRAINSTORM-teach-dates-automation-2026-01-16.md (project root) -**Target Release:** v5.12.0 -**Effort Estimate:** 12-17 hours (5 phases) -**Priority:** High - ---- - -## Overview - -Add intelligent date/deadline synchronization to flow-cli's teaching workflow. Instructors define dates once in `teach-config.yml` (weeks, holidays, deadlines, exams) and automatically propagate them to syllabus, schedule, assignments, and lecture files. Support relative dates (week + offset), conflict resolution, and one-command semester rollover. - -**Key Value Proposition:** Eliminate 40+ manual date updates per semester, prevent inconsistencies, reduce semester rollover from 2 hours to 5 minutes. - ---- - -## Primary User Story - -**As a course instructor managing teaching materials,** -**I want** to define my semester schedule once in teach-config.yml and sync dates to all files automatically, -**So that** I never have date inconsistencies between syllabus, assignments, and lectures, -**And** I can roll over courses to new semesters in one command. - -**Acceptance Criteria:** -1. teach-config.yml has extended schema for weeks, holidays, deadlines, exams -2. `teach dates sync` finds all teaching files and shows date mismatches -3. File-by-file prompts allow selective date updates -4. Relative dates (week: 3, offset: +2 days) are computed correctly -5. `teach semester new "Spring 2026"` shifts all dates and syncs files - ---- - -## Secondary User Stories - -### User Story 2: Preventing Date Inconsistencies - -**As a course instructor with materials across many files,** -**I want** an automated check that all my dates match the course config, -**So that** students never see conflicting deadlines. - -**Acceptance Criteria:** -- teach status shows date sync status -- teach dates sync --preview shows mismatches without modifying files -- Mismatches are clearly presented with file:field:old_date→new_date - -### User Story 3: Semester Rollover - -**As an instructor teaching the same course each semester,** -**I want** to copy my course and update all dates automatically, -**So that** I don't spend 2 hours manually updating 40+ dates. - -**Acceptance Criteria:** -- `teach semester new "Spring 2026"` calculates date shift automatically -- All config dates (weeks, holidays, exams) are shifted -- All file dates are synced via teach dates sync -- One command, complete rollover in < 5 minutes - ---- - -## Technical Requirements - -### Architecture - -#### Component Diagram - -```mermaid -graph TB - subgraph "Date Management Layer (NEW)" - DP[lib/date-parser.zsh] - DS[teach dates sync] - SR[teach semester new] - end - - subgraph "Config & Validation" - CFG[teach-config.yml] - SCHEMA[schema with dates] - VAL[config-validator.zsh] - end - - subgraph "Teaching Files" - QMD[*.qmd files] - YAML[YAML frontmatter] - MD[Markdown content] - end - - DS -->|1. Scan| QMD - DP -->|2. Extract dates| YAML - DP -->|3. Extract dates| MD - DS -->|4. Compare| CFG - DS -->|5. Show diffs| USER[Instructor] - USER -->|6. Confirm| DS - DS -->|7. Apply| QMD - - SR -->|Shift dates| CFG - SR -->|Trigger sync| DS - - CFG -->|Validate| VAL - VAL -->|Check| SCHEMA - - classDef new fill:#e1f5e1 - classDef existing fill:#e1e5f5 - class DP,DS,SR new - class CFG,SCHEMA,VAL,QMD,YAML,MD existing -``` - -#### Data Flow: Date Sync Process - -```mermaid -sequenceDiagram - actor User - participant CMD as teach dates sync - participant Parser as date-parser.zsh - participant Config as teach-config.yml - participant Files as *.qmd files - - User->>CMD: teach dates sync - CMD->>Files: Find all teaching files - Files-->>CMD: List of 23 files - - loop For each file - CMD->>Parser: Extract dates from file - Parser->>Files: Read YAML + markdown - Parser-->>CMD: File dates {due: 2025-01-20} - - CMD->>Config: Load config dates - Config-->>CMD: Config dates {hw1: week 2 + 2 days} - - CMD->>Parser: Compute relative date - Parser-->>CMD: 2025-01-22 - - CMD->>CMD: Compare: 2025-01-20 vs 2025-01-22 - CMD->>CMD: Flag mismatch - end - - CMD->>User: Show 5 files with mismatches - - loop For each mismatched file - CMD->>User: File: hw1.qmd
due: 2025-01-20 → 2025-01-22
Apply? [y/N] - User->>CMD: y - - CMD->>Parser: Apply date changes - Parser->>Files: Update YAML + content - Parser-->>CMD: ✓ Updated - - CMD->>User: ✓ Updated hw1.qmd - end - - CMD->>User: ✅ Applied 3 updates, skipped 2 -``` - ---- - -### API Design - -#### New Module: lib/date-parser.zsh - -| Function | Parameters | Returns | Description | -|----------|-----------|---------|-------------| -| `_date_parse_quarto_yaml` | file, field | ISO date | Extract date from YAML frontmatter | -| `_date_parse_markdown_inline` | file, pattern | date array | Find inline dates in markdown | -| `_date_normalize` | date_string | ISO date | Convert any format to YYYY-MM-DD | -| `_date_compute_from_week` | week_start, offset | ISO date | Calculate week + offset days | -| `_date_find_teaching_files` | [path] | file array | Find all teaching files to sync | -| `_date_load_config` | - | config hash | Load all dates from teach-config.yml | -| `_date_compare` | file, file_dates, config_dates | diff string | Compare and flag mismatches | -| `_date_apply_to_file` | file, changes | 0/1 | Apply date changes to file | -| `_teach_dates_sync` | [--preview] | 0/1 | Main sync command | -| `_teach_semester_new` | semester, year | 0/1 | Semester rollover command | - -#### teach-config.yml Schema Extensions - -```yaml -semester_info: - start_date: string (YYYY-MM-DD) - end_date: string (YYYY-MM-DD) - - weeks: # NEW - - number: integer - start_date: string (YYYY-MM-DD) - topic: string - - holidays: # NEW - - name: string - date: string (YYYY-MM-DD) - type: enum(break|holiday|no_class) - - deadlines: # NEW - : - due_date: string (YYYY-MM-DD) # Absolute - # OR - week: integer # Relative - offset_days: integer - - exams: # NEW - - name: string - date: string (YYYY-MM-DD) - time: string - location: string -``` - -#### Command Enhancements - -| Command | New Behavior | Flags | -|---------|-------------|-------| -| `teach dates sync` | Sync dates from config to files | `--preview` - show changes only | -| `teach dates sync` | File-by-file prompts for updates | `--no-prompt` - auto-apply all | -| `teach semester new ` | Shift all dates and sync files | - | -| `teach init` | Prompt for start date, generate weeks | - | -| `teach status` | Show date sync status | - | - ---- - -### Data Models - -#### Config Schema (JSON Schema) - -```json -{ - "semester_info": { - "type": "object", - "properties": { - "start_date": { - "type": "string", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$" - }, - "end_date": { - "type": "string", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$" - }, - "weeks": { - "type": "array", - "items": { - "type": "object", - "required": ["number", "start_date"], - "properties": { - "number": {"type": "integer", "minimum": 1}, - "start_date": {"type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$"}, - "topic": {"type": "string"} - } - } - }, - "holidays": { - "type": "array", - "items": { - "type": "object", - "required": ["name", "date"], - "properties": { - "name": {"type": "string"}, - "date": {"type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$"}, - "type": {"enum": ["break", "holiday", "no_class"]} - } - } - }, - "deadlines": { - "type": "object", - "additionalProperties": { - "type": "object", - "properties": { - "due_date": {"type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$"}, - "week": {"type": "integer", "minimum": 1}, - "offset_days": {"type": "integer"} - }, - "oneOf": [ - {"required": ["due_date"]}, - {"required": ["week", "offset_days"]} - ] - } - }, - "exams": { - "type": "array", - "items": { - "type": "object", - "required": ["name", "date"], - "properties": { - "name": {"type": "string"}, - "date": {"type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$"}, - "time": {"type": "string"}, - "location": {"type": "string"} - } - } - } - } - } -} -``` - -#### Date Mismatch Record - -```yaml -file: assignments/hw1.qmd -mismatches: - - field: due - file_date: "2025-01-20" - config_date: "2025-01-22" - source: "week 2 + 2 days" - - field: inline_date (line 45) - file_date: "January 20, 2025" - config_date: "2025-01-22" - source: "week 2 + 2 days" -``` - ---- - -### Dependencies - -#### External Tools - -| Tool | Version | Required | Purpose | -|------|---------|----------|---------| -| yq | ≥ 4.0 | Yes | YAML parsing and manipulation | -| date / gdate | Any | Yes | Date arithmetic (GNU or BSD date) | -| sed | Any | Yes | File content replacement | -| find | Any | Yes | Recursive file search | - -#### Internal Dependencies - -| Module | Dependency Type | Reason | -|--------|----------------|--------| -| `lib/config-validator.zsh` | Required | Validate extended date schema | -| `lib/core.zsh` | Required | Logging, error handling | -| `lib/dispatchers/teach-dispatcher.zsh` | Modified | Add dates and semester subcommands | -| `commands/teach-init.zsh` | Modified | Add date setup wizard | - ---- - -## UI/UX Specifications - -### User Flow: teach dates sync - -``` -┌─────────────────────────────────────────────────────────────┐ -│ teach dates sync │ -└─────────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ 🔍 Scanning for teaching files... │ -│ Found 23 files in: │ -│ - assignments/ (8 files) │ -│ - lectures/ (12 files) │ -│ - exams/ (2 files) │ -│ - Root (syllabus.qmd, schedule.qmd) │ -└─────────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ 📊 Analyzing dates... │ -│ [████████████████████████████████] 23/23 │ -└─────────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ ⚠️ Date Mismatches Found │ -├─────────────────────────────────────────────────────────────┤ -│ 5 files have dates that differ from teach-config.yml: │ -│ │ -│ 1. assignments/hw1.qmd (1 mismatch) │ -│ 2. assignments/hw2.qmd (2 mismatches) │ -│ 3. lectures/week02.qmd (1 mismatch) │ -│ 4. syllabus.qmd (3 mismatches) │ -│ 5. schedule.qmd (8 mismatches) │ -│ │ -│ Total: 15 date differences │ -└─────────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ File: assignments/hw1.qmd │ -├─────────────────────────────────────────────────────────────┤ -│ │ -│ YAML Frontmatter: │ -│ due: 2025-01-20 → 2025-01-22 │ -│ Source: week 2 (2025-01-20) + 2 days │ -│ │ -│ Apply changes to this file? [y/N] │ -│ y - Yes, update this file │ -│ n - No, skip this file │ -│ d - Show diff │ -│ q - Quit (no more changes) │ -└─────────────────────────────────────────────────────────────┘ - ↓ - [User presses 'y'] - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ ✓ Updated: assignments/hw1.qmd │ -│ - due: 2025-01-20 → 2025-01-22 │ -└─────────────────────────────────────────────────────────────┘ - ↓ - [Repeat for each file] - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ ✅ Date Sync Complete │ -├─────────────────────────────────────────────────────────────┤ -│ │ -│ Summary: │ -│ Applied: 3 files (8 date changes) │ -│ Skipped: 2 files (7 date changes) │ -│ │ -│ Next Steps: │ -│ 1. Review changes: git diff │ -│ 2. Commit: git add -A && git commit │ -│ │ -│ Modified Files: │ -│ M assignments/hw1.qmd │ -│ M lectures/week02.qmd │ -│ M syllabus.qmd │ -└─────────────────────────────────────────────────────────────┘ -``` - -### Wireframe: teach semester new - -``` -┌─────────────────────────────────────────────────────────────┐ -│ teach semester new "Spring 2026" │ -└─────────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ 🗓️ Semester Rollover │ -├─────────────────────────────────────────────────────────────┤ -│ │ -│ Current Semester: Fall 2025 │ -│ Start: 2025-08-18 │ -│ End: 2025-12-12 │ -│ │ -│ New Semester: Spring 2026 │ -│ Start: 2026-01-13 (calculated) │ -│ End: 2026-05-01 (calculated) │ -│ │ -│ Date Shift: +148 days (21 weeks, 1 day) │ -│ │ -├─────────────────────────────────────────────────────────────┤ -│ Will update: │ -│ ✓ teach-config.yml (semester, year, all dates) │ -│ ✓ 23 teaching files (via teach dates sync) │ -│ │ -│ Proceed with semester rollover? [Y/n] │ -└─────────────────────────────────────────────────────────────┘ - ↓ - [User presses 'Y'] - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ 🔄 Shifting Config Dates... │ -│ ✓ Semester start/end dates │ -│ ✓ 15 week dates │ -│ ✓ 3 holidays │ -│ ✓ 8 assignment deadlines │ -│ ✓ 2 exam dates │ -└─────────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ 📝 Updating teach-config.yml... │ -│ ✓ course.semester = "Spring" │ -│ ✓ course.year = 2026 │ -│ ✓ All dates shifted by +148 days │ -└─────────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ 🔍 Syncing dates to files... │ -│ (runs: teach dates sync --no-prompt) │ -│ │ -│ [████████████████████████████████] 23/23 │ -│ │ -│ Updated 23 files automatically │ -└─────────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ ✅ Semester Rollover Complete! │ -├─────────────────────────────────────────────────────────────┤ -│ │ -│ Spring 2026 course ready: │ -│ - Config updated (semester, year, dates) │ -│ - All files synced (23 files) │ -│ - Total time: 8 seconds │ -│ │ -│ Next: │ -│ 1. Review: git diff │ -│ 2. Update topics: edit teach-config.yml │ -│ 3. Commit: git add -A && git commit │ -└─────────────────────────────────────────────────────────────┘ -``` - -### Accessibility Checklist - -- [x] All prompts use clear y/N format -- [x] Color not sole indicator (uses ✓, ⚠️, ✗ symbols) -- [x] Progress bars show numeric completion (23/23) -- [x] Error messages include actionable next steps -- [x] File paths are absolute or relative to project root -- [x] Date formats are ISO (universally parsable) -- [x] --preview flag for read-only preview - ---- - -## Open Questions - -1. **Should we validate that week dates are actually 7 days apart?** - - **Recommendation:** No, allow flexibility for courses with variable schedules - -2. **How to handle courses with 2 meetings per week?** - - Week 1 Monday + Week 1 Wednesday - - **Recommendation:** Phase 6, use `lectures` array with multiple dates per week - -3. **Should teach dates sync backup files before modifying?** - - Create `.bak` files as safety net - - **Recommendation:** Yes, remove backups after successful update - -4. **What about dates embedded in prose (not YAML)?** - - "The assignment is due on January 22, 2025." - - **Recommendation:** Phase 2 handles this with inline markdown parsing - ---- - -## Review Checklist - -### Design Review - -- [x] All user stories have acceptance criteria -- [x] Architecture diagram shows component relationships -- [x] Data flow diagrams illustrate key workflows -- [x] API design is consistent with existing conventions -- [x] Config schema validated against JSON Schema spec -- [x] UI/UX wireframes for all interactive prompts - -### Security Review - -- [ ] File writes use atomic operations (write temp, then rename) -- [ ] sed operations use backup files (.bak) -- [ ] No shell injection in date string parsing -- [ ] Config validation prevents malicious YAML -- [ ] File paths are validated (no directory traversal) - -### Performance Review - -- [ ] File scanning uses find (efficient for large repos) -- [ ] YAML parsing cached (don't re-parse same file) -- [ ] Date computations are O(1) (not O(n)) -- [ ] teach dates sync completes in < 10s for 50 files - -### Testing Review - -- [ ] Test suite covers all 5 phases -- [ ] Mock yq operations in tests (no real file writes) -- [ ] Test date parsing for all supported formats -- [ ] Test relative date computation with offsets -- [ ] Test semester rollover with various date shifts -- [ ] Test conflict resolution (file vs config dates) - -### Documentation Review - -- [ ] TEACHING-DATES-GUIDE.md created -- [ ] Tutorial 14 updated with date management examples -- [ ] DISPATCHER-REFERENCE.md updated for teach dates -- [ ] Migration guide for adding dates to existing courses -- [ ] FAQ section for common date issues -- [ ] Video walkthrough of semester rollover - -### Accessibility Review - -- [x] All interactive prompts use y/N format -- [x] Clear option labels with descriptions -- [x] Status symbols not emoji-only -- [x] Error messages actionable -- [ ] Tested with screen reader (manual test needed) - ---- - -## Implementation Notes - -### Critical Path - -1. **Phase 1 (Date Schema)** must be completed first - - Foundation for all date management - - Config validation ensures data quality - - Can ship independently (no breaking changes) - -2. **Phase 2 (Date Parser)** depends on Phase 1 - - Needs schema to know what dates to extract - - Pure functions, easy to test - -3. **Phase 3 (Date Sync)** depends on Phases 1-2 - - Uses date parser to extract and compare - - High-value feature (eliminates manual updates) - -4. **Phase 4 (Semester Rollover)** depends on Phases 1-3 - - Uses date shifting + sync - - Can be developed in parallel with Phase 5 - -5. **Phase 5 (teach init)** depends on Phase 1 - - Uses date schema - - Independent of Phases 2-4 - -### Testing Strategy - -**Unit Tests:** -- date-parser.zsh: Test each function with mocked files -- config-validator.zsh: Test extended schema validation -- Date arithmetic: Test week + offset calculations - -**Integration Tests:** -- Full workflow: Create config → Sync dates → Verify files -- Semester rollover: Shift dates → Sync → Verify consistency -- Conflict resolution: Manual edit → Sync → Prompt user - -**Manual Tests:** -- Test with real course (STAT 545) -- Test semester rollover (Fall 2025 → Spring 2026) -- Test with various file formats (Quarto, Markdown) -- Test date formats (ISO, long, short) - -### Migration Guide (v5.10.0 → v5.12.0) - -**For existing teaching projects:** - -1. **Add dates to teach-config.yml:** - - ```yaml - semester_info: - start_date: "2025-01-13" - weeks: - - number: 1 - start_date: "2025-01-13" - # ... add all 15 weeks - ``` - -2. **Run initial sync (preview):** - - ```bash - teach dates sync --preview - # Review what would change - ``` - -3. **Apply sync:** - - ```bash - teach dates sync - # Confirm file-by-file - ``` - -4. **Commit changes:** - - ```bash - git add -A - git commit -m "chore: centralize course dates in config" - ``` - -**Rollback plan:** -- Remove `semester_info` extended fields from config -- teach dates sync will not find config dates (no-op) -- Manual dates in files preserved - ---- - -## History - -### 2026-01-16 - Command Naming Decision - -**Decision:** Use `teach dates` subcommand hierarchy (Option A) - -**Rationale:** -1. **Discoverability**: Natural fit under existing `teach` dispatcher -2. **Clarity**: `dates` clearly indicates the domain (date management) -3. **Consistency**: Matches existing patterns (`teach status`, `teach deploy`, `teach exam`) -4. **Extensibility**: Easy to add new date operations without breaking changes -5. **ADHD-Friendly**: Clear, predictable structure with safe defaults - -**Command Structure:** - -```bash -teach dates sync # Main sync command (interactive) -teach dates sync --dry-run # Preview changes (safe mode) -teach dates status # Check date consistency -teach dates rollover # Semester rollover wizard -teach dates init # Initial setup wizard -teach dates validate # Validate date config -``` - -**Alternatives Considered:** -- **Option B** (`teach semester`): Less clear about date management focus -- **Option C** (`teach sync dates`): Action-first, but doesn't scale well for non-sync operations -- **Option D** (`dates` dispatcher): Shorter, but breaks flow-cli convention and less discoverable - -**References:** - -- Command naming brainstorm: BRAINSTORM-teach-dates-command-naming-2026-01-16.md (project root) -- [Dispatcher reference](../reference/MASTER-DISPATCHER-GUIDE.md) - ---- - -### 2026-01-16 - Initial Draft - -- **Created by:** Claude Code (Deep Feature Brainstorm) -- **Reviewed by:** (pending) -- **Changes:** N/A - initial version - -**From Brainstorm Findings:** -- 8 expert questions completed -- Pain points: Manual updates, inconsistencies, semester rollover -- Source of truth: Config + external calendar -- Date types: All (weeks, deadlines, exams, holidays) -- UX: File-by-file prompts -- Triggers: Manual command + semester rollover - -**Next Steps:** -1. Review spec with flow-cli maintainer -2. Get approval for Phase 1 (Date schema) -3. Create feature branch for implementation -4. Begin Phase 1 development (2-3 hours) diff --git a/docs/specs/SPEC-teach-deploy-v2-2026-02-03.md b/docs/specs/SPEC-teach-deploy-v2-2026-02-03.md new file mode 100644 index 000000000..ab6af05cc --- /dev/null +++ b/docs/specs/SPEC-teach-deploy-v2-2026-02-03.md @@ -0,0 +1,218 @@ +# SPEC: teach deploy v2 — STAT-545 Port + New Features + +**Status:** draft +**Created:** 2026-02-03 +**From Brainstorm:** BRAINSTORM-teach-deploy-v2-2026-02-03.md +**Target Version:** v6.4.0 + +--- + +## Overview + +Enhance `teach deploy` with 8 features ported from STAT-545's battle-tested deploy workflow and new capabilities. Consolidate two deploy code paths into a single enhanced implementation. Adds direct merge mode (8-15s deploys), smart commit messages, deployment history with rollback, dry-run preview, CI support, and .STATUS auto-updates. + +--- + +## Primary User Story + +**As a** solo course instructor using flow-cli, +**I want** fast, safe, trackable deployments with smart defaults, +**So that** I can deploy course content in under 15 seconds with full history and rollback capability. + +### Acceptance Criteria + +- [ ] `teach deploy --direct` completes in <15 seconds (vs 45-90s PR mode) +- [ ] Smart commit messages generated automatically from changed files +- [ ] `teach deploy --dry-run` previews all operations without mutation +- [ ] `.flow/deploy-history.yml` records every deployment +- [ ] `teach deploy --rollback` reverts any recent deployment +- [ ] `teach deploy --ci` works without interactive prompts +- [ ] `.STATUS` auto-updates teaching week on deploy +- [ ] Old `_teach_deploy()` dead code removed from teach-dispatcher.zsh +- [ ] All existing tests pass (`./tests/run-all.sh`) +- [ ] 65+ new tests across 3 test files + +--- + +## Secondary User Stories + +**As a** CI/CD pipeline, +**I want** non-interactive deploy mode, +**So that** GitHub Actions can deploy without prompts. + +**As an** instructor who made a mistake, +**I want** to rollback the last deployment, +**So that** students don't see broken content. + +--- + +## Architecture + +``` +teach deploy [args] + → teach-dispatcher.zsh (routing only) + → _teach_deploy_enhanced() + ├── Parse flags + ├── _deploy_preflight_checks() + ├── Mode dispatch: + │ ├── --dry-run → _deploy_dry_run_report() + │ ├── --rollback → _deploy_rollback() + │ ├── --direct → _deploy_direct_merge() + │ ├── partial → _deploy_partial() + │ └── default → _deploy_full_site() + └── Post-deploy: + ├── _deploy_history_append() + └── _deploy_update_status_file() +``` + +--- + +## API Design + +| Flag | Short | Purpose | +|------|-------|---------| +| `--direct` | `-d` | Direct merge mode (no PR) | +| `--dry-run` | `--preview` | Preview without executing | +| `--rollback [N]` | | Revert deployment N from history | +| `--ci` | | Force non-interactive mode | +| `--message "text"` | `-m` | Custom commit message | +| `--auto-commit` | | Auto-commit dirty files | +| `--auto-tag` | | Tag with timestamp | +| `--skip-index` | | Skip index management | +| `--check-prereqs` | | Validate prerequisites | +| `--direct-push` | | Alias for `--direct` (backward compat) | + +--- + +## Data Models + +### deploy-history.yml + +```yaml +deploys: + - timestamp: '2026-02-03T14:30:22-06:00' + mode: 'direct' + commit_hash: 'a1b2c3d4' + commit_before: 'e5f6g7h8' + branch_from: 'draft' + branch_to: 'production' + files_deployed: [] + file_count: 15 + commit_message: 'content: week-05 lecture' + pr_number: null + tag: null + user: 'dt' + duration_seconds: 12 +``` + +--- + +## Dependencies + +- `yq` — YAML parsing (already a teach dependency) +- `gh` — GitHub CLI for PR creation (already used) +- `git` — Core operations (already used) +- No new external dependencies + +--- + +## UI/UX Specifications + +### Deploy Output Format + +``` + Deploying to production... + + Pre-flight: + [ok] Git repository + [ok] Config file found + [ok] On draft branch + [ok] Working tree clean + [ok] No production conflicts + + Smart commit: content: week-05 lecture, assignment 3, config + + Direct merge: draft -> production + [ok] Merged successfully + [ok] Pushed to origin/production + + History logged: #12 (2026-02-03 14:30) + .STATUS updated: week 5, deploy #12 + + Done in 11s + Site: https://example.github.io/stat-545/ +``` + +### Dry-Run Output + +``` + DRY RUN — No changes will be made + + Would deploy 3 files: + lectures/week-05.qmd + scripts/analysis.R (dependency) + home_lectures.qmd (index update) + + Would commit: "content: week-05 lecture, analysis script" + Would merge: draft -> production (direct mode) + Would log: deploy #12 to .flow/deploy-history.yml + Would update: .STATUS (teaching_week: 5) +``` + +### Rollback Interactive + +``` + Recent deployments: + + # When Mode Files Message + 1 2026-02-03 14:30 direct 3 content: week-05 lecture + 2 2026-02-02 09:15 pr 15 deploy: full site update + 3 2026-02-01 16:45 partial 2 content: assignment 3 + + Rollback which deployment? [1]: +``` + +### Accessibility + +- All output uses existing `_flow_log_*` color functions with fallbacks +- `--ci` mode outputs plain text (no ANSI) +- Help follows CONVENTIONS.md 9-rule compliance + +--- + +## Open Questions + +1. Should `--rollback` of a PR deploy create a revert PR, or direct-push the revert? +2. Should deploy-history.yml be git-tracked or gitignored? + +--- + +## Review Checklist + +- [ ] All 9 features implemented +- [ ] Old dead code removed from teach-dispatcher.zsh +- [ ] Flag matrix interactions tested +- [ ] 65+ new tests pass +- [ ] Existing 462+ tests still pass +- [ ] Help compliance passes (`flow doctor --help-check`) +- [ ] CLAUDE.md updated +- [ ] Backward compatibility: `--direct-push` still works + +--- + +## Implementation Notes + +- Implementation follows 7 phases (see plan file) +- Phase 1 (CI mode) must come first — all other features depend on interactivity awareness +- Smart commit messages reuse STAT-545's `generate_smart_message()` categorization logic +- Deploy history uses append-only YAML (no full-file rewrite via yq) +- Rollback is "forward rollback" via `git revert`, not destructive `git reset` +- .STATUS updates are non-destructive: skip if file doesn't exist, skip teaching_week if no semester_info.start_date + +--- + +## History + +| Date | Change | +|------|--------| +| 2026-02-03 | Initial spec from deep brainstorm session | diff --git a/docs/specs/SPEC-teach-init-migration-2026-01-12.md b/docs/specs/SPEC-teach-init-migration-2026-01-12.md deleted file mode 100644 index 5c89c636f..000000000 --- a/docs/specs/SPEC-teach-init-migration-2026-01-12.md +++ /dev/null @@ -1,708 +0,0 @@ -# SPEC: Intelligent Migration Workflow for teach-init - -**Status:** Completed (Implemented in v5.4.1) -**Created:** 2026-01-12 -**Updated:** 2026-01-13 (Documentation cleanup) -**Target Release:** v5.4.0 (Delivered in v5.4.1) -**Estimated Effort:** 10-12 hours over 2 weeks - ---- - -## Overview - -Add intelligent Quarto project detection and guided migration workflow to `teach-init` command. Enable safe, automated conversion of existing course websites (like STAT 545, STAT 440, causal-inference) to teaching workflow with branch safety, quick deploy, and week calculation features. - -**Key Value:** Unlocks teaching workflow adoption for all existing teaching projects, not just new courses. - ---- - -## Primary User Story - -**As a** course instructor (DT) with existing Quarto course websites, -**I want** to convert them to the teaching workflow with branch safety and quick deployment, -**So that** I can safely edit course materials on draft branch while students see stable production content. - -### Acceptance Criteria - -1. ✅ `teach-init` auto-detects existing Quarto projects via `_quarto.yml` -2. ✅ Strict validation prevents migration on invalid projects (missing required files) -3. ✅ Interactive prompt handles renv/ directories (R package management) -4. ✅ Git tag backup created before migration (lightweight, no directory copy) -5. ✅ Automatic rollback on ANY migration error -6. ✅ All existing git history preserved during migration -7. ✅ Auto-generated `MIGRATION-COMPLETE.md` documentation -8. ✅ Semester date prompts appear after git setup (not before) -9. ✅ Optional GitHub remote push integration (prompt for URL) -10. ✅ `--dry-run` flag shows migration plan without changes -11. ✅ Unit tests validate detection, validation, rollback functions -12. ✅ Integration tests validate end-to-end Quarto migration -13. ✅ Real-world migrations succeed: STAT 545, STAT 440, causal-inference - ---- - -## Secondary User Stories - -### Story 2: Safe Migration with Rollback - -**As a** course instructor, -**I want** automatic rollback if migration fails, -**So that** I never end up with a broken course repository mid-semester. - -**Acceptance Criteria:** -- Error detection at every migration step -- Automatic git reset to pre-migration tag -- Cleanup of created files (.flow/, scripts/) -- Clear error message showing what failed - ---- - -### Story 3: Dry-Run Validation - -**As a** course instructor, -**I want** to preview migration changes before committing, -**So that** I can verify the plan matches my expectations. - -**Acceptance Criteria:** -- `teach-init --dry-run "Course"` shows plan -- No git changes made -- Shows detected project type, validation results -- Shows what branches would be created -- Shows what files would be added - ---- - -## Technical Requirements - -### Architecture - -#### Current Flow (v5.3.0) - -``` -teach-init "Course" - ↓ -Check git - ├→ No git: Error "Initialize git first" - └→ Has git: _teach_migrate_existing_repo - ↓ - Strategy menu (2 options) - ├→ In-place conversion - └→ Two-branch setup -``` - -#### Proposed Flow (v5.4.0) - -``` -teach-init [--dry-run] "Course" - ↓ -Parse flags - ↓ -Check git - ├→ No git: Offer to initialize - │ ↓ - │ git init → Continue - │ - └→ Has git: Detect project type - ├→ Quarto: _teach_migrate_quarto_project - │ ↓ - │ Validate (_teach_validate_quarto_project) - │ ├→ FAIL: Show errors, exit - │ └→ PASS: Continue - │ ↓ - │ Handle renv (_teach_handle_renv) - │ ↓ - │ Create rollback tag - │ ↓ - │ Execute migration (with error trap) - │ ├→ SUCCESS: Continue - │ └→ FAIL: Rollback - │ ↓ - │ Install templates - │ ↓ - │ GitHub push? (optional) - │ ↓ - │ Generate docs - │ ↓ - │ Done - │ - └→ Generic: _teach_migrate_generic_repo (existing) -``` - ---- - -### API Design - -#### New Functions - -| Function | Purpose | Input | Output | -|----------|---------|-------|--------| -| `_teach_detect_project_type` | Detect project type | None (checks CWD) | "quarto" \| "mkdocs" \| "unknown" | -| `_teach_validate_quarto_project` | Validate Quarto structure | None | 0 (success) \| 1 (fail) | -| `_teach_handle_renv` | Handle renv/ directories | None | Modifies .gitignore if needed | -| `_teach_rollback_migration` | Rollback failed migration | tag_name | None (resets to tag) | -| `_teach_migrate_quarto_project` | Quarto-specific migration | course_name | 0 (success) \| 1 (fail) | -| `_teach_offer_github_push` | Optional GitHub integration | None | Pushes if user confirms | -| `_teach_generate_migration_docs` | Create MIGRATION-COMPLETE.md | course_name, dates | None (creates file) | -| `_teach_show_migration_plan` | Dry-run output | course_name | None (prints plan) | - ---- - -#### Modified Functions - -| Function | Changes | Rationale | -|----------|---------|-----------| -| `teach-init` | Add `--dry-run` flag parsing | Enable preview mode | -| `teach-init` | Offer `git init` if no .git/ | Smoother UX for fresh projects | -| `_teach_migrate_existing_repo` | Add project type detection | Route to Quarto vs generic flow | -| `_teach_install_templates` | Wrap in error trap | Enable rollback on template failure | - ---- - -### Data Models - -#### Project Detection Result - -```zsh -# Returned by _teach_detect_project_type -"quarto" # Has _quarto.yml -"mkdocs" # Has mkdocs.yml (future) -"unknown" # Generic git repo -``` - -#### Validation Result - -```zsh -# _teach_validate_quarto_project returns: -0 # Valid: _quarto.yml + index.qmd present -1 # Invalid: missing required files - -# Errors array populated on failure: -errors=( - "Missing _quarto.yml" - "Missing index.qmd (homepage)" -) -``` - -#### Migration Strategy - -```zsh -# User selects one of three strategies: -1 # Convert existing → production (preserve history) -2 # Create parallel branches (keep existing + add draft/prod) -3 # Fresh start (tag current, start new structure) -``` - ---- - -### Dependencies - -| Dependency | Required | Purpose | Installation | -|------------|----------|---------|--------------| -| **yq** | Yes | YAML config parsing | `brew install yq` | -| **git** | Yes | Version control | Built-in (macOS) | -| **gh CLI** | Optional | GitHub push integration | `brew install gh` | -| **rsync** | Optional | Future: directory backups | Built-in (macOS) | - ---- - -## UI/UX Specifications - -### User Flow: Quarto Migration - -``` -┌────────────────────────────────────────────────────────────┐ -│ Step 1: Detection │ -├────────────────────────────────────────────────────────────┤ -│ │ -│ $ teach-init "STAT 545" │ -│ │ -│ 🎓 Initializing teaching workflow for: STAT 545 │ -│ │ -│ 📋 Detected existing git repository │ -│ 📚 Detected: Quarto website │ -│ Current branch: main │ -│ │ -└────────────────────────────────────────────────────────────┘ - -┌────────────────────────────────────────────────────────────┐ -│ Step 2: Validation │ -├────────────────────────────────────────────────────────────┤ -│ │ -│ ✅ _quarto.yml found │ -│ ✅ index.qmd found │ -│ ✅ Project structure valid │ -│ │ -│ ⚠️ Detected renv/ directory │ -│ R package management with symlinks (not suitable for git)│ -│ │ -│ Exclude renv/ from git? [Y/n]: █ │ -│ │ -└────────────────────────────────────────────────────────────┘ - -┌────────────────────────────────────────────────────────────┐ -│ Step 3: Strategy Selection │ -├────────────────────────────────────────────────────────────┤ -│ │ -│ Choose migration strategy: │ -│ 1. Convert existing branch → production (preserve history)│ -│ 2. Create parallel branches (keep existing + add draft) │ -│ 3. Fresh start (tag current, start new structure) │ -│ │ -│ Choice [1/2/3]: █ │ -│ │ -└────────────────────────────────────────────────────────────┘ - -┌────────────────────────────────────────────────────────────┐ -│ Step 4: Confirmation │ -├────────────────────────────────────────────────────────────┤ -│ │ -│ ⚠️ This will: │ -│ 1. Create rollback tag: spring-2026-pre-migration │ -│ 2. Rename main → production │ -│ 3. Create new draft branch from production │ -│ 4. Add .flow/teach-config.yml and scripts/ │ -│ │ -│ Continue? [y/N]: █ │ -│ │ -└────────────────────────────────────────────────────────────┘ - -┌────────────────────────────────────────────────────────────┐ -│ Step 5: Migration Execution │ -├────────────────────────────────────────────────────────────┤ -│ │ -│ 🏷️ Creating rollback tag... │ -│ ✅ Tag created: spring-2026-pre-migration │ -│ │ -│ 🔀 Renaming branch... │ -│ ✅ Renamed main → production │ -│ │ -│ 🌿 Creating draft branch... │ -│ ✅ Created draft from production │ -│ │ -│ 📝 Installing templates... │ -│ ✅ Created .flow/teach-config.yml │ -│ ✅ Created scripts/quick-deploy.sh │ -│ ✅ Created scripts/semester-archive.sh │ -│ ✅ Created .github/workflows/deploy.yml │ -│ │ -└────────────────────────────────────────────────────────────┘ - -┌────────────────────────────────────────────────────────────┐ -│ Step 6: Semester Configuration │ -├────────────────────────────────────────────────────────────┤ -│ │ -│ Semester Schedule │ -│ Configure semester start/end dates for week calculation │ -│ │ -│ Start date (YYYY-MM-DD) [2026-01-13]: █ │ -│ │ -│ Calculated end date: 2026-05-06 (16 weeks) │ -│ │ -│ Add spring/fall break? [y/N]: y │ -│ │ -│ Break name [Spring Break]: █ │ -│ Break start [2026-03-10]: █ │ -│ Break end [2026-03-17]: █ │ -│ │ -└────────────────────────────────────────────────────────────┘ - -┌────────────────────────────────────────────────────────────┐ -│ Step 7: GitHub Integration (Optional) │ -├────────────────────────────────────────────────────────────┤ -│ │ -│ GitHub Integration (Optional) │ -│ Push to GitHub remote? [y/N]: y │ -│ │ -│ GitHub remote URL: https://github.com/dtofighi/stat-545 │ -│ │ -│ 📤 Pushing to remote... │ -│ ✅ Pushed draft → origin/draft │ -│ ✅ Pushed production → origin/production │ -│ │ -└────────────────────────────────────────────────────────────┘ - -┌────────────────────────────────────────────────────────────┐ -│ Step 8: Documentation Generation │ -├────────────────────────────────────────────────────────────┤ -│ │ -│ 📄 Generating migration documentation... │ -│ ✅ Created MIGRATION-COMPLETE.md │ -│ │ -└────────────────────────────────────────────────────────────┘ - -┌────────────────────────────────────────────────────────────┐ -│ Step 9: Success │ -├────────────────────────────────────────────────────────────┤ -│ │ -│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ -│ 🎉 Teaching workflow initialized! │ -│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ -│ │ -│ Next steps: │ -│ │ -│ 1. Review config: │ -│ $EDITOR .flow/teach-config.yml │ -│ │ -│ 2. Test deployment: │ -│ ./scripts/quick-deploy.sh │ -│ │ -│ 3. Start working: │ -│ work STAT 545 │ -│ │ -│ 📚 Documentation: │ -│ https://data-wise.github.io/flow-cli/ │ -│ │ -└────────────────────────────────────────────────────────────┘ -``` - ---- - -### Wireframes (ASCII) - -#### Dry-Run Output - -``` -$ teach-init --dry-run "STAT 545" - -┌────────────────────────────────────────────────────────────┐ -│ 🔍 DRY RUN MODE - No changes will be made │ -├────────────────────────────────────────────────────────────┤ -│ │ -│ Migration Plan for: STAT 545 │ -│ │ -│ Detection: │ -│ ✅ Git repository found │ -│ ✅ Project type: Quarto website │ -│ ✅ Current branch: main │ -│ │ -│ Validation: │ -│ ✅ _quarto.yml found │ -│ ✅ index.qmd found │ -│ ⚠️ renv/ detected (will prompt to exclude) │ -│ │ -│ Actions that would be taken: │ -│ 1. Create rollback tag: spring-2026-pre-migration │ -│ 2. Rename main → production │ -│ 3. Create draft branch from production │ -│ 4. Add .flow/teach-config.yml │ -│ 5. Add scripts/quick-deploy.sh │ -│ 6. Add scripts/semester-archive.sh │ -│ 7. Add .github/workflows/deploy.yml │ -│ 8. Prompt for semester dates │ -│ 9. Prompt for GitHub push (optional) │ -│ 10. Generate MIGRATION-COMPLETE.md │ -│ │ -│ Estimated time: ~3 minutes │ -│ │ -│ To execute for real: │ -│ teach-init "STAT 545" │ -│ │ -└────────────────────────────────────────────────────────────┘ -``` - ---- - -#### Error with Rollback - -``` -$ teach-init "STAT 545" - -[... migration steps ...] - -📝 Installing templates... -✅ Created .flow/teach-config.yml -❌ ERROR: Failed to copy quick-deploy.sh - -┌────────────────────────────────────────────────────────────┐ -│ ❌ Migration Failed - Rolling Back │ -├────────────────────────────────────────────────────────────┤ -│ │ -│ Error: Failed to copy quick-deploy.sh │ -│ Location: _teach_install_templates │ -│ │ -│ 🔄 Automatic Rollback: │ -│ ✅ Reset to tag: spring-2026-pre-migration │ -│ ✅ Removed .flow/ directory │ -│ ✅ Removed scripts/ directory │ -│ ✅ Removed .github/workflows/deploy.yml │ -│ ✅ Deleted rollback tag │ -│ │ -│ Your repository is back to its original state. │ -│ │ -│ 💡 Troubleshooting: │ -│ - Check file permissions │ -│ - Verify FLOW_PLUGIN_DIR is set │ -│ - Run: flow doctor │ -│ │ -└────────────────────────────────────────────────────────────┘ -``` - ---- - -### Accessibility Checklist - -- [x] Color-blind friendly (use icons + text, not just color) -- [x] Clear progress indicators (emoji + text) -- [x] Keyboard-only navigation (no mouse required) -- [x] Error messages actionable (tell user HOW to fix) -- [x] Success criteria explicit (what does "done" mean?) -- [x] Dry-run available (preview before commit) -- [x] Rollback automatic (no manual recovery steps) -- [x] Documentation auto-generated (no separate docs task) - ---- - -## Open Questions - -### Q1: How to handle semester date errors? - -**Current:** Validation stops on invalid format (e.g., "Jan 13" instead of "2025-01-13") - -**Options:** -1. Strict: Reject any format except YYYY-MM-DD -2. Permissive: Allow any format, warn if unparseable, fallback to manual edit -3. Smart: Try to parse various formats (dateutil-style), convert to YYYY-MM-DD - -**Decision:** Strict (Option 1) - Least ambiguity, clearest UX - -**Rationale:** Semester dates are critical for week calculation. Better to error early with clear message than accept ambiguous input. - ---- - -### Q2: Should we support migration from non-current branches? - -**Current:** Assumes migration from current branch - -**Options:** -1. Current branch only (simplest) -2. Add `--from ` flag to specify -3. Auto-detect if branch name suggests semester (e.g., feature/spring-2025) -4. Interactive: "Migrate from current (main) or different branch?" - -**Decision:** Current branch only (Option 1) for v5.4.0 - -**Rationale:** YAGNI principle. If needed, add `--from` flag in future version. - ---- - -### Q3: What if GitHub remote already exists? - -**Current:** `git remote add origin ` will fail if origin exists - -**Options:** -1. Error: "Remote already exists - push manually" -2. Detect: Use `git remote set-url origin ` -3. Ask: "Remote exists. Update URL or skip?" -4. Smart: If URL matches, just push. If different, ask. - -**Decision:** Smart detection (Option 4) - -**Implementation:** - -```zsh -if git remote get-url origin &>/dev/null; then - current_url=$(git remote get-url origin) - if [[ "$current_url" == "$remote_url" ]]; then - # Same URL, just push - git push -u origin draft production - else - # Different URL, ask user - echo "Remote origin exists: $current_url" - read "?Update to $remote_url? [y/N]: " update - if [[ "$update" == "y" ]]; then - git remote set-url origin "$remote_url" - git push -u origin draft production - fi - fi -else - # No remote, add it - git remote add origin "$remote_url" - git push -u origin draft production -fi -``` - ---- - -### Q4: Should dry-run show actual file contents? - -**Current spec:** Dry-run shows plan only (what WOULD happen) - -**Options:** -1. Plan only (current spec) -2. Plan + file previews (show generated .flow/teach-config.yml) -3. Plan + full simulation (generate all files in /tmp, show diffs) -4. Interactive: "Show details? [y/N]" after plan - -**Decision:** Plan only (Option 1) for v5.4.0, add `--verbose` flag later - -**Rationale:** Dry-run should be fast (< 1 second). Generating files defeats purpose. - -**Future enhancement:** Add `teach-init --dry-run --verbose` to show file previews - ---- - -## Review Checklist - -### Completeness - -- [x] All 10 user requirements addressed -- [x] All functions defined with signatures -- [x] All user flows documented with wireframes -- [x] Error handling specified -- [x] Rollback mechanism detailed -- [x] Testing strategy included -- [x] Open questions resolved - -### Clarity - -- [x] Primary user story clear -- [x] Acceptance criteria measurable -- [x] Architecture diagrams present -- [x] Code examples provided -- [x] UX flows illustrated - -### Feasibility - -- [x] Implementation phases defined (4 phases) -- [x] Effort estimated (10-12 hours) -- [x] Dependencies identified (yq, git) -- [x] Test strategy comprehensive -- [x] Rollback safety proven (git tags) - -### Alignment - -- [x] Matches brainstorm decisions -- [x] Follows flow-cli conventions -- [x] Integrates with existing commands -- [x] Compatible with teaching workflow v2.0 - ---- - -## Implementation Notes - -### Phase 1: Foundation (2-3 hours) - -**Goal:** Core safety features - -**Tasks:** -1. Add `_teach_detect_project_type()` function -2. Add `_teach_validate_quarto_project()` function -3. Add `_teach_handle_renv()` function -4. Add `_teach_rollback_migration()` function -5. Add `--dry-run` flag parsing to `teach-init` - -**Tests:** -- Unit test each function in isolation -- Mock git commands for testing - -**Deliverable:** Safety infrastructure in place - ---- - -### Phase 2: Migration Logic (3-4 hours) - -**Goal:** Smart Quarto migration - -**Tasks:** -1. Refactor `_teach_migrate_existing_repo()` with detection -2. Create `_teach_migrate_quarto_project()` function -3. Add three migration strategies (convert, parallel, fresh) -4. Integrate error handling with rollback -5. Add semester date prompts after git setup - -**Tests:** -- Integration test: full Quarto migration -- Integration test: rollback on error -- Integration test: dry-run shows plan - -**Deliverable:** End-to-end Quarto migration works - ---- - -### Phase 3: Polish (2-3 hours) - -**Goal:** Production features - -**Tasks:** -1. Add `_teach_offer_github_push()` function -2. Add `_teach_generate_migration_docs()` function -3. Handle existing GitHub remotes (smart detection) -4. Polish UX messages and progress indicators -5. Update `_teach_show_next_steps()` with migration context - -**Tests:** -- Manual test: GitHub push integration -- Manual test: MIGRATION-COMPLETE.md generation -- Manual test: Existing remote handling - -**Deliverable:** Production-ready v5.4.0 - ---- - -### Phase 4: Real-World Testing (2-3 hours) - -**Goal:** Validate with actual courses - -**Tasks:** -1. Dry-run on STAT 545 -2. Full migration on STAT 545 -3. Full migration on STAT 440 -4. Full migration on causal-inference -5. Gather feedback, iterate - -**Tests:** -- Real project validation -- User acceptance testing - -**Deliverable:** Confidence for release - ---- - -## History - -| Date | Event | Notes | -|------|-------|-------| -| 2026-01-12 | Brainstorm session | 10 questions, deep mode, feature focus | -| 2026-01-12 | Spec created | From BRAINSTORM-teach-init-migration-2026-01-12.md | -| TBD | Implementation start | Phase 1: Foundation | -| TBD | PR to dev | Phase 1-3 complete | -| TBD | Release v5.4.0 | After real-world testing | - ---- - -## Success Criteria - -### Must Have (v5.4.0 Release) - -- [x] All 10 user requirements implemented -- [x] Unit tests pass (detection, validation, rollback) -- [x] Integration tests pass (full migration, error rollback) -- [x] Real migration succeeds: STAT 545, STAT 440, causal-inference -- [x] Documentation updated (teach-init reference, migration guide) -- [x] Zero data loss (rollback works 100%) - -### Should Have (v5.5.0) - -- [ ] `teach-init --diagnose` for readiness check -- [ ] Batch migration support -- [ ] Migration progress UI -- [ ] Video tutorial created - -### Nice to Have (Future) - -- [ ] Migration undo command -- [ ] Migration history log -- [ ] Template system for other frameworks (Jupyter, R Markdown) - ---- - -## Related Documentation - -- [Teaching Workflow v2.0 Spec](SPEC-teaching-workflow-v2.md) -- [TEACH Dispatcher Reference](../reference/MASTER-DISPATCHER-GUIDE.md#teach-dispatcher) -- [DOT Dispatcher Tutorial](../tutorials/12-dot-dispatcher.md) -- [Architecture Reference](../reference/MASTER-ARCHITECTURE.md) -- [Testing Guide](../guides/TESTING.md) - ---- - -**Implementation Complete:** v5.4.1 - See [TEACH Dispatcher Reference](../reference/MASTER-DISPATCHER-GUIDE.md#teach-dispatcher) for current documentation. diff --git a/docs/specs/SPEC-teach-plan-create-2026-01-27.md b/docs/specs/SPEC-teach-plan-create-2026-01-27.md deleted file mode 100644 index b1a59b8e1..000000000 --- a/docs/specs/SPEC-teach-plan-create-2026-01-27.md +++ /dev/null @@ -1,196 +0,0 @@ -# Implementation Spec: teach plan create Command - -**Status:** draft -**Created:** 2026-01-27 -**From Issue:** #278 -**Priority:** High (fixes broken UX) -**Effort Estimate:** 2-3 hours - ---- - -## Overview - -Add `teach plan` subcommand to create and manage lesson plan YAML files. This fixes a UX bug where error messages reference `teach plan create` but the command doesn't exist. - -**Key Value:** Completes existing lesson plan infrastructure (v5.13.0+) that already has consumers but no creator. - ---- - -## Primary User Story - -**As an instructor using `teach slides --week N`,** -**I want** to create a lesson plan file when prompted, -**So that** I can provide detailed content context beyond what's in `teach-config.yml`. - -**Acceptance Criteria:** -1. `teach plan create ` creates `.flow/lesson-plans/week-N.yml` with template -2. `teach plan list` shows existing lesson plans -3. `teach plan show ` displays lesson plan content -4. Help text updated to include `plan` subcommand -5. Existing hint message (line 587) now works - ---- - -## Technical Requirements - -### Existing Infrastructure (Already Implemented) - -| Function | Purpose | Location | -|----------|---------|----------| -| `_teach_load_lesson_plan()` | Parse YAML lesson plan | teach-dispatcher.zsh:491 | -| `_teach_integrate_lesson_plan()` | Apply plan to Scholar | teach-dispatcher.zsh:598 | -| `_teach_prompt_missing_plan()` | User prompt when missing | teach-dispatcher.zsh:558 | -| `_teach_lookup_topic()` | Fallback topic lookup | teach-dispatcher.zsh:534 | - -### New Commands - -```bash -teach plan create # Create YAML template -teach plan list # List existing plans -teach plan show # Display plan content -teach plan help # Show help -``` - -### YAML Template (Existing Schema) - -```yaml -# Week N Lesson Plan -# Created: YYYY-MM-DD -# Edit this file to customize Scholar output for this week - -topic: "" # Required: Main topic for the week -style: "conceptual" # Options: conceptual | computational | rigorous | applied - -objectives: - - "Objective 1" - - "Objective 2" - -subtopics: - - "Subtopic 1" - - "Subtopic 2" - -key_concepts: - - "Concept 1" - - "Concept 2" - -prerequisites: - - "Prerequisite 1" - -# Optional: readings, activities, assignment -readings: - - "Reading 1" - -activities: - lecture: "" - lab: "" - -assignment: - name: "" - due: "" -``` - -### File Structure - -``` -.flow/ -└── lesson-plans/ - ├── week-1.yml - ├── week-2.yml - └── ... -``` - ---- - -## Implementation Plan - -### Phase 1: Core Commands (2-3 hours) - -1. **Add dispatcher case** (teach-dispatcher.zsh ~line 4844) - ```zsh - plan|pl) - case "$1" in - create|c) shift; _teach_plan_create "$@" ;; - list|ls|l) _teach_plan_list ;; - show|s) shift; _teach_plan_show "$@" ;; - help|--help|-h) _teach_plan_help ;; - *) _teach_plan_help ;; - esac - ;; - ``` - -2. **Implement `_teach_plan_create()`** - - Validate week number (1-16 range) - - Create `.flow/lesson-plans/` if needed - - Check if file exists (warn, offer --force) - - Write YAML template with comments - - Show success message with edit hint - -3. **Implement `_teach_plan_list()`** - - List files in `.flow/lesson-plans/` - - Show week number and topic for each - - Handle empty state - -4. **Implement `_teach_plan_show()`** - - Display lesson plan content - - Format with colors - - Handle missing file - -5. **Add `_teach_plan_help()`** - - Document all subcommands - - Show examples - -### Phase 2: Testing (30 min) - -- Unit tests for plan creation -- E2E test: create → load → integrate flow - -### Phase 3: Documentation (30 min) - -- Update dispatcher reference -- Add to help system - ---- - -## Deferred (Future Enhancement) - -| Feature | Reason to Defer | -|---------|-----------------| -| `--interactive` mode | Basic template sufficient for MVP | -| `--from-markdown FILE` | Complex parsing, unclear format | -| `teach plan convert` | Same as above | -| Validation of plan content | yq handles basic YAML validation | - ---- - -## Dependencies - -- None (uses existing ZSH, mkdir, cat) -- yq recommended but not required for creation - ---- - -## Open Questions - -1. Should `teach plan create` auto-populate topic from `teach-config.yml`? - - **Recommendation:** Yes, if available - -2. Should we validate week number against `semester_info.weeks`? - - **Recommendation:** Warn if week not in config, but allow creation - ---- - -## Review Checklist - -- [ ] Code follows project conventions -- [ ] Help text comprehensive -- [ ] Error messages helpful -- [ ] Tests cover happy path and edge cases -- [ ] Documentation updated - ---- - -## History - -| Date | Change | -|------|--------| -| 2026-01-27 | Initial spec from brainstorm session | diff --git a/docs/specs/SPEC-teach-prompt-command-2026-01-21.md b/docs/specs/SPEC-teach-prompt-command-2026-01-21.md deleted file mode 100644 index d888b4b1d..000000000 --- a/docs/specs/SPEC-teach-prompt-command-2026-01-21.md +++ /dev/null @@ -1,758 +0,0 @@ -# SPEC: teach prompt Command - -**Status:** draft -**Created:** 2026-01-21 -**From Brainstorm:** [BRAINSTORM-pr283-improvements-2026-01-21.md](BRAINSTORM-pr283-improvements-2026-01-21.md) -**Target Version:** v5.16.0 - ---- - -## Overview - -Add a `teach prompt` subcommand to the teach dispatcher that allows users to list and view Claude Code teaching prompts. This complements PR #283 which adds the prompt template files. - ---- - -## Primary User Story - -**As a** statistics instructor using flow-cli, -**I want to** quickly view Claude Code teaching prompts from the command line, -**So that** I can reference the prompt structure when generating course content with Scholar. - -### Acceptance Criteria - -- [ ] `teach prompt list` displays all available prompts with descriptions -- [ ] `teach prompt ` opens the prompt file in a pager -- [ ] `teach prompt help` shows usage documentation -- [ ] Unknown prompt names show helpful error message -- [ ] Tab completion works for prompt names - ---- - -## Secondary User Stories - -### Story 2: Documentation Integration - -**As a** new flow-cli user, -**I want to** find teaching prompt documentation in the workflow guide, -**So that** I understand how prompts integrate with Scholar commands. - -**Acceptance Criteria:** -- [ ] TEACHING-WORKFLOW-V3-GUIDE.md includes prompts section -- [ ] Documentation explains Scholar integration -- [ ] Cross-references to Scholar plugin docs - -### Story 3: PR #283 Cleanup - -**As a** repository maintainer, -**I want to** have implementation docs in the proper location, -**So that** the repo structure is consistent and discoverable. - -**Acceptance Criteria:** -- [ ] IMPLEMENTATION.md moved to docs/specs/ -- [ ] README.md Scholar path reference clarified -- [ ] Markdown lint validation passes - ---- - -## System Architecture - -### flow-cli Teaching Ecosystem - -```mermaid -flowchart TB - subgraph "User Interface Layer" - CLI[teach command] - COMP[ZSH Completions] - end - - subgraph "Dispatcher Layer" - TD[teach-dispatcher.zsh] - TD --> |routes| INIT[teach init] - TD --> |routes| STATUS[teach status] - TD --> |routes| DEPLOY[teach deploy] - TD --> |routes| PROMPT[teach prompt] - TD --> |routes| OTHER[... other commands] - end - - subgraph "Template Layer" - TEMPLATES[lib/templates/teaching/] - TEMPLATES --> CONFIG[teach-config.yml.template] - TEMPLATES --> EXAM[exam-template.md] - TEMPLATES --> PROMPTS[claude-prompts/] - end - - subgraph "Claude Prompts" - PROMPTS --> LN[lecture-notes.md] - PROMPTS --> RS[revealjs-slides.md] - PROMPTS --> DA[derivations-appendix.md] - PROMPTS --> README[README.md] - end - - subgraph "External Integration" - SCHOLAR[Scholar Plugin] - CLAUDE[Claude Code] - end - - CLI --> TD - COMP --> CLI - PROMPT --> PROMPTS - SCHOLAR -.->|uses structure| PROMPTS - CLAUDE -.->|references| PROMPTS -``` - -### Component Hierarchy - -```mermaid -flowchart LR - subgraph "teach-dispatcher.zsh" - MAIN[teach function] - MAIN --> CASE{case statement} - - CASE --> |prompt| TP[_teach_prompt] - - subgraph "_teach_prompt functions" - TP --> TPL[_teach_prompt_list] - TP --> TPH[_teach_prompt_help] - TP --> TPS[_teach_prompt_show] - end - end - - subgraph "lib/core.zsh" - LOG[_flow_log_*] - COLORS[Color constants] - end - - TPL --> LOG - TPL --> COLORS - TPS --> LOG -``` - ---- - -## Workflow Processes - -### Workflow 1: Content Creation with Prompts - -**Goal:** Instructor creates lecture notes using Claude Code prompt as reference - -```mermaid -sequenceDiagram - participant I as Instructor - participant T as teach prompt - participant P as Pager (less) - participant S as Scholar Plugin - participant C as Claude Code - - I->>T: teach prompt lecture-notes - T->>T: Locate prompt file - T->>P: Open in pager - P->>I: Display prompt structure - - Note over I: Reviews prompt structure - - I->>S: /teaching:lecture "ANOVA" - S->>C: Generate content - C->>S: Return lecture notes - S->>I: Save to course directory - - Note over I: Content follows prompt structure -``` - -### Workflow 2: Discovering Available Prompts - -**Goal:** New user explores what prompts are available - -```mermaid -sequenceDiagram - participant U as User - participant T as teach prompt - participant FS as File System - participant H as Help System - - U->>T: teach prompt - T->>H: Show help (no args) - H->>U: Display usage + examples - - U->>T: teach prompt list - T->>FS: Scan claude-prompts/ - FS->>T: Return *.md files - T->>T: Extract descriptions - T->>U: Display formatted list - - U->>T: teach prompt derivations-appendix - T->>FS: Read file - FS->>T: Return content - T->>U: Open in pager -``` - -### Workflow 3: Error Recovery - -**Goal:** User types invalid prompt name, gets helpful guidance - -```mermaid -sequenceDiagram - participant U as User - participant T as teach prompt - participant FS as File System - - U->>T: teach prompt anova-lecture - T->>FS: Check for anova-lecture.md - FS->>T: File not found - T->>T: Log error with suggestion - T->>U: "Unknown prompt: anova-lecture" - T->>U: "Run 'teach prompt list' to see available prompts" - - Note over U: User learns correct name - - U->>T: teach prompt lecture-notes - T->>FS: Check for lecture-notes.md - FS->>T: File exists - T->>U: Open in pager -``` - ---- - -## Integration Architecture - -### Scholar Plugin Integration - -```mermaid -flowchart TB - subgraph "flow-cli (This Project)" - TP[teach prompt command] - PROMPTS[claude-prompts/*.md] - TP --> |reads| PROMPTS - end - - subgraph "Scholar Plugin (External)" - SC[/teaching:* commands] - TS[teaching-style.local.md] - SC --> |uses structure from| PROMPTS - SC --> |customized by| TS - end - - subgraph "User's Course Repo" - COURSE[course directory] - TS --> |lives in| COURSE - OUTPUT[generated content] - SC --> |creates| OUTPUT - end - - USER((Instructor)) - USER --> |references| TP - USER --> |invokes| SC - USER --> |configures| TS -``` - -### Data Flow: Prompt to Content - -```mermaid -flowchart LR - subgraph "Reference" - P1[lecture-notes.md] - P2[revealjs-slides.md] - P3[derivations-appendix.md] - end - - subgraph "Customization" - TS[teaching-style.local.md] - end - - subgraph "Generation" - SCHOLAR[Scholar /teaching:*] - CLAUDE[Claude Code LLM] - end - - subgraph "Output" - LEC[lecture.qmd] - SLIDES[slides.qmd] - APPENDIX[appendix.qmd] - end - - P1 -.->|structure| SCHOLAR - P2 -.->|structure| SCHOLAR - P3 -.->|structure| SCHOLAR - TS -->|preferences| SCHOLAR - SCHOLAR -->|prompt| CLAUDE - CLAUDE -->|content| SCHOLAR - SCHOLAR --> LEC - SCHOLAR --> SLIDES - SCHOLAR --> APPENDIX -``` - ---- - -## Command Architecture - -### Internal Function Design - -```mermaid -flowchart TD - subgraph "_teach_prompt()" - ENTRY[Entry Point] - ENTRY --> PARSE{Parse $1} - - PARSE -->|"" or help| HELP[_teach_prompt_help] - PARSE -->|list| LIST[_teach_prompt_list] - PARSE -->|*| SHOW[_teach_prompt_show] - - subgraph "_teach_prompt_list" - L1[Get prompts_dir path] - L2[Loop through *.md files] - L3[Skip README.md] - L4[Extract title from # heading] - L5[Format with colors] - L6[Print usage hint] - L1 --> L2 --> L3 --> L4 --> L5 --> L6 - end - - subgraph "_teach_prompt_show" - S1[Build file path] - S2{File exists?} - S3[Open in PAGER] - S4[Log error] - S5[Show hint] - S1 --> S2 - S2 -->|Yes| S3 - S2 -->|No| S4 --> S5 - end - end -``` - -### State Diagram: Command Execution - -```mermaid -stateDiagram-v2 - [*] --> ParseArgs: teach prompt [args] - - ParseArgs --> ShowHelp: no args - ParseArgs --> ShowHelp: "help" or "-h" - ParseArgs --> ListPrompts: "list" - ParseArgs --> ShowPrompt: prompt name - - ShowHelp --> [*]: exit 0 - - ListPrompts --> ScanDir: get prompt files - ScanDir --> FormatOutput: extract titles - FormatOutput --> [*]: exit 0 - - ShowPrompt --> CheckExists: validate file - CheckExists --> OpenPager: file exists - CheckExists --> ShowError: file missing - OpenPager --> [*]: exit 0 - ShowError --> [*]: exit 1 -``` - ---- - -## API Design - -### Command Interface - -| Command | Description | Exit Code | -|---------|-------------|-----------| -| `teach prompt` | Show help | 0 | -| `teach prompt list` | List available prompts | 0 | -| `teach prompt ` | Display prompt in pager | 0 or 1 | -| `teach prompt help` | Show detailed help | 0 | - -### Function Signatures - -```zsh -# Main router -_teach_prompt() { - # Args: $@ - subcommand and arguments - # Returns: 0 on success, 1 on error -} - -# List all prompts -_teach_prompt_list() { - # Args: none - # Output: formatted list to stdout - # Returns: 0 -} - -# Show specific prompt -_teach_prompt_show() { - # Args: $1 - prompt name (without .md) - # Output: opens file in $PAGER - # Returns: 0 if found, 1 if not found -} - -# Display help -_teach_prompt_help() { - # Args: none - # Output: help text to stdout - # Returns: 0 -} -``` - -### Output Format - -**`teach prompt list`:** - -``` -Available Teaching Prompts - - lecture-notes Comprehensive Lecture Notes Generator - revealjs-slides RevealJS Presentation Generator - derivations-appendix Mathematical Derivations & Theory Appendix Generator - -Usage: teach prompt to view a prompt -``` - -**`teach prompt ` (error):** - -``` -✗ Unknown prompt: foo - -Run teach prompt list to see available prompts -``` - ---- - -## Data Models - -N/A - No data model changes. Command reads existing markdown files from `lib/templates/teaching/claude-prompts/`. - -### File Discovery Logic - -``` -prompts_dir = ${FLOW_ROOT}/lib/templates/teaching/claude-prompts - -for each file in prompts_dir/*.md: - if file.name == "README.md": - skip - else: - name = file.basename without .md - title = first line matching /^# .*/ - yield (name, title) -``` - ---- - -## Dependencies - -| Dependency | Type | Purpose | -|------------|------|---------| -| `lib/core.zsh` | Internal | Logging, colors | -| `$PAGER` or `less` | External | Display prompt content | -| Prompt files | Internal | Content source | -| `$FLOW_ROOT` | Environment | Plugin root path | - ---- - -## UI/UX Specifications - -### User Flow - -```mermaid -flowchart TD - A[User runs teach prompt] --> B{Has argument?} - B -->|No| C[Show help] - B -->|list| D[List all prompts] - B -->|help| C - B -->|name| E{Prompt exists?} - E -->|Yes| F[Open in pager] - E -->|No| G[Show error + hint] -``` - -### Wireframe (Terminal Output) - -**Help Output:** - -``` -┌────────────────────────────────────────────────────────────┐ -│ teach prompt - Display Claude Code teaching prompts │ -├────────────────────────────────────────────────────────────┤ -│ USAGE: │ -│ teach prompt │ -│ │ -│ COMMANDS: │ -│ list List available prompts │ -│ Display prompt in pager │ -│ help Show this help │ -│ │ -│ AVAILABLE PROMPTS: │ -│ lecture-notes Lecture documents (20-40 pages) │ -│ revealjs-slides Presentations (25+ slides) │ -│ derivations-appendix Mathematical theory appendices │ -│ │ -│ EXAMPLES: │ -│ teach prompt list │ -│ teach prompt lecture-notes │ -│ │ -│ INTEGRATION: │ -│ These prompts complement Scholar plugin commands: │ -│ - /teaching:lecture uses lecture-notes.md structure │ -│ - /teaching:slides uses revealjs-slides.md structure │ -└────────────────────────────────────────────────────────────┘ -``` - -**List Output:** - -``` -┌────────────────────────────────────────────────────────────┐ -│ $ teach prompt list │ -├────────────────────────────────────────────────────────────┤ -│ Available Teaching Prompts │ -│ │ -│ lecture-notes Comprehensive Lecture Notes... │ -│ revealjs-slides RevealJS Presentation Generator │ -│ derivations-appendix Mathematical Derivations & ... │ -│ │ -│ Usage: teach prompt to view a prompt │ -└────────────────────────────────────────────────────────────┘ -``` - -### Accessibility - -- Uses standard terminal colors from `lib/core.zsh` -- Works with screen readers (plain text output) -- Respects `$PAGER` environment variable -- No flashing or animation - ---- - -## Process Workflows - -### Implementation Process - -```mermaid -flowchart TD - subgraph "Phase 1: PR #283 Amendments" - A1[Fix README Scholar path] --> A2[Add markdown lint] - A2 --> A3[Merge PR #283] - end - - subgraph "Phase 2: Command Implementation" - B1[Create feature branch] - B1 --> B2[Add _teach_prompt to dispatcher] - B2 --> B3[Add _teach_prompt_help] - B3 --> B4[Add _teach_prompt_list] - B4 --> B5[Add _teach_prompt_show] - B5 --> B6[Add completions] - end - - subgraph "Phase 3: Documentation" - C1[Update TEACHING-WORKFLOW-V3-GUIDE] - C2[Move IMPLEMENTATION.md] - C3[Update CHANGELOG] - end - - subgraph "Phase 4: Testing & Release" - D1[Run test suite] - D2[Manual testing] - D3[Create PR to dev] - D4[Merge and tag v5.16.0] - end - - A3 --> B1 - B6 --> C1 - C1 --> C2 --> C3 - C3 --> D1 --> D2 --> D3 --> D4 -``` - -### User Workflow: First-Time Setup - -```mermaid -flowchart TD - START([Instructor wants to create content]) - - START --> Q1{Familiar with prompts?} - - Q1 -->|No| DISCOVER[teach prompt list] - DISCOVER --> REVIEW[teach prompt lecture-notes] - REVIEW --> UNDERSTAND[Understand structure] - - Q1 -->|Yes| UNDERSTAND - - UNDERSTAND --> CONFIG{Has teaching style?} - - CONFIG -->|No| CREATE[Create .claude/teaching-style.local.md] - CONFIG -->|Yes| GENERATE - - CREATE --> GENERATE[/teaching:lecture "Topic"] - - GENERATE --> RESULT[Content generated] - RESULT --> END([Review and publish]) -``` - -### User Workflow: Daily Usage - -```mermaid -flowchart LR - subgraph "Quick Reference" - A[teach prompt list] --> B[See available prompts] - end - - subgraph "Content Generation" - C[/teaching:lecture] --> D[Claude generates] - D --> E[Review output] - end - - subgraph "Customization" - F[Edit teaching-style.local.md] - F --> G[Re-run command] - end - - B -.->|informs| C - E -->|needs adjustment| F - G --> C -``` - ---- - -## Open Questions - -1. **Prompt copying:** Should `teach prompt copy ` copy to current directory for customization? → *Deferred to future* -2. **Scholar integration:** Should `teach prompt` launch Scholar command directly? → *No, keep separation of concerns* - ---- - -## Review Checklist - -- [ ] Code follows project conventions (ZSH style) -- [ ] Help function follows teach-dispatcher pattern -- [ ] Tab completions added to `completions/_teach` -- [ ] Tests cover list, show, error cases -- [ ] Documentation updated -- [ ] CHANGELOG.md entry added - ---- - -## Implementation Notes - -### Key Considerations - -1. **PAGER handling:** Use `${PAGER:-less}` for portability -2. **README exclusion:** Skip README.md when listing prompts -3. **Description extraction:** Parse first `#` heading from each file -4. **Error messages:** Include actionable hint (run `teach prompt list`) -5. **FLOW_ROOT:** Use plugin root for portable path resolution - -### File Changes - -| File | Change | -|------|--------| -| `lib/dispatchers/teach-dispatcher.zsh` | Add `prompt)` case + functions | -| `completions/_teach` | Add prompt subcommand completions | -| `docs/guides/TEACHING-WORKFLOW-V3-GUIDE.md` | Add prompts section | -| `lib/templates/teaching/claude-prompts/README.md` | Fix Scholar path note | -| `IMPLEMENTATION.md` | Move to `docs/specs/SPEC-teaching-prompts.md` | - -### Implementation Order - -1. Add `_teach_prompt()` function to teach-dispatcher.zsh -2. Add `_teach_prompt_help()` function -3. Add `_teach_prompt_list()` function -4. Add `_teach_prompt_show()` function -5. Update completions -6. Update documentation -7. Move IMPLEMENTATION.md -8. Test all scenarios - -### Code Template - -```zsh -# Add to teach-dispatcher.zsh case statement: -prompt) shift; _teach_prompt "$@" ;; - -# Main function -_teach_prompt() { - local prompts_dir="${FLOW_ROOT}/lib/templates/teaching/claude-prompts" - - case "$1" in - list) - _teach_prompt_list - ;; - help|--help|-h|"") - _teach_prompt_help - ;; - *) - _teach_prompt_show "$1" - ;; - esac -} - -_teach_prompt_list() { - local prompts_dir="${FLOW_ROOT}/lib/templates/teaching/claude-prompts" - - _flow_log_header "Available Teaching Prompts" - echo "" - - for f in "$prompts_dir"/*.md; do - [[ "$(basename "$f")" == "README.md" ]] && continue - local name=$(basename "$f" .md) - local desc=$(head -5 "$f" | grep -E "^# " | head -1 | sed 's/^# //') - printf " ${CYAN}%-25s${RESET} %s\n" "$name" "$desc" - done - - echo "" - echo "Usage: ${BOLD}teach prompt ${RESET} to view a prompt" -} - -_teach_prompt_show() { - local name="$1" - local prompts_dir="${FLOW_ROOT}/lib/templates/teaching/claude-prompts" - local prompt_file="$prompts_dir/$name.md" - - if [[ -f "$prompt_file" ]]; then - ${PAGER:-less} "$prompt_file" - else - _flow_log_error "Unknown prompt: $name" - echo "" - echo "Run ${BOLD}teach prompt list${RESET} to see available prompts" - return 1 - fi -} - -_teach_prompt_help() { - cat << 'EOF' -teach prompt - Display Claude Code teaching prompts - -USAGE: - teach prompt - -COMMANDS: - list List available prompts - Display prompt content (opens in pager) - help Show this help - -AVAILABLE PROMPTS: - lecture-notes Comprehensive lecture documents (20-40 pages) - revealjs-slides Visual presentations (25+ slides) - derivations-appendix Mathematical theory appendices - -EXAMPLES: - teach prompt list # See all prompts - teach prompt lecture-notes # View lecture prompt - teach prompt derivations-appendix # View derivations prompt - -INTEGRATION: - These prompts complement Scholar plugin commands: - - /teaching:lecture uses lecture-notes.md structure - - /teaching:slides uses revealjs-slides.md structure - - Customize output via .claude/teaching-style.local.md -EOF -} -``` - ---- - -## History - -| Date | Change | Author | -|------|--------|--------| -| 2026-01-21 | Initial spec from brainstorm | Claude Code | -| 2026-01-21 | Added architecture & workflow diagrams | Claude Code | - ---- - -## Related - -- **PR #283:** Teaching prompts addition (prerequisite) -- **Brainstorm:** [BRAINSTORM-pr283-improvements-2026-01-21.md](BRAINSTORM-pr283-improvements-2026-01-21.md) -- **Guide:** [TEACHING-WORKFLOW-V3-GUIDE.md](../guides/TEACHING-WORKFLOW-V3-GUIDE.md) diff --git a/docs/specs/SPEC-teach-scholar-enhancement-2026-01-17.md b/docs/specs/SPEC-teach-scholar-enhancement-2026-01-17.md deleted file mode 100644 index b71f1e3c4..000000000 --- a/docs/specs/SPEC-teach-scholar-enhancement-2026-01-17.md +++ /dev/null @@ -1,475 +0,0 @@ -# SPEC: Teach Dispatcher Scholar Enhancement - -**Feature:** Comprehensive teach dispatcher enhancement with Scholar plugin integration -**Status:** Draft -**Created:** 2026-01-17 -**From Brainstorm:** Deep interactive sessions (merged from 2 specs) -**Target Release:** flow-cli v5.13.0 -**Estimated Effort:** 20-24 hours across 6 phases - ---- - -## Metadata - -| Field | Value | -| ------------------- | -------------------------------------------------- | -| **Status** | Draft | -| **Priority** | High (enables 10x faster course material creation) | -| **Complexity** | Medium-High (20-24 hours) | -| **Risk Level** | Low (enhances existing teach dispatcher) | -| **Dependencies** | Claude Code CLI 2.1.12+, Scholar plugin, yq 4.0+ | -| **Target Users** | Academic instructors (ADHD-friendly required) | -| **Branch Strategy** | feature/teach-scholar-enhancement → dev → main | - ---- - -## Overview - -Comprehensive enhancement of the `teach` dispatcher with: - -1. **Smart Defaults** - Auto-detect week/topic from schedule -2. **Interactive Mode** (`-i`) - Step-by-step wizard for content generation -3. **Revision Workflow** (`--revise`) - Iterate on existing content -4. **Context Integration** (`--context`) - Include course materials as context -5. **Content Customization** - Style presets + individual content flags -6. **Topic/Week Selection** - Explicit `--topic` and `--week` flags with lesson plan support - -The teach dispatcher already has solid infrastructure (config validation, post-generation hooks, git integration). This spec focuses on enhancing the AI generation experience. - ---- - -## User Stories - -### Primary Story: Minimal Friction Generation - -**As an** academic instructor with ADHD -**I want to** generate teaching materials with minimal friction -**So that I** can focus on content quality rather than tooling - -### Acceptance Criteria - -- [ ] `teach slides -w 8` generates slides for week 8's topic -- [ ] `teach slides -i` provides step-by-step interactive wizard with style selection -- [ ] `teach slides --revise FILE` improves existing content -- [ ] `teach slides --style rigorous --no-proof` customizes content -- [ ] Progress indicators show elapsed time during generation -- [ ] Error messages provide actionable recovery steps - -### Secondary Stories - -**Story 2: Content Customization** - -- As an instructor, I want to specify content style (conceptual, computational, rigorous, applied) -- So that generated materials match my teaching approach - -**Story 3: Iterative Refinement** - -- As an instructor reviewing content, I want to refine materials through revision -- So that I get content matching my teaching style - ---- - -## Architecture - -### Component Flow - -``` -┌─────────────────────────────────────────────────────────────────┐ -│ User: teach slides -w 8 --style computational --diagrams │ -└─────────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────┐ -│ teach-dispatcher.zsh │ -│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ -│ │ _teach_parse_ │ │ _teach_resolve_ │ │ _teach_inter_ │ │ -│ │ args() │→│ content() │→│ active() │ │ -│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ -│ │ │ │ │ -│ ▼ ▼ ▼ │ -│ ┌─────────────────────────────────────────────────────────────┐ │ -│ │ _teach_scholar_wrapper() [ENHANCED] │ │ -│ │ - Flag validation (topic/week, style, content flags) │ │ -│ │ - Conflict detection │ │ -│ │ - Lesson plan loading │ │ -│ │ - Build Scholar command with content instructions │ │ -│ └─────────────────────────────────────────────────────────────┘ │ -│ │ │ -│ ▼ │ -│ ┌─────────────────────────────────────────────────────────────┐ │ -│ │ _teach_execute() [ENHANCED] │ │ -│ │ - Progress indicator with elapsed time │ │ -│ │ - Timeout handling (120s default) │ │ -│ │ - Structured error messages │ │ -│ └─────────────────────────────────────────────────────────────┘ │ -└─────────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────┐ -│ Claude Code CLI │ -│ claude -p "/scholar:teaching:slides 'Regression' ..." \ │ -│ --output-format text --max-budget-usd 0.50 │ -└─────────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────┐ -│ Post-Generation Hooks [EXISTING] │ -│ - Auto-stage files │ -│ - Update .STATUS │ -│ - Interactive commit workflow │ -└─────────────────────────────────────────────────────────────────┘ -``` - ---- - -## API Design - -### New Flags Summary - -| Flag | Short | Purpose | Example | -| --------------- | ----- | ---------------------------------------- | ------------------------------------- | -| `--topic` | `-t` | Explicit topic (bypasses lesson plan) | `teach slides --topic "Regression"` | -| `--week` | `-w` | Week number (uses lesson plan if exists) | `teach slides -w 8` | -| `--style` | | Content style preset | `teach slides -w 8 --style rigorous` | -| `--interactive` | `-i` | Step-by-step wizard | `teach slides -i` | -| `--revise` | | Improve existing file | `teach slides --revise slides/w8.qmd` | -| `--context` | | Include course context | `teach exam "Midterm" --context` | - -### Content Flags (9 total) - -| Flag | Short | Negation | Description | -| --------------------- | ----- | ------------------------ | ------------------------------------- | -| `--explanation` | `-e` | `--no-explanation` | Conceptual explanations | -| `--proof` | | `--no-proof` | Mathematical proofs | -| `--math` | `-m` | `--no-math` | Formal math notation | -| `--examples` | `-x` | `--no-examples` | Worked numerical examples | -| `--code` | `-c` | `--no-code` | Code demonstrations (R/Python) | -| `--diagrams` | `-d` | `--no-diagrams` | Visual diagrams/plots (always opt-in) | -| `--practice-problems` | `-p` | `--no-practice-problems` | Practice exercises | -| `--definitions` | | `--no-definitions` | Formal definitions | -| `--references` | `-r` | `--no-references` | Citations (always opt-in) | - -### Style Presets (4 total) - -| Preset | Includes | Use Case | -| ----------------- | ---------------------------------------------- | -------------------------------------- | -| **conceptual** | explanation, definitions, examples | Intuition-focused, theory introduction | -| **computational** | explanation, examples, code, practice-problems | Hands-on, lab-style | -| **rigorous** | definitions, explanation, math, proof | Graduate level, formal treatment | -| **applied** | explanation, examples, code, practice-problems | Real-world applications | - -**Notes:** - -- `diagrams` and `references` are always opt-in (never preset-included) -- Use `--no-*` to remove from preset: `--style rigorous --no-proof` - -### Command Syntax Examples - -```bash -# Topic/Week selection (must specify one) -teach slides --topic "Linear Regression" -teach slides -w 8 - -# Style presets -teach slides -w 8 --style computational -teach slides -w 8 --style rigorous --no-proof - -# Individual content flags -teach slides -w 8 --explanation --examples --code -teach slides -w 8 --style conceptual --diagrams # Add to preset - -# Interactive mode -teach slides -i -w 8 # Prompts for style -teach slides -i -w 8 --style applied # Skips style prompt - -# Revision mode -teach slides --revise slides/week08.qmd - -# Context mode -teach exam "Final" --context -``` - ---- - -## Lesson Plan Integration - -### Location - -`.flow/lesson-plans/week-{NN}.yml` - -### Schema (Standard) - -```yaml -# .flow/lesson-plans/week-08.yml -week: 8 -topic: "Multiple Regression" -style: computational # Default preset for this week - -# Learning objectives (required) -objectives: - - "Understand multiple regression model assumptions" - - "Interpret regression coefficients correctly" - - "Perform model diagnostics in R" - -# Subtopics (required) -subtopics: - - "Model specification" - - "Coefficient interpretation" - - "Multicollinearity" - - "Model diagnostics" - -# Key concepts (required) -key_concepts: - - "Partial regression coefficients" - - "Adjusted R-squared" - - "VIF (Variance Inflation Factor)" - -# Prerequisites (optional) -prerequisites: - - "Simple linear regression (Week 6)" - - "Matrix notation basics (Week 7)" -``` - -### Fallback (From Config) - -When no lesson plan file exists, fall back to `semester_info.weeks`: - -```yaml -# In teach-config.yml -semester_info: - weeks: - - number: 8 - start_date: "2026-03-02" - topic: "Multiple Regression" # Used as fallback -``` - -### Flag Interaction Rules - -1. **Must specify one:** Either `--topic` or `--week` is required -2. **--topic takes precedence:** If both given, `--week` is ignored -3. **--topic bypasses lesson plan:** Explicit topic means no lesson plan lookup -4. **--week with lesson plan:** Merges plan defaults with command flags -5. **--week without plan:** Prompts interactively "Continue with topic 'X'? [Y/n]" - ---- - -## UI/UX Specifications - -### Progress Indicator - -``` -⠋ Generating slides (~30s)... 12s -⠙ Generating slides (~30s)... 13s -``` - -### Interactive Style Selection (in `-i` mode) - -``` -📚 Content Style - -What style should this content use? - - [1] conceptual Explanation + definitions + examples - [2] computational Explanation + examples + code + practice - [3] rigorous Definitions + explanation + math + proofs - [4] applied Explanation + examples + code + practice - -Your choice [1-4]: _ -``` - -### Missing Lesson Plan Prompt - -``` -⚠️ No lesson plan found for Week 8 - -Topic from config: "Multiple Regression" - -Continue with this topic? [Y/n]: _ - -Hint: Create a lesson plan with: teach plan create 8 -``` - -### Flag Conflict Error - -``` -✗ teach: Conflicting flags - - Both --proof and --no-proof specified - - These flags are mutually exclusive. - -Fix: - teach slides -w 8 --style rigorous --no-proof - ^^^^^^^^^ keep one -``` - -### Revision Menu - -``` -📝 Revise: slides/week08.qmd - -What would you like to improve? - - [1] Expand content Add more detail - [2] Add examples Include practical examples - [3] Simplify language Make more accessible - [4] Add visuals Suggest images/diagrams - [5] Custom instructions Enter specific feedback - [6] Full regenerate Start fresh - -Your choice [1-6]: _ -``` - -### Success Message - -``` -✓ Created: slides/week08-regression.qmd - - File: slides/week08-regression.qmd - Slides: 24 slides (6 sections) - Style: computational - Format: Quarto RevealJS - -Next steps: - Review: teach slides --revise slides/week08-regression.qmd - Preview: qu preview slides/week08-regression.qmd - -Commit this content? [1] Review [2] Commit [3] Skip: _ -``` - ---- - -## Implementation Plan - -### Phase 1: Flag Infrastructure (3h) - -- [ ] Add new flags to `TEACH_*_FLAGS` arrays -- [ ] Implement `_teach_validate_content_flags()` with conflict detection -- [ ] Implement `_teach_parse_topic_week()` for topic/week extraction -- [ ] Add tests for flag parsing and validation - -### Phase 2: Preset System (2h) - -- [ ] Define preset content maps (conceptual, computational, rigorous, applied) -- [ ] Implement `_teach_resolve_content()` - merge preset + overrides -- [ ] Add `--style` flag handling in `_teach_scholar_wrapper()` -- [ ] Tests for content resolution - -### Phase 3: Lesson Plan Integration (3h) - -- [ ] Implement `_teach_load_lesson_plan()` - load and parse YAML -- [ ] Create lesson plan schema validation -- [ ] Implement missing plan prompt workflow -- [ ] Implement `_teach_lookup_topic()` from semester_info.weeks -- [ ] Tests for lesson plan loading - -### Phase 4: Interactive Mode (4h) - -- [ ] Implement `-i` flag parsing -- [ ] Implement `_teach_interactive_wizard()` with style selection -- [ ] Keyboard navigation (q, b, ?, Enter, numbers) -- [ ] Topic selection from schedule (when no --topic/--week given) -- [ ] Interactive tests - -### Phase 5: Revision Workflow (4h) - -- [ ] Implement `--revise` flag parsing -- [ ] Implement `_teach_analyze_file()` - detect existing content type -- [ ] Implement `_teach_revise_workflow()` - revision menu -- [ ] Diff preview functionality -- [ ] Tests for revision workflow - -### Phase 6: Context & Polish (4h) - -- [ ] Implement `--context` flag parsing -- [ ] Implement `_teach_build_context()` - gather context files -- [ ] Enhanced progress indicator with elapsed time -- [ ] Update help system with all new flags -- [ ] Integration tests for full workflows -- [ ] Documentation updates - ---- - -## Testing Strategy - -### Unit Tests - -```bash -# Flag parsing -test_teach_parse_topic_week() -test_teach_validate_content_flags() -test_teach_detect_conflicts() - -# Content resolution -test_teach_resolve_content_conceptual() -test_teach_resolve_content_with_additions() -test_teach_resolve_content_with_removals() - -# Lesson plan -test_teach_load_lesson_plan() -test_teach_missing_lesson_plan() -test_teach_week_topic_override() -``` - -### Integration Tests - -```bash -# End-to-end -test_teach_slides_with_week() -test_teach_slides_with_topic() -test_teach_slides_style_override() -test_teach_slides_conflict_error() -test_teach_slides_interactive_mode() -test_teach_slides_revision_workflow() -``` - ---- - -## Dependencies - -| Dependency | Version | Purpose | -| --------------- | -------- | ----------------------------- | -| Claude Code CLI | 2.1.12+ | AI generation via `claude -p` | -| Scholar plugin | 2.3.0+ | Teaching commands | -| yq | 4.0+ | YAML parsing | -| fzf | Optional | Interactive selection | - ---- - -## Review Checklist - -- [ ] Backward compatible with existing teach commands -- [ ] All new flags documented in help -- [ ] Progress indicators work in all terminal types -- [ ] Error messages tested for all failure modes -- [ ] Interactive mode keyboard navigation complete -- [ ] Config schema validated -- [ ] Tests added for new functions -- [ ] Documentation updated - ---- - -## Supersedes - -This spec supersedes and merges: - -- `SPEC-teaching-integration-2026-01-17.md` -- `SPEC-teaching-flags-enhancement-2026-01-17.md` - -These files should be archived after this spec is approved. - ---- - -## History - -| Date | Change | Author | -| ---------- | ------------------------------------------------------- | ----------- | -| 2026-01-17 | Merged from teaching-integration + teaching-flags specs | Claude + DT | - ---- - -## Related Documents - -- [Main Plugin Integration Spec](SPEC-claude-code-plugin-integration-2026-01-17.md) -- [Teach Dispatcher Reference](../reference/MASTER-DISPATCHER-GUIDE.md#teach-dispatcher) -- [Teach Config Dates Schema](../reference/MASTER-API-REFERENCE.md#config-validation) diff --git a/docs/specs/SPEC-teaching-git-integration-2026-01-16.md b/docs/specs/SPEC-teaching-git-integration-2026-01-16.md deleted file mode 100644 index 65b7514d4..000000000 --- a/docs/specs/SPEC-teaching-git-integration-2026-01-16.md +++ /dev/null @@ -1,728 +0,0 @@ -# Implementation Spec: Teaching + Git Integration Enhancement - -**Status:** draft -**Created:** 2026-01-16 -**From Brainstorm:** BRAINSTORM-teaching-git-integration-2026-01-16.md (project root) -**Target Release:** v5.11.0 -**Effort Estimate:** 11-16 hours (5 phases) -**Priority:** High -**Worktree:** `~/.git-worktrees/flow-cli/teaching-git-integration` -**Branch:** `feature/teaching-git-integration` - ---- - -## Overview - -Enhance flow-cli's teaching dispatcher with seamless git integration featuring smart automation, safety checks, and PR-based deployment workflows. Transform the teaching content creation experience from manual git operations to a streamlined "generate → review → commit → deploy" flow that preserves user control while eliminating friction. - -**Key Value Proposition:** Reduce 5 manual git steps per content generation to 0, while maintaining safety through interactive confirmations. Enable git novices and empower experts. - ---- - -## Primary User Story - -**As a course instructor creating teaching materials,** -**I want** teaching commands to handle git operations intelligently with interactive prompts, -**So that** I can focus on content creation without manual commit/push context switching, -**And** my course repository maintains a clean PR-based workflow for production deployments. - -**Acceptance Criteria:** -1. After generating content (teach exam/quiz/slides), I can review and commit in < 30 seconds via interactive prompt -2. teach deploy creates a PR from draft → production branch (never direct push) -3. teach status shows uncommitted teaching files and offers to commit/stash them -4. Teaching mode can be enabled for auto-commit (opt-in, with push confirmations) -5. teach init sets up git structure (draft/main branches) automatically - ---- - -## Secondary User Stories - -### User Story 2: Git Novice Onboarding - -**As a git novice starting my first course repository,** -**I want** teach init to set up branching structure automatically, -**So that** I don't need to learn git commands to have a professional workflow. - -**Acceptance Criteria:** -- teach init offers git setup wizard (detect existing repo or create new) -- Creates draft/main branches with appropriate .gitignore -- Optionally creates GitHub repo via gh CLI -- Initial commit with course structure - -### User Story 3: Collaborative Course Development - -**As a TA collaborating on a shared course repository,** -**I want** conflict detection before deployment, -**So that** I don't accidentally overwrite my instructor's changes. - -**Acceptance Criteria:** -- teach deploy checks if production branch has new commits -- If conflicts detected, prompts to rebase before creating PR -- Shows clear instructions for resolving conflicts -- PR workflow enables code review before production - ---- - -## Technical Requirements - -### Architecture - -#### Component Diagram - -```mermaid -graph TB - subgraph "Teaching Commands" - TE[teach exam/quiz/slides] - TS[teach status] - TD[teach deploy] - TI[teach init] - end - - subgraph "Git Integration Layer (NEW)" - GH[lib/git-helpers.zsh] - PC[Post-commit Hook] - BS[Branch Strategy] - PR[PR Creation] - end - - subgraph "Existing Infrastructure" - CFG[teach-config.yml] - VAL[config-validator.zsh] - GD[g-dispatcher.zsh] - end - - TE -->|1. Generate content| PC - PC -->|2. Interactive prompt| GH - GH -->|3. Commit/Push| BS - - TS -->|Query git status| GH - GH -->|Filter teaching files| TS - - TD -->|Pre-flight checks| GH - GH -->|Create PR| PR - PR -->|Use gh CLI| TD - - TI -->|Setup wizard| GH - GH -->|Initialize repo| BS - - GH -->|Read config| CFG - CFG -->|Validate| VAL - - GD -.->|Orthogonal| GH - - classDef new fill:#e1f5e1 - classDef existing fill:#e1e5f5 - class GH,PC,BS,PR new - class CFG,VAL,GD existing -``` - -#### Data Flow: Post-Generation Workflow - -```mermaid -sequenceDiagram - actor User - participant Teach as teach exam - participant Hook as Post-Gen Hook - participant Git as git-helpers.zsh - participant Editor - - User->>Teach: teach exam "Topic" --questions 20 - Teach->>Teach: Generate exam01.qmd - Teach->>Hook: Trigger post-generation - Hook->>Git: git add exam01.qmd - Git-->>Hook: ✓ Staged - - Hook->>User: 📝 Review and commit?
(Review/Commit/Skip) - - alt Review in editor - User->>Editor: Select "Review" - Editor->>User: Opens exam01.qmd - User->>Editor: Makes edits - Editor-->>Hook: Editor closed - Hook->>User: Ready to commit? - end - - alt Commit now - User->>Hook: Select "Commit" - Hook->>Git: Generate commit message - Git->>Git: teach: add exam01 for Topic
Course: STAT 545 - Git->>Git: git commit -m "..." - Git-->>Hook: ✓ Committed - - Hook->>User: Push to remote? [y/N] - - alt Push - User->>Git: y - Git->>Git: git push origin draft - Git-->>User: ✓ Pushed - else Don't push - User->>Hook: n - Hook-->>User: ✓ Committed locally - end - end - - alt Skip - User->>Hook: Select "Skip" - Hook-->>User: Manual commit later - end -``` - ---- - -### API Design - -#### New Module: lib/git-helpers.zsh - -| Function | Parameters | Returns | Description | -|----------|-----------|---------|-------------| -| `_git_teaching_commit_message` | type, topic, course | string | Generate conventional commit message | -| `_git_is_clean` | - | 0/1 | Check if working tree is clean | -| `_git_is_synced` | - | 0/1 | Check if remote is up-to-date | -| `_git_teaching_files` | - | array | List uncommitted teaching files | -| `_git_create_deploy_pr` | title, body | 0/1 | Create PR via gh CLI | -| `_git_detect_conflicts` | - | 0/1 | Check if production has new commits | -| `_git_interactive_commit` | files | 0/1 | Prompt user for commit workflow | -| `_git_setup_teaching_repo` | draft_branch, prod_branch | 0/1 | Initialize repo with branches | - -#### teach-config.yml Schema Updates - -```yaml -# NEW: Git configuration section -git: - draft_branch: draft # Branch for content development - production_branch: main # Branch for site deployment - auto_pr: true # Auto-create PR on teach deploy - require_clean: true # Abort if uncommitted changes - -# NEW: Workflow configuration section -workflow: - teaching_mode: false # Enable streamlined workflow - auto_commit: false # Auto-commit after generation - auto_push: false # Auto-push commits (not recommended) -``` - -#### Command Enhancements - -| Command | New Behavior | Flags | -|---------|-------------|-------| -| `teach exam/quiz/slides/...` | Interactive commit after generation | (existing flags preserved) | -| `teach status` | Show git status + cleanup prompts | `--git-only` - show only git info | -| `teach deploy` | Create PR, conflict detection | `--direct-push` - bypass PR (advanced) | -| `teach init` | Git setup wizard | `--no-git` - skip git setup | - ---- - -### Data Models - -#### Config Schema Additions - -```json -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "properties": { - "git": { - "type": "object", - "properties": { - "draft_branch": { - "type": "string", - "default": "draft", - "pattern": "^[a-zA-Z0-9_-]+$", - "description": "Branch for content development" - }, - "production_branch": { - "type": "string", - "default": "main", - "pattern": "^[a-zA-Z0-9_-]+$", - "description": "Branch for site deployment" - }, - "auto_pr": { - "type": "boolean", - "default": true - }, - "require_clean": { - "type": "boolean", - "default": true - } - } - }, - "workflow": { - "type": "object", - "properties": { - "teaching_mode": { - "type": "boolean", - "default": false - }, - "auto_commit": { - "type": "boolean", - "default": false - }, - "auto_push": { - "type": "boolean", - "default": false - } - } - } - } -} -``` - -#### Commit Message Template - -``` -teach: for - -Generated via: teach "" -Course: ( ) - - - -Co-Authored-By: Scholar -``` - -**Examples:** - -``` -teach: add exam01 for Hypothesis Testing - -Generated via: teach exam "Hypothesis Testing" --questions 20 -Course: STAT 545 (Fall 2024) - -Co-Authored-By: Scholar -``` - -``` -teach: update slides for Week 3 - -Generated via: teach slides "Regression Models" --theme academic -Course: STAT 440 (Spring 2025) - -Modified existing slides to include new examples. - -Co-Authored-By: Scholar -``` - ---- - -### Dependencies - -#### External Tools - -| Tool | Version | Required | Purpose | -|------|---------|----------|---------| -| git | ≥ 2.25 | Yes | Version control | -| gh | ≥ 2.0 | Optional | PR creation (teach deploy) | -| fzf | Latest | No | Enhanced interactive prompts (future) | - -#### Internal Dependencies - -| Module | Dependency Type | Reason | -|--------|----------------|--------| -| `lib/config-validator.zsh` | Required | Validate git/workflow config | -| `lib/core.zsh` | Required | Logging, error handling | -| `lib/dispatchers/teach-dispatcher.zsh` | Modified | Add git integration hooks | -| `commands/teach-init.zsh` | Modified | Add git setup wizard | - ---- - -## UI/UX Specifications - -### User Flow: Post-Generation Commit - -``` -┌─────────────────────────────────────────────────────────────┐ -│ teach exam "Hypothesis Testing" --questions 20 │ -└─────────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ ⏳ Generating exam with Scholar... │ -│ [████████████████████ ] 75% │ -└─────────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ ✅ Generated: exams/exam01.qmd │ -│ │ -│ 📝 Next steps: │ -│ 1. Review content (opens in $EDITOR) │ -│ 2. Commit to git │ -│ │ -│ AskUserQuestion: │ -│ question: "Review and commit this content?" │ -│ header: "Next" │ -│ options: │ -│ ○ Review in editor first (Recommended) │ -│ ○ Commit now with auto-generated message │ -│ ○ Skip commit (I'll do it manually) │ -└─────────────────────────────────────────────────────────────┘ - ↓ - [User selects option] - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ If "Review in editor": │ -│ → Opens nvim exams/exam01.qmd (blocking) │ -│ → After editor closes: │ -│ "Ready to commit? [Y/n]" │ -│ │ -│ If "Commit now": │ -│ → Generates commit message │ -│ → git commit -m "teach: add exam01 for Hypothesis..." │ -│ → "Push to remote? [y/N]" │ -│ │ -│ If "Skip": │ -│ → "✓ File staged. Commit manually when ready." │ -└─────────────────────────────────────────────────────────────┘ -``` - -### Wireframe: Enhanced teach status - -``` -┌─────────────────────────────────────────────────────────────┐ -│ 📚 Teaching Project Status │ -├─────────────────────────────────────────────────────────────┤ -│ │ -│ Course: STAT 545 - Data Science │ -│ Semester: Fall 2024 │ -│ Branch: draft │ -│ Remote: ✓ Up-to-date with origin/draft │ -│ Mode: 🎓 Teaching mode enabled │ -│ │ -├─────────────────────────────────────────────────────────────┤ -│ 📊 Course Progress │ -├─────────────────────────────────────────────────────────────┤ -│ │ -│ Current Week: 3 of 15 │ -│ Next Class: Jan 22, 2026 (2 days) │ -│ │ -│ Content Status: │ -│ Lectures: 3/15 complete │ -│ Assignments: 2/5 released │ -│ Exams: 0/3 scheduled │ -│ │ -├─────────────────────────────────────────────────────────────┤ -│ 🔧 Git Status │ -├─────────────────────────────────────────────────────────────┤ -│ │ -│ ⚠️ 3 uncommitted changes (teaching content) │ -│ │ -│ M exams/exam01.qmd │ -│ A slides/week03-slides.qmd │ -│ M teach-config.yml │ -│ │ -│ AskUserQuestion: │ -│ question: "Clean up uncommitted changes?" │ -│ header: "Action" │ -│ options: │ -│ ○ Commit teaching files (Recommended) │ -│ ○ Stash teaching files │ -│ ○ View diff first │ -│ ○ Leave as-is │ -│ │ -└─────────────────────────────────────────────────────────────┘ -``` - -### ASCII Wireframe: teach deploy PR Flow - -``` -┌─────────────────────────────────────────────────────────────┐ -│ teach deploy │ -└─────────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ 🔍 Pre-flight Checks │ -├─────────────────────────────────────────────────────────────┤ -│ │ -│ ✓ On draft branch (draft) │ -│ ✓ No uncommitted changes │ -│ ✓ Remote is up-to-date │ -│ ⚠️ Production (main) has 2 new commits │ -│ │ -│ AskUserQuestion: │ -│ question: "Production branch has updates. Rebase first?" │ -│ header: "Conflict" │ -│ options: │ -│ ○ Yes - Rebase draft onto main (Recommended) │ -│ ○ No - Continue anyway (may have conflicts) │ -│ ○ Cancel deployment │ -└─────────────────────────────────────────────────────────────┘ - ↓ - [User selects "Yes"] - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ 🔄 Rebasing draft onto origin/main... │ -│ │ -│ Fetching latest from origin... │ -│ Applying 5 commits from draft... │ -│ ✓ Rebase successful (no conflicts) │ -└─────────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ 🏗️ Building Site │ -├─────────────────────────────────────────────────────────────┤ -│ │ -│ Running: quarto render │ -│ [████████████████████████████████████] 100% │ -│ │ -│ ✓ Build successful │ -│ Output: _site/ (42 files) │ -└─────────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ 📋 Creating Pull Request │ -├─────────────────────────────────────────────────────────────┤ -│ │ -│ Title: Deploy: Week 3 Content Updates │ -│ │ -│ Body: │ -│ ## Changes │ -│ - Add exam01 for Hypothesis Testing │ -│ - Add slides for Week 3 Regression Models │ -│ - Update syllabus schedule │ -│ │ -│ ## Commits (5) │ -│ - teach: add exam01 for Hypothesis Testing │ -│ - teach: add slides for Regression Models │ -│ - teach: update syllabus with Week 3 dates │ -│ - fix: typo in assignment 2 instructions │ -│ - docs: update office hours in contact page │ -│ │ -│ ## Build Status │ -│ ✓ Site builds successfully (42 files) │ -│ │ -│ Generated via: teach deploy │ -│ │ -│ AskUserQuestion: │ -│ question: "Create PR and push?" │ -│ header: "Deploy" │ -│ options: │ -│ ○ Yes - Create PR (Recommended) │ -│ ○ Push to draft only (no PR) │ -│ ○ Cancel │ -└─────────────────────────────────────────────────────────────┘ - ↓ - [User selects "Yes"] - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ ✅ Pull Request Created │ -├─────────────────────────────────────────────────────────────┤ -│ │ -│ PR #42: Deploy: Week 3 Content Updates │ -│ https://github.com/user/stat545/pull/42 │ -│ │ -│ Status: ● Open │ -│ Labels: teaching, deploy │ -│ Reviewers: (none assigned) │ -│ │ -│ Next steps: │ -│ 1. Review PR on GitHub │ -│ 2. Merge when ready │ -│ 3. Site will auto-deploy after merge │ -│ │ -└─────────────────────────────────────────────────────────────┘ -``` - -### Accessibility Checklist - -- [x] Interactive prompts use AskUserQuestion (keyboard accessible) -- [x] Clear option labels with descriptions -- [x] Status indicators use both emoji + text (not emoji-only) -- [x] Error messages include actionable next steps -- [x] Success messages confirm what was done -- [x] Color not sole indicator (uses ✓, ⚠️, ✗ symbols) -- [x] Verbose output available via --verbose flag - ---- - -## Open Questions - -1. **Should teaching mode be enabled by default in new projects?** - - **Pro:** Best UX for most users, guides toward good practice - - **Con:** Unexpected auto-commits might surprise users - - **Current Recommendation:** Default off, prompt during teach init - - **Decision needed:** User research with 5 instructors - -2. **How to handle merge conflicts during teach deploy?** - - **Option A:** Show git instructions, abort - - **Option B:** Offer interactive conflict resolution - - **Option C:** Auto-rebase, abort if conflicts - - **Current Recommendation:** Option C (safest) - - **Decision needed:** Test with real conflicts - -3. **Should teach commands work in non-git repos?** - - **Pro:** Lower barrier to entry, works everywhere - - **Con:** Missing git benefits - - **Current Recommendation:** Work without git, prompt to enable - - **Decision needed:** None (implement as recommended) - -4. **Co-authored-by tag for Scholar?** - - **Pro:** Transparency, AI credit, accurate contribution history - - **Con:** Extra line in commit messages - - **Current Recommendation:** Yes, always include - - **Decision needed:** None (implement as recommended) - ---- - -## Review Checklist - -### Design Review - -- [x] All user stories have acceptance criteria -- [x] Architecture diagram shows component relationships -- [x] Data flow diagrams illustrate key workflows -- [x] API design is consistent with existing conventions -- [x] Config schema validated against JSON Schema spec -- [x] UI/UX wireframes for all interactive prompts - -### Security Review - -- [ ] Git operations never expose credentials -- [ ] teach commands validate branch names (prevent injection) -- [ ] PR creation uses gh CLI (audited tool) -- [ ] No auto-push without confirmation (prevent data loss) -- [ ] teach deploy requires clean state (prevent accidental commits) -- [ ] Config validation prevents malicious YAML - -### Performance Review - -- [ ] Git operations are non-blocking where possible -- [ ] teach status caches git status (< 100ms) -- [ ] PR creation is asynchronous (doesn't block terminal) -- [ ] teach deploy pre-flight checks are parallelized - -### Testing Review - -- [ ] Test suite covers all 5 phases -- [ ] Mock git operations in tests (no real repos) -- [ ] Test interactive prompts with canned responses -- [ ] Test conflict scenarios -- [ ] Test non-git repo behavior -- [ ] Test teaching mode on/off - -### Documentation Review - -- [ ] TEACHING-GIT-WORKFLOW.md guide created -- [ ] Tutorial 14 updated with git integration examples -- [ ] DISPATCHER-REFERENCE.md updated for teach commands -- [ ] Migration guide for v5.10.0 → v5.11.0 users -- [ ] FAQ section for common git issues -- [ ] Video walkthrough of PR workflow - -### Accessibility Review - -- [x] All interactive prompts use AskUserQuestion -- [x] Clear option labels with descriptions -- [x] Status symbols not emoji-only -- [x] Error messages actionable -- [ ] Tested with screen reader (manual test needed) - ---- - -## Implementation Notes - -### Critical Path - -1. **Phase 1 (Quick Win)** must be completed first - - Foundation for all other phases - - Tests interactive commit workflow - - Can ship independently - -2. **Phase 2 (PR workflow)** depends on Phase 1 - - Uses git-helpers.zsh functions - - Requires teach deploy to be stable - -3. **Phase 3 (teach status)** is independent - - Can be developed in parallel with Phase 1 - - No dependencies on other phases - -4. **Phase 4 (teaching mode)** depends on Phases 1-3 - - Consolidates all git integration - - Requires config validation - -5. **Phase 5 (teach init)** depends on Phase 1 - - Uses git setup functions from Phase 1 - - Can be developed early - -### Testing Strategy - -**Unit Tests:** -- git-helpers.zsh: Test each function with mocked git -- config-validator.zsh: Test git/workflow schema validation -- teach-dispatcher.zsh: Test post-generation hooks - -**Integration Tests:** -- Full workflow: teach exam → review → commit → push -- teach deploy: PR creation flow -- teach status: Git status display + cleanup - -**Manual Tests:** -- Test with real course repository -- Test conflict scenarios (manual conflicts) -- Test with gh CLI authentication -- Test non-git repo behavior - -### Migration Guide (v5.10.0 → v5.11.0) - -**For existing users:** - -1. **No breaking changes** - All v5.10.0 commands work unchanged - -2. **Enable git integration** (optional): - - ```yaml - # Add to teach-config.yml - git: - draft_branch: draft - production_branch: main - auto_pr: true - require_clean: true - ``` - -3. **Enable teaching mode** (optional): - - ```yaml - # Add to teach-config.yml - workflow: - teaching_mode: true - auto_commit: true - auto_push: false # Recommended: keep false - ``` - -4. **Install gh CLI** (for teach deploy PR creation): - - ```bash - brew install gh - gh auth login - ``` - -5. **Set up branches** (if not already): - - ```bash - git branch draft - git checkout draft - ``` - -**Rollback plan:** -- Remove git/workflow sections from teach-config.yml -- Commands revert to v5.10.0 behavior (no git integration) -- No data loss (git operations are non-destructive) - ---- - -## History - -### 2026-01-16 - Initial Draft - -- **Created by:** Claude Code (Deep Feature Brainstorm) -- **Reviewed by:** (pending) -- **Changes:** N/A - initial version - -**From Brainstorm Findings:** -- 8 expert questions completed -- User preference: Generate → Review → Commit → Deploy -- All users want: Auto-commit, smart branches, conflict detection, PRs -- Teaching mode desired: Yes, with confirmations - -**Next Steps:** -1. Review spec with flow-cli maintainer -2. Get approval for Phase 1 (Quick Win) -3. Create feature branch for implementation -4. Begin Phase 1 development (Smart post-generation workflow) - diff --git a/docs/specs/SPEC-teaching-prompts-enhancement-2026-01-21.md b/docs/specs/SPEC-teaching-prompts-enhancement-2026-01-21.md deleted file mode 100644 index 0dced6714..000000000 --- a/docs/specs/SPEC-teaching-prompts-enhancement-2026-01-21.md +++ /dev/null @@ -1,368 +0,0 @@ -# SPEC: Teaching Prompts Enhancement System - -**Version:** 1.0.0 -**Status:** Draft -**Created:** 2026-01-21 -**From Brainstorm:** BRAINSTORM-teaching-prompts-enhancement-2026-01-21.md - ---- - -## Overview - -Enhance PR #283's static teaching prompts into an integrated, intelligent content generation system with 3-tier storage, template rendering, and interactive workflows. - ---- - -## Primary User Story - -**As a** statistics instructor using flow-cli -**I want** seamless prompt integration with teach-dispatcher commands -**So that** I can generate course content with one command instead of manual copy-paste - -**Acceptance Criteria:** -- ✅ `teach prompt list` shows available prompts -- ✅ `teach prompt show ` displays prompt with pagination -- ✅ `teach lecture "Topic"` auto-uses lecture-notes.md prompt -- ✅ Prompts auto-fill from teach-config.yml (packages, notation) -- ✅ Generated content validates against prompt requirements - ---- - -## Secondary User Stories - -### Discovery - -**As a** new flow-cli user -**I want** to discover available prompts easily -**So that** I know what content types I can generate - -### Customization - -**As an** instructor with specific pedagogical preferences -**I want** to customize prompts for my course -**So that** generated content matches my teaching style - -### Sharing - -**As a** lead instructor -**I want** to share prompt customizations with TAs -**So that** all sections have consistent teaching materials - ---- - -## Technical Requirements - -### Architecture - -**3-Tier Storage System:** - -``` -Tier 1: Global Defaults (read-only) - lib/templates/teaching/claude-prompts/ - - Shipped with flow-cli releases - - Source of truth for fresh installs - -Tier 2: User Library (editable) - ~/.flow/prompts/ - - User-wide customizations - - Survives flow-cli updates - - Named collections: ~/.flow/libraries/ - -Tier 3: Course-Specific (versioned) - .claude/prompts/*.local.md - - Full copies (isolated per course) - - Committed to git - - Shared with TAs -``` - -**Precedence:** Course → User → Global - ---- - -### API Design - -#### Core Commands (Phase 1) - -```bash -teach prompt list # Show available prompts -teach prompt show # Display prompt (paginated) -teach prompt info # Show metadata -``` - -#### Management Commands (Phase 2) - -```bash -teach prompt edit # Copy to .claude/, open in $EDITOR -teach prompt enhance # Interactive wizard -teach prompt add # Create new prompt -teach prompt promote # Copy .local.md → ~/.flow/ -``` - -#### Advanced Commands (Phase 3) - -```bash -teach prompt library create -teach prompt library use -teach prompt catalog install -teach prompt versions -teach prompt upgrade -``` - ---- - -### Data Models - -#### teach-config.yml Schema - -```yaml -course: - name: string - code: string - r_packages: - core: string[] - diagnostics: string[] - reporting: string[] - notation: - expectation: string - variance: string - style: "macros" | "inline" | "mixed" - pedagogy: - derivation_depth: "heuristic" | "rigorous-with-intuition" | "full-rigor" - practice_problems_count: [number, number] - include_diagnostic_workflow: boolean -``` - -#### Prompt Metadata - -```markdown - -``` - ---- - -### Dependencies - -**Required:** -- yq (YAML parsing) -- git (version control) -- ZSH (shell environment) - -**Optional:** -- Scholar plugin v2.x (enhanced teaching commands) -- Claude Code (AI recipe triggers) - ---- - -## UI/UX Specifications - -### Interactive Enhancement Wizard - -**Flow:** - -``` -teach prompt enhance lecture - ↓ -Step 1/5: R Package Customization - Current: emmeans, lme4, car - Add more? [y/N]: y - Packages: DHARMa, broom - ✓ -Step 2/5: Derivation Depth - 1. Heuristic only - 2. Rigorous-with-intuition ✓ - 3. Full rigor - Choice: 2 - ✓ -Step 3/5: Practice Problems - Current: 4-10 - Change? [y/N]: y - New count: 6-8 - ✓ -Step 4/5: Add Custom Section - Add section? [y/N]: y - Name: Computational Performance - (Opens editor for content) - ✓ -Step 5/5: Save Location - (g) Global ~/.flow/ - (l) Local .claude/ [recommended] - Choice: l - ✓ -Enhanced prompt saved to .claude/prompts/lecture-notes.local.md -``` - ---- - -### Conflict Resolution - -**Flow:** - -``` -teach lecture "ANOVA" - ↓ -⚠️ Prompt exists in both global + local - ↓ -Show diff: - Section: R Packages - Global: emmeans, lme4, car - Local: emmeans, lme4, car, DHARMa, broom - Change: +2 packages - ↓ -Which version? - (l) Local [recommended] - (g) Global - (d) Show full diff - (m) Merge interactively - ↓ -Using local version -``` - ---- - -## Open Questions - -1. **Should prompts validate teach-config.yml exists?** - - Option A: Require teach-config.yml for template rendering - - Option B: Work without it (no variable substitution) - - **Recommendation:** Option B (graceful degradation) - -2. **How to handle prompt upgrades with breaking changes?** - - Option A: Auto-upgrade with migration - - Option B: Notify user, manual upgrade - - **Recommendation:** Option B (explicit user control) - -3. **Should validation block content generation?** - - Option A: Hard block (prevent generation if invalid) - - Option B: Warn only (allow generation, show warnings) - - **Recommendation:** Option B for Phase 1, Option A opt-in for Phase 2 - ---- - -## Review Checklist - -**Architecture:** -- [ ] 3-tier storage implemented -- [ ] Precedence rules working (Course → User → Global) -- [ ] Auto-restore if ~/.flow/prompts/ deleted -- [ ] Full copies per course (not symlinks) - -**Commands:** -- [ ] `teach prompt list` shows all prompts -- [ ] `teach prompt show ` paginates correctly -- [ ] `teach prompt edit ` copies to .claude/ -- [ ] `teach prompt enhance ` wizard works -- [ ] `teach prompt promote ` with backup - -**Template Rendering:** -- [ ] teach-config.yml schema documented -- [ ] Variables render correctly ({{course.*}}) -- [ ] Graceful degradation without teach-config.yml -- [ ] All 3 prompts support template variables - -**Testing:** -- [ ] Unit tests for template rendering -- [ ] Integration tests for conflict resolution -- [ ] Manual testing with real course -- [ ] Validation schema tests - -**Documentation:** -- [ ] README updated with examples -- [ ] teach-dispatcher help updated -- [ ] Migration guide for existing courses -- [ ] Quick start guide - ---- - -## Implementation Notes - -### Phase 1: Quick Wins (1-2 hours) - -**Wave 1: Foundation (20 min)** -- Add versioning headers to prompts -- Create 3 sample outputs - -**Wave 2: teach-dispatcher Integration (30 min)** -- Add `teach prompt list/show` commands -- Update routing and help - -**Wave 3: User Library (15 min)** -- Initialize ~/.flow/prompts/ on first use -- Auto-restore if deleted - -**Wave 4: Documentation (15 min)** -- Enhance README with examples -- Add Quick Start guide - ---- - -### Phase 2: Power Features (3-5 hours) - -**Wave 1: Edit & Promote (1 hour)** -- `teach prompt edit ` -- `teach prompt promote ` -- Conflict resolution - -**Wave 2: Template Rendering (1.5 hours)** -- Create teach-config.yml.template -- Implement rendering engine -- Add template variables to prompts - -**Wave 3: Enhancement Wizard (1.5 hours)** -- Design wizard flow -- Implement step-by-step enhancement -- Save location decision - -**Wave 4: Validation (1 hour)** -- Create validation schemas -- Implement `teach validate ` -- Course-specific schema support - ---- - -### Phase 3: Advanced Systems (8-12 hours) - -**Wave 1: Named Collections (2 hours)** -- ~/.flow/libraries/ structure -- `teach prompt library` commands -- Library selection in teach init - -**Wave 2: New Prompts (6 hours)** -- assignment.md -- exam.md -- syllabus.md -- rubric.md - -**Wave 3: Built-in Catalog (2 hours)** -- CATALOG.yml schema -- `teach prompt browse/install` -- Community prompts - -**Wave 4: Version Management (2 hours)** -- `teach prompt versions/upgrade/diff` -- Migration guide system - ---- - -## History - -| Date | Version | Author | Changes | -|------|---------|--------|---------| -| 2026-01-21 | 1.0.0 | DT | Initial specification from brainstorm | - ---- - -## Related Documents - -- **Brainstorm:** `docs/specs/BRAINSTORM-teaching-prompts-enhancement-2026-01-21.md` -- **Architecture:** `docs/architecture/ARCHITECTURE-prompt-storage-2026-01-21.md` -- **UX Spec:** `docs/specs/UX-SPEC-prompt-management-2026-01-21.md` -- **Implementation Plan:** `docs/planning/FINAL-IMPLEMENTATION-PLAN-2026-01-21.md` -- **Original PR:** PR #283 (teaching prompts) - diff --git a/docs/specs/SPEC-teaching-prompts.md b/docs/specs/SPEC-teaching-prompts.md deleted file mode 100644 index 189d9c3ab..000000000 --- a/docs/specs/SPEC-teaching-prompts.md +++ /dev/null @@ -1,98 +0,0 @@ -# Implementation Instructions - -## Branch: feature/teaching-prompts - -### What This PR Adds - -1. **`lib/templates/teaching/claude-prompts/`** - New directory for Claude Code prompts -2. **`lecture-notes.md`** - Prompt for 20-40 page lecture documents -3. **`revealjs-slides.md`** - Prompt for 25+ slide presentations -4. **`derivations-appendix.md`** - Prompt for mathematical theory appendices -5. **`README.md`** - Documentation for the prompts - -### Testing - -1. **Verify files are in place:** - - ```bash - ls -la lib/templates/teaching/claude-prompts/ - ``` - -2. **Test prompt content validity:** - - Each prompt should be valid Markdown - - Code blocks should have proper syntax highlighting - - Tables should render correctly - -3. **Integration test with teach dispatcher:** - - ```bash - # Verify teach init includes new templates - flow teach init --dry-run - ``` - -### Usage - -These prompts are used in two ways: - -1. **With Scholar Plugin:** - - ```bash - /teaching:lecture "Topic" # Uses lecture-notes.md structure - /teaching:slides "Topic" # Uses revealjs-slides.md structure - ``` - -2. **Standalone Reference:** - - ``` - Claude, create lecture notes following the structure in - lib/templates/teaching/claude-prompts/lecture-notes.md - ``` - -### Integration Points - -1. **teach-dispatcher.zsh:** - - Could add `teach prompt lecture` command to display prompt - - Could add `teach validate-prompt` to check course compliance - -2. **ai-recipes.zsh:** - - Could add `[teach-lecture]`, `[teach-slides]` recipes - - Would reference these prompts - -3. **Scholar Plugin:** - - Prompts complement Scholar's teaching style system - - Users configure `.claude/teaching-style.local.md` for customization - -### Future Work - -After this PR is merged, consider: - -1. **Additional prompts:** - - `assignment.md` - Homework/project assignments - - `exam.md` - Exam generation - - `syllabus.md` - Course syllabus - - `rubric.md` - Grading rubrics - -2. **teach-dispatcher integration:** - - ```bash - flow teach prompt lecture # Display lecture prompt - flow teach prompt slides # Display slides prompt - flow teach prompt list # List available prompts - ``` - -3. **Validation command:** - - ```bash - flow teach validate-style # Validate teaching-style.local.md - ``` - -4. **Template customization:** - - Allow `teach init` to copy prompts to course - - Support course-specific prompt overrides - -### Merge Checklist - -- [ ] All prompts are valid Markdown -- [ ] README accurately describes usage -- [ ] No conflicts with existing templates -- [ ] Compatible with Scholar plugin diff --git a/docs/specs/SPEC-teaching-workflow-v2.md b/docs/specs/SPEC-teaching-workflow-v2.md deleted file mode 100644 index 2f02e8686..000000000 --- a/docs/specs/SPEC-teaching-workflow-v2.md +++ /dev/null @@ -1,1558 +0,0 @@ -# Teaching Feature for flow-cli - Implementation Plan v2.0 - -**Feature:** Teaching workflow with deployment automation and course context -**Status:** Planning - Updated after Deep Brainstorm (2026-01-11) -**Created:** 2026-01-11 (original), updated 2026-01-11 -**Branch Strategy:** feature/teaching-workflow → dev → main - ---- - -## Executive Summary - -Implement a **deployment-focused teaching workflow system** in flow-cli that: -1. **Fast Deployment** (Phase 1) - Typo-to-live in < 2 min with branch safety -2. **Course Context** (Phase 1) - Teaching-aware `work` command with status dashboard -3. **Exam Workflow** (Phase 1.5) - Optional exam generation + conversion (2-3 exams/semester) -4. **Generic Framework** (Phase 2) - `teach init` for future courses - -**Key Innovation:** Teaching-aware `work` command that auto-detects course context, prevents production branch edits, and provides one-command deployment. - -**Major Changes from v1.0:** -- ✅ **Deployment-first** (was assessment-first) -- ✅ **No Canvas API** (manual upload is acceptable) -- ✅ **Scholar skills optional** (Phase 1.5, not Phase 1) -- ✅ **Incremental shipping** (not big-bang 22-hour implementation) -- ✅ **STAT 545 only in Phase 1** (other courses in Phase 2) - ---- - -## Deep Brainstorm Findings - -### User Research Summary (8 expert questions, 2026-01-11) - -| Insight | Impact on Spec | -|---------|----------------| -| **Pain point:** 5-15 min manual git workflow | Priority 1: Fast deployment automation | -| **Frequency:** 2-3 exams/semester (not weekly) | De-prioritize quiz workflow, focus on deployment | -| **Canvas:** Manual upload acceptable | Remove Canvas API integration entirely | -| **Scholar:** Will build alongside flow-cli | Make scholar integration optional Phase 1.5 | -| **Scope:** STAT 545 only in Phase 1 | Remove multi-course support from Phase 1 | -| **Budget:** Flexible, ship incrementally | Break into 3 shippable increments | -| **Definition of Done:** All 4 success criteria selected | Branch safety + deployment + context + exams | -| **Content generation:** Copy-paste from Claude then format | Scholar skills = Phase 1.5 after core workflow works | - -### Revised Success Criteria (Priority Order) - -1. ⚡ **Fast deployment < 2 min** (Priority 1) - Typo → live -2. 🛡️ **Branch safety** (Priority 1) - Workflow guard prevents production edits -3. 📚 **Course context** (Priority 2) - `work stat-545` shows current week, status -4. 📝 **Exam workflow** (Priority 3) - One-command generation + conversion (Optional) - ---- - -## Incremental Shipping Strategy - -### Increment 1: Core Deployment (MVP - 4-6 hours) - -**Goal:** Solve the 5-15 min deployment problem - -**Deliverables:** -- Branch safety guard in `work` command -- `scripts/quick-deploy.sh` template -- Teaching project detection -- Basic `.flow/teach-config.yml` - -**Success:** Typo deployment < 2 min, production branch warning works - -**Ship When:** All tests pass, STAT 545 repo has config + script - ---- - -### Increment 2: Course Context (4-6 hours) - -**Goal:** Teaching-aware terminal experience - -**Deliverables:** -- Enhanced `work` session with course info -- Current week calculation -- Course shortcuts loaded per-session -- Semester archive script - -**Success:** `work stat-545` shows week, shortcuts work, context is clear - -**Ship When:** Course context display tested in STAT 545 - ---- - -### Increment 3: Exam Workflow (8-10 hours) - OPTIONAL - -**Goal:** Optional exam automation (Phase 1.5) - -**Deliverables:** -- `teach-exam` command for guided exam creation -- examark integration -- Scholar skill integration (if ready) -- Exam workflow documentation - -**Success:** End-to-end exam creation in < 30 min - -**Ship When:** At least 1 exam created using the workflow - -**Dependencies:** -- examark installed (`npm install -g examark`) -- Scholar teaching skills available (optional) - ---- - -## Architecture Overview (Revised) - -### Layer Integration - -``` -┌─────────────────────────────────────────────────────────────┐ -│ scholar (Claude Plugin) - OPTIONAL PHASE 1.5 │ -│ /scholar:exam, /scholar:lecture │ -│ → AI-powered content generation │ -└────────────────────┬────────────────────────────────────────┘ - │ (optional) - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ flow-cli (Pure ZSH) ← THIS IMPLEMENTATION │ -│ ├── work stat-545 (teaching-aware session) ← CORE │ -│ ├── Branch safety guard ← CORE │ -│ ├── scripts/quick-deploy.sh ← CORE │ -│ ├── .flow/teach-config.yml ← CORE │ -│ └── teach-exam command ← OPTIONAL (Phase 1.5) │ -└────────────────────┬────────────────────────────────────────┘ - │ - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ GitHub Actions - Auto-deploy on production push │ -│ → Quarto render → GitHub Pages │ -└─────────────────────────────────────────────────────────────┘ -``` - -### Data Flow (Increment 1 - Core Deployment) - -``` -Scenario: Fix typo on website - -1. Developer: - cd ~/projects/teaching/stat-545 - work stat-545 - -2. flow-cli: - → Detects teaching project - → Checks git branch - → ⚠️ WARN if on production branch - → Show course context - → Open editor - -3. Developer fixes typo, saves - -4. Developer: - s545d (shortcut for scripts/quick-deploy.sh) - -5. Script: - → Commit changes - → Merge draft → production - → Push to GitHub - → GitHub Actions deploys - -6. Result: Live in < 2 min -``` - -### Data Flow (Increment 3 - Exam Workflow - OPTIONAL) - -``` -Scenario: Create exam - -1. Developer: - work stat-545 - teach-exam "midterm covering weeks 1-8" - -2. flow-cli: - → Prompts for exam details (duration, points, topics) - → Calls scholar skill (if available) OR opens template - → Saves to exams/midterm-week08.md - -3. Developer edits exam content - -4. Developer: - s545e midterm-week08 (convert exam) - -5. Script: - → Runs examark: md → qti.zip - → Saves to exams/midterm-week08.qti.zip - → Opens Canvas in browser - -6. Developer manually uploads QTI to Canvas -``` - ---- - -## Phase 1: STAT 545 Core Deployment (Increment 1) - -### 1.1: Teaching Project Detection (Enhanced) - -**File:** `lib/project-detector.zsh` - -**Current State:** - -```zsh -# Line 30-33: Already detects teaching projects -if [[ -f "$dir/syllabus.qmd" ]] || [[ -d "$dir/lectures" ]]; then - echo "teaching" - return 0 -fi -``` - -**Enhancement Needed:** - -```zsh -_detect_teaching_enhanced() { - local dir="$1" - - # Enhanced detection - if [[ -f "$dir/syllabus.qmd" ]] || - [[ -d "$dir/lectures" ]] || - [[ -f "$dir/.flow/teach-config.yml" ]]; then - - # Validate config if present - if [[ -f "$dir/.flow/teach-config.yml" ]]; then - if ! _validate_teaching_config "$dir/.flow/teach-config.yml"; then - _flow_log_error "Invalid teaching config: $dir/.flow/teach-config.yml" - return 1 - fi - fi - - echo "teaching" - return 0 - fi - - return 1 -} - -_validate_teaching_config() { - local config="$1" - - # Check required fields - command -v yq &>/dev/null || return 1 - - yq -e '.course.name' "$config" &>/dev/null || return 1 - yq -e '.branches.draft' "$config" &>/dev/null || return 1 - yq -e '.branches.production' "$config" &>/dev/null || return 1 - - return 0 -} -``` - -**Test Cases:** -- [x] Detects existing courses (syllabus.qmd or lectures/ dir) -- [x] Detects new courses (.flow/teach-config.yml) -- [x] Validates config structure -- [x] Returns error for malformed config - ---- - -### 1.2: Teaching Configuration (Simplified) - -**New File:** `.flow/teach-config.yml` (in STAT 545 repo) - -**Minimal config for Increment 1:** - -```yaml -# STAT 545 Teaching Configuration -# Version: 1.0 (Increment 1 - Core Deployment) - -course: - name: "STAT 545" - full_name: "Design of Experiments" - semester: "spring" - year: 2026 - instructor: "DT" - -branches: - draft: "draft" - production: "production" - -deployment: - web: - type: "github-pages" - branch: "production" - url: "https://data-wise.github.io/stat-545" - -automation: - quick_deploy: "scripts/quick-deploy.sh" - -shortcuts: - s545: "work stat-545" - s545d: "./scripts/quick-deploy.sh" -``` - -**Fields for Increment 2 (Course Context):** - -```yaml -# Add to above config for Increment 2 -semester_info: - start_date: "2026-01-13" # ISO 8601 - weeks: 16 - breaks: - - start: "2026-03-10" - end: "2026-03-14" - name: "Spring Break" -``` - -**Fields for Increment 3 (Exam Workflow - OPTIONAL):** - -```yaml -# Add to above config for Increment 3 -scholar: - exam_skill: "/scholar:exam" # If scholar plugin available - lecture_skill: "/scholar:lecture" # Optional - -examark: - enabled: true - exam_dir: "exams" - output_format: "qti" - -shortcuts: - s545e: "./scripts/exam-to-qti.sh" # Exam conversion -``` - ---- - -### 1.3: Enhanced `work` Command (Increment 1 - Branch Safety) - -**File:** `commands/work.zsh` - -**New Function:** `_work_teaching_session()` (Minimal for Increment 1) - -```zsh -_work_teaching_session() { - local project_dir="$1" - local config_file="$project_dir/.flow/teach-config.yml" - - # 1. Validate config exists - if [[ ! -f "$config_file" ]]; then - _flow_log_error "Teaching config not found: $config_file" - _flow_log_info "Run 'teach-init' to create configuration" - return 1 - fi - - # 2. Validate yq available - if ! command -v yq &>/dev/null; then - _flow_log_error "yq is required for teaching workflow" - _flow_log_info "Install: brew install yq" - return 1 - fi - - # 3. Branch safety check - local current_branch=$(git -C "$project_dir" branch --show-current) - local production_branch=$(yq -r '.branches.production' "$config_file") - local draft_branch=$(yq -r '.branches.draft' "$config_file") - - if [[ "$current_branch" == "$production_branch" ]]; then - echo "" - echo "${FLOW_COLORS[header]}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${FLOW_COLORS[reset]}" - echo "${FLOW_COLORS[error]}⚠️ WARNING: You are on PRODUCTION branch${FLOW_COLORS[reset]}" - echo "${FLOW_COLORS[header]}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${FLOW_COLORS[reset]}" - echo "" - echo " ${FLOW_COLORS[bold]}Branch:${FLOW_COLORS[reset]} $production_branch" - echo " ${FLOW_COLORS[error]}Students see this branch!${FLOW_COLORS[reset]}" - echo "" - echo " ${FLOW_COLORS[info]}Recommended: Switch to draft branch for edits${FLOW_COLORS[reset]}" - echo " ${FLOW_COLORS[info]}Draft branch: $draft_branch${FLOW_COLORS[reset]}" - echo "" - echo "${FLOW_COLORS[header]}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${FLOW_COLORS[reset]}" - echo "" - - # Prompt to switch (with timeout for non-interactive contexts) - if [[ -z "$FLOW_TEACHING_ALLOW_PRODUCTION" ]]; then - read -t 30 -q "?Continue on production anyway? [y/N] " continue_anyway - echo "" - - if [[ "$continue_anyway" != "y" ]]; then - _flow_log_info "Switching to draft branch: $draft_branch" - git -C "$project_dir" checkout "$draft_branch" - current_branch="$draft_branch" - fi - fi - fi - - # 4. Load shortcuts for current session - _load_teaching_shortcuts "$config_file" - - # 5. Show minimal context - local course_name=$(yq -r '.course.name' "$config_file") - echo "" - echo "${FLOW_COLORS[bold]}📚 $course_name${FLOW_COLORS[reset]}" - echo " ${FLOW_COLORS[info]}Branch:${FLOW_COLORS[reset]} $current_branch" - echo "" - - # 6. Open editor - cd "$project_dir" - ${EDITOR:-code} . -} - -_load_teaching_shortcuts() { - local config_file="$1" - - # Create aliases for current session - eval "$(yq -r '.shortcuts | to_entries[] | "alias \(.key)=\"\(.value)\""' "$config_file")" - - # Show loaded shortcuts - echo "${FLOW_COLORS[bold]}Shortcuts loaded:${FLOW_COLORS[reset]}" - yq -r '.shortcuts | to_entries[] | " \(.key) → \(.value)"' "$config_file" - echo "" -} -``` - -**Enhancement for Increment 2 (Course Context):** - -```zsh -# Add after step 5 in _work_teaching_session() - -# 5b. Show course context (Increment 2) -_display_teaching_context "$project_dir" "$config_file" - -_display_teaching_context() { - local project_dir="$1" - local config_file="$2" - - local semester=$(yq -r '.course.semester' "$config_file") - local year=$(yq -r '.course.year' "$config_file") - - echo " ${FLOW_COLORS[info]}Semester:${FLOW_COLORS[reset]} $semester $year" - - # Calculate current week - local current_week=$(_calculate_current_week "$config_file") - if [[ -n "$current_week" && "$current_week" != "null" ]]; then - echo " ${FLOW_COLORS[info]}Current Week:${FLOW_COLORS[reset]} Week $current_week" - fi - - # Show recent git activity - local recent_commits=$(git -C "$project_dir" log --oneline -3 --format="%s") - if [[ -n "$recent_commits" ]]; then - echo "" - echo " ${FLOW_COLORS[bold]}Recent Changes:${FLOW_COLORS[reset]}" - echo "$recent_commits" | sed 's/^/ /' - fi - - echo "" -} - -_calculate_current_week() { - local config_file="$1" - - # Read semester start date from config - local start_date=$(yq -r '.semester_info.start_date // empty' "$config_file") - - if [[ -z "$start_date" ]]; then - return 0 - fi - - # Calculate weeks since start (macOS compatible) - local start_epoch=$(date -j -f "%Y-%m-%d" "$start_date" "+%s" 2>/dev/null) - local now_epoch=$(date "+%s") - - if [[ -z "$start_epoch" ]]; then - return 0 - fi - - local days_diff=$(( (now_epoch - start_epoch) / 86400 )) - local week=$(( (days_diff / 7) + 1 )) - - # Cap at 16 weeks - if [[ $week -gt 16 ]]; then - week=16 - fi - - echo "$week" -} -``` - ---- - -### 1.4: Automation Scripts (Templates) - -**New Directory:** `lib/templates/teaching/` (in flow-cli) - -#### Template 1: `quick-deploy.sh` (Increment 1 - CORE) - -```bash -#!/usr/bin/env bash -# Quick Deploy - Single commit to production -# Generated by flow-cli teaching framework - -set -euo pipefail - -# Colors -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# Load course config -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_DIR="$(dirname "$SCRIPT_DIR")" -CONFIG="$PROJECT_DIR/.flow/teach-config.yml" - -if [[ ! -f "$CONFIG" ]]; then - echo -e "${RED}❌ Config not found: $CONFIG${NC}" - exit 1 -fi - -# Validate yq available -if ! command -v yq &>/dev/null; then - echo -e "${RED}❌ yq is required${NC}" - echo -e "${YELLOW}Install: brew install yq${NC}" - exit 1 -fi - -# Read config -DRAFT_BRANCH=$(yq -r '.branches.draft' "$CONFIG") -PRODUCTION_BRANCH=$(yq -r '.branches.production' "$CONFIG") -SITE_URL=$(yq -r '.deployment.web.url' "$CONFIG") - -# Safety check -CURRENT_BRANCH=$(git branch --show-current) -if [[ "$CURRENT_BRANCH" != "$DRAFT_BRANCH" ]]; then - echo -e "${RED}❌ Must be on $DRAFT_BRANCH branch${NC}" - echo -e "${YELLOW}Current branch: $CURRENT_BRANCH${NC}" - echo -e "${BLUE}Run: git checkout $DRAFT_BRANCH${NC}" - exit 1 -fi - -# Check for uncommitted changes -if ! git diff-index --quiet HEAD --; then - echo -e "${YELLOW}⚠️ Uncommitted changes detected${NC}" - read -p "Commit changes first? [Y/n] " commit_first - - if [[ "$commit_first" != "n" ]]; then - read -p "Commit message: " commit_msg - git add . - git commit -m "${commit_msg:-Quick update}" - else - echo -e "${RED}❌ Cannot deploy with uncommitted changes${NC}" - exit 1 - fi -fi - -# Quick deploy -echo "" -echo -e "${BLUE}🚀 Quick Deploy: $DRAFT_BRANCH → $PRODUCTION_BRANCH${NC}" -echo "" - -# Record start time -START_TIME=$(date +%s) - -# Switch to production -git checkout "$PRODUCTION_BRANCH" - -# Merge draft -echo -e "${YELLOW}Merging draft...${NC}" -if ! git merge "$DRAFT_BRANCH" --no-edit; then - echo -e "${RED}❌ Merge conflict detected${NC}" - echo -e "${YELLOW}Resolve conflicts and run again${NC}" - git merge --abort - git checkout "$DRAFT_BRANCH" - exit 1 -fi - -# Push to remote -echo -e "${YELLOW}Pushing to remote...${NC}" -git push origin "$PRODUCTION_BRANCH" - -# Switch back to draft -git checkout "$DRAFT_BRANCH" - -# Calculate duration -END_TIME=$(date +%s) -DURATION=$((END_TIME - START_TIME)) - -echo "" -echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" -echo -e "${GREEN}✅ Deployed to production in ${DURATION}s${NC}" -echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" -echo "" -echo -e "${BLUE}🌐 Site: $SITE_URL${NC}" -echo -e "${YELLOW}⏳ GitHub Actions deploying (usually < 2 min)${NC}" -echo "" -echo -e "${BLUE}💡 Tip: Check deployment status at:${NC}" -echo -e " https://github.com/$(git config --get remote.origin.url | sed 's/.*github.com[:\/]\(.*\)\.git/\1/')/actions" -echo "" -``` - -**Key Features:** -- ✅ Branch safety check -- ✅ Uncommitted changes detection -- ✅ Deployment time tracking -- ✅ Color-coded output -- ✅ Error handling with rollback - -#### Template 2: `semester-archive.sh` (Increment 1 - CORE) - -```bash -#!/usr/bin/env bash -# Semester Archive - Annual transition helper - -set -euo pipefail - -# Load config -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_DIR="$(dirname "$SCRIPT_DIR")" -CONFIG="$PROJECT_DIR/.flow/teach-config.yml" - -if [[ ! -f "$CONFIG" ]]; then - echo "❌ Config not found: $CONFIG" - exit 1 -fi - -SEMESTER=$(yq -r '.course.semester' "$CONFIG") -YEAR=$(yq -r '.course.year' "$CONFIG") -TAG="$SEMESTER-$YEAR-final" - -echo "📋 Semester Archive Tool" -echo "" -echo " Semester: $SEMESTER $YEAR" -echo " Tag: $TAG" -echo "" - -read -p "Create archive tag? [Y/n] " confirm -if [[ "$confirm" == "n" ]]; then - exit 0 -fi - -# Tag production branch -PRODUCTION_BRANCH=$(yq -r '.branches.production' "$CONFIG") -git checkout "$PRODUCTION_BRANCH" -git tag -a "$TAG" -m "$SEMESTER $YEAR - Course Complete" -git push --tags - -echo "" -echo "✅ Archived: $TAG" -echo "📝 Next steps:" -echo " 1. Update .flow/teach-config.yml for next semester" -echo " 2. Update course.year (or course.semester)" -echo " 3. Commit config changes to draft branch" -echo "" -``` - -#### Template 3: `exam-to-qti.sh` (Increment 3 - OPTIONAL) - -```bash -#!/usr/bin/env bash -# Exam Converter - Markdown to Canvas QTI -# Uses examark for conversion -# OPTIONAL: Only needed if using exam workflow - -set -euo pipefail - -EXAM_FILE="$1" - -if [[ -z "$EXAM_FILE" ]]; then - echo "Usage: exam-to-qti.sh " - exit 1 -fi - -if [[ ! -f "$EXAM_FILE" ]]; then - echo "❌ Exam file not found: $EXAM_FILE" - exit 1 -fi - -# Check examark installed -if ! command -v examark &>/dev/null; then - echo "❌ examark not installed" - echo "Install: npm install -g examark" - exit 1 -fi - -echo "📝 Converting exam to Canvas format..." -examark "$EXAM_FILE" -o "${EXAM_FILE%.md}.qti.zip" - -if [[ $? -eq 0 ]]; then - echo "✅ Canvas file ready: ${EXAM_FILE%.md}.qti.zip" - echo "📤 Upload to Canvas manually" - echo "" - echo "💡 Tip: Open Canvas in browser and upload the .qti.zip file" -else - echo "❌ Conversion failed" - exit 1 -fi -``` - ---- - -### 1.5: Migration Command `teach-init` (Increment 1) - -**New File:** `commands/teach-init.zsh` - -**Purpose:** Scaffold teaching workflow in existing or new course repo - -```zsh -teach-init() { - local course_name="$1" - - if [[ -z "$course_name" ]]; then - _flow_log_error "Usage: teach-init " - echo "" - echo "Examples:" - echo " teach-init \"STAT 545\"" - echo " teach-init \"STAT 440\"" - return 1 - fi - - echo "🎓 Initializing teaching workflow for: $course_name" - echo "" - - # Check dependencies - if ! command -v yq &>/dev/null; then - _flow_log_error "yq is required" - echo "Install: brew install yq" - return 1 - fi - - # Detect git state - if [[ -d .git ]]; then - _teach_migrate_existing_repo "$course_name" - else - _teach_create_fresh_repo "$course_name" - fi -} - -_teach_migrate_existing_repo() { - local course_name="$1" - - echo "📋 Detected existing git repository" - echo "" - - # Check current branch - local current_branch=$(git branch --show-current) - echo "Current branch: $current_branch" - echo "" - - # Strategy menu - echo "Choose migration strategy:" - echo " ${FLOW_COLORS[bold]}1.${FLOW_COLORS[reset]} In-place conversion (rename $current_branch → production, create draft)" - echo " ${FLOW_COLORS[bold]}2.${FLOW_COLORS[reset]} Two-branch setup (keep $current_branch, create draft + production)" - echo "" - - read -p "Choice [1/2]: " choice - - case "$choice" in - 1) _teach_inplace_conversion "$course_name" ;; - 2) _teach_two_branch_setup "$course_name" ;; - *) echo "Invalid choice"; return 1 ;; - esac -} - -_teach_inplace_conversion() { - local course_name="$1" - local current_branch=$(git branch --show-current) - - echo "" - echo "⚠️ This will:" - echo " 1. Rename $current_branch → production" - echo " 2. Create new draft branch from production" - echo " 3. Add .flow/teach-config.yml and scripts/" - echo "" - - read -p "Continue? [y/N] " confirm - if [[ "$confirm" != "y" ]]; then - echo "Cancelled" - return 1 - fi - - # Tag current state - local semester=$(date +"%B" | sed 's/January\|February\|March\|April\|May/spring/; s/June\|July/summer/; s/August\|September\|October\|November\|December/fall/') - local year=$(date +%Y) - git tag -a "$semester-$year-pre-migration" -m "Pre-migration snapshot" - - # Rename to production - git branch -m "$current_branch" production - git push -u origin production - - # Create draft from production - git checkout -b draft production - git push -u origin draft - - # Install templates - _teach_install_templates "$course_name" - - echo "" - echo "✅ Migration complete" - _teach_show_next_steps "$course_name" -} - -_teach_two_branch_setup() { - local course_name="$1" - - # Create production and draft branches - git checkout -b production - git push -u origin production - - git checkout -b draft - git push -u origin draft - - # Install templates - _teach_install_templates "$course_name" - - echo "" - echo "✅ Two-branch setup complete" - _teach_show_next_steps "$course_name" -} - -_teach_install_templates() { - local course_name="$1" - - # Create directory structure - mkdir -p .flow scripts - - # Copy script templates from flow-cli - local template_dir="${FLOW_PLUGIN_ROOT}/lib/templates/teaching" - - cp "$template_dir/quick-deploy.sh" scripts/ - cp "$template_dir/semester-archive.sh" scripts/ - chmod +x scripts/*.sh - - # Generate minimal config (Increment 1) - local course_slug=$(echo "$course_name" | tr '[:upper:]' '[:lower:]' | tr ' ' '-') - local semester=$(date +"%B" | sed 's/January\|February\|March\|April\|May/spring/; s/June\|July/summer/; s/August\|September\|October\|November\|December/fall/') - local year=$(date +%Y) - - cat > .flow/teach-config.yml <" - echo "" - echo "Examples:" - echo " teach-exam \"midterm covering weeks 1-8\"" - echo " teach-exam \"final exam\"" - return 1 - fi - - # Detect teaching project - local project_type=$(_flow_detect_project_type .) - if [[ "$project_type" != "teaching" ]]; then - _flow_log_error "Not in a teaching project" - return 1 - fi - - # Load config - local config_file=".flow/teach-config.yml" - if [[ ! -f "$config_file" ]]; then - _flow_log_error "Teaching config not found: $config_file" - return 1 - fi - - # Check examark config - local examark_enabled=$(yq -r '.examark.enabled // false' "$config_file") - if [[ "$examark_enabled" != "true" ]]; then - _flow_log_warning "examark not enabled in config" - echo "To enable: yq -i '.examark.enabled = true' .flow/teach-config.yml" - fi - - # Get exam directory - local exam_dir=$(yq -r '.examark.exam_dir // "exams"' "$config_file") - mkdir -p "$exam_dir" - - echo "📝 Creating exam: $topic" - echo "" - - # Prompt for exam details - echo "Exam details:" - read -p " Duration (minutes): " duration - read -p " Total points: " points - read -p " Filename (without .md): " filename - - if [[ -z "$filename" ]]; then - filename="exam-$(date +%Y%m%d)" - fi - - local exam_file="$exam_dir/$filename.md" - - # Check if scholar available - if command -v claude &>/dev/null && yq -e '.scholar.exam_skill' "$config_file" &>/dev/null; then - local scholar_skill=$(yq -r '.scholar.exam_skill' "$config_file") - - echo "" - echo "🤖 Scholar skill available: $scholar_skill" - read -p "Use scholar to generate exam? [Y/n] " use_scholar - - if [[ "$use_scholar" != "n" ]]; then - echo "" - echo "Launching Claude with scholar skill..." - echo "Prompt: $scholar_skill \"$topic\"" - echo "" - - # Launch Claude in current directory - # User will interact with scholar skill - # Save output to exam file - - _flow_log_info "After generating content, save to: $exam_file" - return 0 - fi - fi - - # Fallback: Create template - _teach_create_exam_template "$exam_file" "$topic" "$duration" "$points" - - echo "" - echo "✅ Exam template created: $exam_file" - echo "" - echo "Next steps:" - echo " 1. Edit exam: \$EDITOR $exam_file" - echo " 2. Convert to Canvas: ./scripts/exam-to-qti.sh $exam_file" - echo " 3. Upload .qti.zip to Canvas manually" -} - -_teach_create_exam_template() { - local exam_file="$1" - local topic="$2" - local duration="$3" - local points="$4" - - cat > "$exam_file" </dev/null && echo "✅" || echo "❌ brew install yq" - -printf "%-15s " "git:" -command -v git &>/dev/null && echo "✅" || echo "❌" - -printf "%-15s " "quarto:" -command -v quarto &>/dev/null && echo "✅" || echo "❌ brew install quarto" - -echo "" -echo "Optional (Exam Workflow):" - -printf "%-15s " "examark:" -command -v examark &>/dev/null && echo "✅" || echo "⚠️ npm install -g examark" - -printf "%-15s " "scholar:" -if command -v claude &>/dev/null; then - echo "⚠️ Check /scholar:exam availability" -else - echo "⚠️ Claude Code not installed" -fi - -echo "" -``` - ---- - -## Testing Strategy - -### Increment 1: Core Deployment - -**Unit Tests:** - -```zsh -# tests/test-teach-init.zsh - -test_teach_init_creates_structure() { - cd /tmp/test-course - git init - git commit --allow-empty -m "Initial" - - teach-init "Test Course" - - assert_directory_exists ".flow" - assert_file_exists ".flow/teach-config.yml" - assert_directory_exists "scripts" - assert_file_executable "scripts/quick-deploy.sh" -} - -test_work_detects_teaching() { - cd /tmp/test-course - local project_type=$(_flow_detect_project_type .) - - assert_equals "teaching" "$project_type" -} - -test_production_branch_warning() { - cd /tmp/test-course - git checkout production - - # Mock FLOW_TEACHING_ALLOW_PRODUCTION to skip prompt - export FLOW_TEACHING_ALLOW_PRODUCTION=1 - - output=$(work test-course 2>&1) - assert_contains "$output" "WARNING: You are on PRODUCTION branch" -} - -test_quick_deploy_validates_branch() { - cd /tmp/test-course - git checkout production - - ./scripts/quick-deploy.sh 2>&1 | grep "Must be on draft branch" - assert_equals 1 $? -} -``` - -**E2E Test (Manual):** - -```bash -# 1. Initialize test course -cd ~/projects/teaching/stat-545-test -teach-init "STAT 545 Test" - -# 2. Verify structure -ls -la .flow/teach-config.yml -ls -la scripts/ - -# 3. Make a change -echo "Test content" >> index.qmd -git add index.qmd -git commit -m "test: Add test content" - -# 4. Deploy -time ./scripts/quick-deploy.sh -# Should complete in < 2 min - -# 5. Verify deployment -curl -s https://data-wise.github.io/stat-545-test | grep "Test content" -``` - -### Increment 2: Course Context - -**Unit Tests:** - -```zsh -test_current_week_calculation() { - # Mock config with known start date - echo "semester_info: - start_date: \"2026-01-13\"" > /tmp/test-config.yml - - # Mock current date (2026-02-03 = 3 weeks after start) - # This test needs date mocking - skip for now or use fixed test date -} - -test_course_context_display() { - cd /tmp/test-course - git checkout draft - - output=$(work test-course 2>&1) - assert_contains "$output" "Semester: spring 2026" - assert_contains "$output" "Current Week:" -} -``` - -### Increment 3: Exam Workflow (Optional) - -**Unit Tests:** - -```zsh -test_teach_exam_creates_template() { - cd /tmp/test-course - - # Mock stdin for prompts - echo -e "60\n100\nmidterm" | teach-exam "midterm covering weeks 1-8" - - assert_file_exists "exams/midterm.md" - assert_contains "$(cat exams/midterm.md)" "Duration: 60 minutes" -} - -test_exam_conversion() { - cd /tmp/test-course - - # Requires examark installed - if ! command -v examark &>/dev/null; then - skip "examark not installed" - fi - - ./scripts/exam-to-qti.sh exams/midterm.md - assert_file_exists "exams/midterm.qti.zip" -} -``` - ---- - -## Implementation Timeline (Revised) - -### Increment 1: Core Deployment (4-6 hours) - -**Week 1, Days 1-2:** - -| Task | Duration | Output | -|------|----------|--------| -| Create `lib/templates/teaching/` with scripts | 2h | 3 template scripts | -| Create `commands/teach-init.zsh` | 2h | Scaffolding command | -| Enhance `lib/project-detector.zsh` | 1h | Teaching detection + validation | -| Enhance `commands/work.zsh` (branch safety) | 1h | Production branch warning | -| Test suite: `tests/test-teach-init.zsh` | 1h | 5 unit tests | - -**Ship When:** -- [ ] All tests pass -- [ ] STAT 545 has `.flow/teach-config.yml` -- [ ] `scripts/quick-deploy.sh` works -- [ ] Branch safety warning tested - ---- - -### Increment 2: Course Context (4-6 hours) - -**Week 1, Days 3-4:** - -| Task | Duration | Output | -|------|----------|--------| -| Add `_display_teaching_context()` | 2h | Week calculation, recent commits | -| Update `.flow/teach-config.yml` schema | 1h | semester_info section | -| Add semester start date UI | 1h | Config prompts in teach-init | -| Test current week calculation | 1h | Edge cases (breaks, past semester) | -| Documentation update | 1h | TEACHING-WORKFLOW.md guide | - -**Ship When:** -- [ ] `work stat-545` shows current week -- [ ] Semester date calculation works -- [ ] Context display tested in STAT 545 - ---- - -### Increment 3: Exam Workflow (8-10 hours) - OPTIONAL - -**Week 2:** - -| Task | Duration | Output | -|------|----------|--------| -| Create `commands/teach-exam.zsh` | 3h | Exam scaffolding | -| Create `scripts/exam-to-qti.sh` | 1h | Conversion script | -| Add examark dependency check | 1h | Validation in teach-init | -| Scholar integration (if available) | 2h | Optional skill calls | -| Test examark conversion | 1h | E2E exam workflow | -| Documentation: exam workflow | 2h | Tutorial + examples | - -**Ship When:** -- [ ] examark installed -- [ ] 1 exam created end-to-end -- [ ] QTI file validates in Canvas -- [ ] Scholar integration works (if available) - -**Can Skip If:** -- examark not working -- Scholar skills not ready -- Only 2-3 exams/semester (manual is fine) - ---- - -## Success Metrics (Updated) - -### Increment 1: Core Deployment - -| Metric | Target | Measurement | -|--------|--------|-------------| -| Deployment speed | < 2 min | Time from `quick-deploy.sh` to live | -| Branch safety | 100% | Production branch warning shown every time | -| Config validation | 100% | Malformed config rejected with helpful error | -| User experience | Smooth | No manual git commands needed | - -### Increment 2: Course Context - -| Metric | Target | Measurement | -|--------|--------|-------------| -| Week calculation | Accurate | Matches manual calculation | -| Context display | < 50ms | work command response time | -| Semester updates | Easy | Config update + commit | - -### Increment 3: Exam Workflow (Optional) - -| Metric | Target | Measurement | -|--------|--------|-------------| -| Exam creation | < 30 min | From `teach-exam` to Canvas upload | -| examark success | 100% | QTI files validate in Canvas | -| Scholar integration | Works | Skills generate exam content (if available) | - ---- - -## Open Questions (Updated) - -### Resolved (from deep brainstorm) - -- ✅ **Which courses in Phase 1?** → STAT 545 only -- ✅ **Primary pain point?** → Deployment speed (5-15 min → < 2 min) -- ✅ **Canvas API needed?** → No, manual upload fine -- ✅ **Scholar availability?** → Build alongside, optional integration -- ✅ **Quiz vs exam focus?** → Focus on deployment, not assessment tools -- ✅ **Time budget?** → Flexible, ship incrementally - -### Still Open - -1. **GitHub Pages setup:** Should `teach-init` automate GitHub repo settings (enable Pages, set source)? - - Option A: Manual (document in next steps) - - Option B: Use `gh` CLI to automate (requires GitHub CLI) - - **Recommendation:** Option A (manual) for Phase 1 - -2. **Error recovery:** If `quick-deploy.sh` fails mid-merge, how to recover? - - Current: Manual `git merge --abort` - - Future: Add `scripts/rollback.sh` helper - -3. **Multi-instructor support:** How to handle TAs contributing to course? - - Current: No special handling - - Future: PR workflow before production deploy - -4. **Exam templates:** Should we provide exam question templates? - - Current: Basic markdown template - - Future: Schema for different question types (MC, short answer, essay) - ---- - -## Next Steps (Decision Points) - -### Before Implementation - -1. **Approve this updated spec** (deep brainstorm incorporated) -2. **Install examark** (for Increment 3, optional): - - ```bash - npm install -g examark - ``` - -3. **Decide on scholar skills:** - - Build `/scholar:exam` skill now? - - Or defer to Phase 1.5? - - **Recommendation:** Defer to Phase 1.5 - -### After Approval - -Following workflow protocol: - -1. **Update original spec** or keep both? - - Option A: Replace SPEC-teaching-workflow.md with v2 - - Option B: Keep both (v1 = archive, v2 = current) - - **Recommendation:** Option A (replace) - -2. **Commit updated spec** to dev branch -3. **Create worktree** for feature branch: - - ```bash - git worktree add ~/.git-worktrees/flow-cli-teaching-workflow \ - -b feature/teaching-workflow dev - ``` - -4. **Start NEW session** in worktree: - - ```bash - cd ~/.git-worktrees/flow-cli-teaching-workflow - claude - ``` - ---- - -## Revision History - -| Version | Date | Changes | -|---------|------|---------| -| 1.0 | 2026-01-11 | Initial spec (quiz-focused, 22h estimate) | -| 2.0 | 2026-01-11 | Deep brainstorm update (deployment-focused, incremental) | - ---- - -**Plan Status:** ✅ v2.0 Complete - Ready for Review -**Estimated Complexity:** Medium (reduced from Medium-High) -**Total Effort:** 16-22 hours (was 22 hours) split into 3 increments -**Risk Level:** Low (proven patterns, incremental shipping, optional exam workflow) diff --git a/docs/specs/SPEC-teaching-workflow-v3-enhancements.md b/docs/specs/SPEC-teaching-workflow-v3-enhancements.md deleted file mode 100644 index 3eb223a67..000000000 --- a/docs/specs/SPEC-teaching-workflow-v3-enhancements.md +++ /dev/null @@ -1,944 +0,0 @@ -# SPEC: Teaching Workflow v3.0 Enhancements - -**Status:** Approved -**Created:** 2026-01-18 -**Author:** DT + Claude -**Target Version:** v5.14.0 -**Brainstorm Source:** `/brainstorm max optimize save` - ---- - -## Overview - -This spec addresses four key enhancement areas for the teaching workflow, based on v5.13.1 learnings: - -1. **Command Consolidation** - Remove `teach-init` only -2. **Health Check (`teach doctor`)** - Full implementation with sensible defaults -3. **Help System** - Add `--help` to sub-commands + more examples -4. **Update Flags** - `--update` with backup, `--version` for versioning - ---- - -## Decisions Summary (Approved 2026-01-18) - -| Area | Decision | -|------|----------| -| Command consolidation | Remove `teach-init` only, keep `flow teach` | -| `teach doctor` | Full implementation (deps, config, git, scholar) + `--fix`, `--json` | -| Help system | Add `--help` to all sub-commands + more real-world examples | -| Regeneration | `--update` (auto-backup), `--version` (exam-v2), `--dry-run` | -| Backup storage | `.backups/` inside content folders | -| Backup naming | Timestamp: `midterm.2026-01-18-1430.qmd` | -| Gitignore backups | User choice, configurable in teach-config.yml | -| Retention: Assessments | Archive per semester | -| Retention: Syllabi/Rubrics | Archive per semester | -| Retention: Lectures/Slides | Keep current semester only | - ---- - -## 1. Command Entry Point Analysis - -### Current State (v5.13.1) - -| Entry Point | What It Does | Location | -|-------------|--------------|----------| -| `work stat-440` | Teaching session with branch safety | `commands/work.zsh` | -| `teach exam` | Scholar wrapper for exams | `lib/dispatchers/teach-dispatcher.zsh` | -| `flow teach` | Alias to `teach` | Plugin routing | -| `teach-init` | Initialize teaching project | `commands/teach-init.zsh` | -| `teach init` | Same as teach-init | Dispatcher routing | - -### Decision: Remove `teach-init` only - -| Entry Point | Action | Reason | -|-------------|--------|--------| -| `work ` | ✅ Keep | Session management, different purpose | -| `teach ` | ✅ Keep | Unified dispatcher (canonical) | -| `flow teach` | ✅ Keep | Some users prefer explicit `flow` prefix | -| `teach-init` | ❌ Remove | Redundant with `teach init` | - -### Implementation - -```zsh -# In commands/teach-init.zsh - Add deprecation warning -teach-init() { - echo "${FLOW_COLORS[warning]}⚠️ 'teach-init' is deprecated. Use 'teach init' instead.${FLOW_COLORS[reset]}" - echo "" - # Forward to dispatcher - teach init "$@" -} -``` - -**Effort:** ⚡ Low (1 hour) - ---- - -## 2. `teach doctor` Command - -### Purpose - -Validate that the teaching environment is correctly configured. Essential for debugging "why isn't this working?" issues. - -### Checks to Perform - -```bash -teach doctor - -╭────────────────────────────────────────────────────────────╮ -│ 📚 Teaching Environment Health Check │ -╰────────────────────────────────────────────────────────────╯ - -Dependencies: - ✓ yq (4.35.1) - YAML processing - ✓ git (2.43.0) - Version control - ✓ quarto (1.4.550) - Document rendering - ✓ gh (2.42.0) - GitHub CLI for deployment - ⚠ examark (not found) - Exam conversion (optional) - → Install: npm install -g examark - -Project Configuration: - ✓ .flow/teach-config.yml exists - ✓ Config validates against schema - ✓ Course name: STAT 440 - ✓ Semester: Spring 2026 - ✓ Dates configured (Jan 13 - May 1) - -Git Setup: - ✓ Git repository initialized - ✓ Draft branch exists: draft - ✓ Production branch exists: main - ✓ Remote configured: origin - ⚠ 3 uncommitted changes - -Scholar Integration: - ✓ Claude Code available - ✓ scholar:teaching skills accessible - ⚠ No lesson-plan.yml found (optional) - -──────────────────────────────────────────────────────────── -Summary: 11 passed, 3 warnings, 0 failures -──────────────────────────────────────────────────────────── -``` - -### Flags - -| Flag | Short | Description | -|------|-------|-------------| -| `--quiet` | `-q` | Only show failures/warnings | -| `--fix` | | Auto-fix what's possible | -| `--json` | | Output as JSON for scripts | - -### Check Categories - -#### A. Dependencies (Required) - -| Check | Command | Auto-Fix | -|-------|---------|----------| -| yq installed | `command -v yq` | `brew install yq` | -| git installed | `command -v git` | Manual (Xcode tools) | -| quarto installed | `command -v quarto` | `brew install --cask quarto` | -| gh installed | `command -v gh` | `brew install gh` | - -#### B. Dependencies (Optional) - -| Check | Command | Purpose | -|-------|---------|---------| -| examark | `command -v examark` | Exam conversion | -| claude | `command -v claude` | Scholar skills | - -#### C. Project Configuration - -| Check | Validation | Auto-Fix | -|-------|------------|----------| -| Config exists | `-f .flow/teach-config.yml` | `teach init` | -| Schema valid | `_teach_validate_config` | Manual | -| Course name set | `yq '.course.name'` | Manual | -| Branches defined | `yq '.branches.*'` | Add defaults | -| Dates configured | `yq '.semester_info.start_date'` | `teach dates` | - -#### D. Git Status - -| Check | Validation | Auto-Fix | -|-------|------------|----------| -| Is git repo | `-d .git` | `git init` | -| Draft branch exists | `git show-ref --verify` | Create branch | -| Production branch exists | `git show-ref --verify` | Create branch | -| Remote configured | `git remote -v` | Manual | -| On correct branch | `git branch --show-current` | Offer checkout | -| Clean working tree | `git status --porcelain` | Warn only | - -### Implementation Outline - -```zsh -_teach_doctor() { - local quiet=false fix=false json=false - local -i passed=0 warnings=0 failures=0 - - # Parse flags - while [[ $# -gt 0 ]]; do - case "$1" in - --quiet|-q) quiet=true ;; - --fix) fix=true ;; - --json) json=true ;; - *) ;; - esac - shift - done - - # Header - [[ "$quiet" == false ]] && { - echo "" - echo "╭────────────────────────────────────────────────────────────╮" - echo "│ 📚 Teaching Environment Health Check │" - echo "╰────────────────────────────────────────────────────────────╯" - echo "" - } - - # Run checks - _check_dependencies - _check_project_config - _check_git_status - _check_scholar_integration - - # Summary - echo "" - echo "────────────────────────────────────────────────────────────" - echo "Summary: $passed passed, $warnings warnings, $failures failures" - echo "────────────────────────────────────────────────────────────" - - [[ $failures -gt 0 ]] && return 1 - return 0 -} - -_check_dep() { - local name="$1" - local cmd="$2" - local fix_cmd="$3" - local required="${4:-true}" - - if command -v "$cmd" &>/dev/null; then - local version=$($cmd --version 2>&1 | head -1 | grep -oE '[0-9]+\.[0-9]+(\.[0-9]+)?') - _doctor_pass "$name ($version)" - elif [[ "$required" == "true" ]]; then - _doctor_fail "$name (not found)" "$fix_cmd" - else - _doctor_warn "$name (not found - optional)" "$fix_cmd" - fi -} - -_doctor_pass() { - ((passed++)) - [[ "$quiet" == false ]] && echo " ${FLOW_COLORS[success]}✓${FLOW_COLORS[reset]} $1" -} - -_doctor_warn() { - ((warnings++)) - echo " ${FLOW_COLORS[warning]}⚠${FLOW_COLORS[reset]} $1" - [[ -n "$2" ]] && echo " → $2" -} - -_doctor_fail() { - ((failures++)) - echo " ${FLOW_COLORS[error]}✗${FLOW_COLORS[reset]} $1" - [[ -n "$2" ]] && echo " → $2" -} -``` - -**Effort:** 🔧 Medium (3-4 hours) - ---- - -## 3. Help System Enhancement - -### Decision: Add `--help` + More Examples - -Keep current help format structure but: -1. Add `--help` flag handling to all sub-commands -2. Add more real-world examples to each command - -### Implementation - -```zsh -# Add to each sub-command handler -_teach_exam() { - # Help check first - if [[ "$1" == "--help" || "$1" == "-h" ]]; then - _teach_exam_help - return 0 - fi - # ... rest of implementation -} - -_teach_exam_help() { - echo "teach exam - Generate exam with solutions" - echo "" - echo "USAGE" - echo " teach exam [options]" - echo "" - echo "OPTIONS" - echo " --update, -u Backup existing and regenerate" - echo " --version, -v Create new version (exam-v2)" - echo " --dry-run, -n Preview without executing" - echo " --help, -h Show this help" - echo "" - echo "EXAMPLES" - echo " teach exam \"Midterm 1\" Create midterm exam" - echo " teach exam \"Final\" --week 15 Exam for week 15 topics" - echo " teach exam \"Quiz 3\" --update Regenerate with backup" - echo " teach exam \"Midterm 2\" --version Create midterm-2-v2" - echo "" -} -``` - -### Commands to Update - -| Command | Add `--help` | Add Examples | -|---------|--------------|--------------| -| `teach init` | ✅ | 3 examples | -| `teach deploy` | ✅ | 3 examples | -| `teach exam` | ✅ | 4 examples | -| `teach lecture` | ✅ | 3 examples | -| `teach slides` | ✅ | 3 examples | -| `teach quiz` | ✅ | 3 examples | -| `teach assignment` | ✅ | 3 examples | -| `teach status` | ✅ | 2 examples | -| `teach week` | ✅ | 3 examples | -| `teach doctor` | ✅ | 3 examples | - -**Effort:** 🔧 Medium (3 hours) - ---- - -## 4. Regeneration & Backup System - -### Flags - -| Flag | Short | Description | -|------|-------|-------------| -| `--update` | `-u` | Auto-backup to `.backups/`, then regenerate | -| `--version` | `-v` | Create new version (exam-v2, exam-v3) | -| `--force` | `-f` | Overwrite without backup (destructive) | -| `--dry-run` | `-n` | Preview what would happen | - -### User Experience - -```bash -# First time - creates exam -$ teach exam "Midterm 1" -✓ Created exams/midterm-1/ - -# Second time - prompts user -$ teach exam "Midterm 1" -⚠ Midterm 1 already exists at exams/midterm-1/ - -Options: - 1. Update (backup old, regenerate) - 2. Create new version (midterm-1-v2) - 3. View existing - 4. Cancel - -Choice [1/2/3/4]: _ - -# With --update flag - auto-backup and regenerate -$ teach exam "Midterm 1" --update - Backed up: exams/midterm-1/ → exams/midterm-1/.backups/midterm-1.2026-01-18-1430/ -✓ Regenerated exams/midterm-1/ - -# With --version flag - create new version -$ teach exam "Midterm 1" --version -✓ Created exams/midterm-1-v2/ - -# With --dry-run - preview only -$ teach exam "Midterm 1" --update --dry-run -Would backup: exams/midterm-1/ → exams/midterm-1/.backups/midterm-1.2026-01-18-1430/ -Would regenerate: exams/midterm-1/ -(dry-run, no changes made) -``` - -### Backup Storage - -**Location:** `.backups/` folder inside each content folder - -``` -exams/ -├── midterm-1/ -│ ├── exam.qmd -│ ├── solutions.qmd -│ └── .backups/ -│ ├── midterm-1.2026-01-18-1430/ -│ └── midterm-1.2026-01-15-0900/ -└── final/ - ├── exam.qmd - └── .backups/ -``` - -**Naming:** Timestamp format `./` - -### Backup Configuration in teach-config.yml - -```yaml -# .flow/teach-config.yml -backups: - # Whether to gitignore .backups folders (user choice during init) - gitignore: true # or false - - # Retention policies by content type - retention: - assessments: archive # exams, quizzes, assignments → archive per semester - syllabi: archive # syllabi, rubrics → archive per semester - lectures: semester # lectures, slides → keep current semester only - - # Archive location for semester-end - archive_dir: ".flow/archives" -``` - -### Retention Policies - -| Content Type | Policy | Behavior | -|--------------|--------|----------| -| **Assessments** (exams, quizzes, assignments) | `archive` | Move to archive at semester end, keep forever | -| **Syllabi & Rubrics** | `archive` | Move to archive at semester end, keep forever | -| **Lectures & Slides** | `semester` | Delete `.backups/` at semester end | - -### Archive Command - -```bash -# At semester end, run: -$ teach archive - -Archiving Spring 2026 backups... - ✓ Moved 12 assessment backups to .flow/archives/spring-2026/ - ✓ Moved 2 syllabus backups to .flow/archives/spring-2026/ - ✓ Deleted 45 lecture backups (semester retention policy) - -Archive complete: .flow/archives/spring-2026/ -``` - -### Implementation - -```zsh -_teach_backup_content() { - local content_path="$1" - local content_name="$(basename "$content_path")" - local backup_dir="$content_path/.backups" - local timestamp=$(date +%Y-%m-%d-%H%M) - local backup_path="$backup_dir/${content_name}.${timestamp}" - - mkdir -p "$backup_dir" - - # Copy content to backup (exclude .backups itself) - rsync -a --exclude='.backups' "$content_path/" "$backup_path/" - - echo "$backup_path" -} - -_teach_get_retention_policy() { - local content_type="$1" - local config_file=".flow/teach-config.yml" - - # Map content type to policy - case "$content_type" in - exam|quiz|assignment) - yq '.backups.retention.assessments // "archive"' "$config_file" - ;; - syllabus|rubric) - yq '.backups.retention.syllabi // "archive"' "$config_file" - ;; - lecture|slides) - yq '.backups.retention.lectures // "semester"' "$config_file" - ;; - *) - echo "archive" # Default to safe retention - ;; - esac -} -``` - -**Effort:** 🔧 Medium (4-5 hours) - ---- - -## 5. Task Breakdown & Interactive Review - -### Task Explanations - -Let me explain each implementation task in detail so you can decide which to keep, modify, or remove. - -#### Task 1: Deprecate `teach-init` command - -**What it does:** -- Adds warning message when user runs `teach-init` -- Redirects to `teach init` (dispatcher version) -- Updates documentation to show `teach init` as canonical - -**Code changes:** - -```zsh -# In commands/teach-init.zsh -teach-init() { - echo "⚠️ 'teach-init' is deprecated. Use 'teach init' instead." - teach init "$@" -} -``` - -**Files modified:** `commands/teach-init.zsh` (5 lines) - -**Why:** Simplifies command structure - one way to do things instead of two. - ---- - -#### Task 2: Basic `teach doctor` skeleton - -**What it does:** -- Creates new `teach doctor` command -- Checks if yq, git, quarto, gh are installed -- Shows simple pass/fail output - -**Example output:** - -``` -Dependencies: - ✓ yq (4.35.1) - ✓ git (2.43.0) - ✗ gh (not found) - → Install: brew install gh -``` - -**Files modified:** `lib/dispatchers/teach-dispatcher.zsh` (~50 lines) - -**Why:** Quick way to debug "why isn't this working?" issues. - ---- - -#### Task 3: Add `--help` to all sub-commands - -**What it does:** -- Every `teach` sub-command responds to `--help` flag -- Shows usage, options, examples - -**Example:** - -```bash -$ teach exam --help - -teach exam - Generate exam with solutions - -USAGE - teach exam [options] - -OPTIONS - --help, -h Show this help - -EXAMPLES - teach exam "Midterm 1" -``` - -**Files modified:** `lib/dispatchers/teach-dispatcher.zsh` (~10 functions, 3-5 lines each) - -**Why:** Discoverability - users can learn commands without reading docs. - ---- - -#### Task 4: Full `teach doctor` implementation - -**What it does:** -- Extends Task 2 with more checks: - - Project config validation - - Git branch status - - Scholar integration -- Adds `--fix` flag to auto-install missing tools -- Adds `--json` output for scripts - -**Example:** - -```bash -$ teach doctor --fix - ✗ yq (not found) - → Installing: brew install yq - ✓ Installed yq (4.35.1) -``` - -**Files modified:** `lib/dispatchers/teach-dispatcher.zsh` (~150 lines) - -**Why:** Comprehensive environment validation + auto-fix capability. - ---- - -#### Task 5: Backup system implementation - -**What it does:** -- Creates `.backups/` folders inside content directories -- Implements `--update` flag (backup old, regenerate) -- Implements `--version` flag (create exam-v2, exam-v3) -- Adds retention policies (archive assessments, delete old lectures) - -**Example:** - -```bash -$ teach exam "Midterm 1" --update - Backed up: exams/midterm-1/ → exams/midterm-1/.backups/midterm-1.2026-01-18-1430/ -✓ Regenerated exams/midterm-1/ -``` - -**Files modified:** -- `lib/dispatchers/teach-dispatcher.zsh` (~200 lines) -- `.flow/teach-config.yml` schema (add `backups:` section) - -**Why:** Prevents data loss, preserves assessment history for legal/academic records. - ---- - -#### Task 6: Archive command enhancement - -**What it does:** -- Enhances `teach archive` to handle backups -- Moves assessment/syllabus backups to `.flow/archives/spring-2026/` -- Deletes lecture backups (ephemeral content) - -**Example:** - -```bash -$ teach archive - -Archiving Spring 2026 backups... - ✓ Moved 12 assessment backups to .flow/archives/spring-2026/ - ✓ Deleted 45 lecture backups (semester retention policy) -``` - -**Files modified:** `lib/dispatchers/teach-dispatcher.zsh` (~80 lines) - -**Why:** Clean semester transitions, preserve what matters, delete noise. - ---- - -## 5.5. Interactive Review Results (2026-01-18) - -### Your Decisions - -| Task | Original Plan | Your Decision | -|------|---------------|---------------| -| **Task 1: teach-init** | Deprecate with warning | ❌ Remove entirely | -| **Task 2: Basic doctor** | Check yq, git, quarto, gh | ✅ Keep + add config validation | -| **Task 3: --help flags** | Add to all sub-commands | ✅ Keep as-is | -| **Task 4: Full doctor** | Config, git, --fix, --json | ✅ Keep as-is | -| **Task 5: Backup system** | Full implementation | ✅ Keep as-is | -| **Task 6: Archive** | Auto-delete by policy | ⚠️ Prompt before deleting | - -### Additional Enhancements Requested - -#### Phase 1 (v5.14.0) - Priority - -| Enhancement | Description | -|-------------|-------------| -| **teach status** | Add: deployment status (last deploy, PR status) + backup summary (# backups, last backup time) | -| **teach deploy** | Add: preview changes before creating PR | -| **Scholar wrappers** | Add: templates (exam types) + lesson plan integration (auto-load lesson-plan.yml) | -| **teach init** | Add: project templates (R/Python/Quarto) + GitHub setup - OPTIONAL with flags | - -#### Phase 2 (v5.15.0+) - Future - -| Enhancement | Description | -|-------------|-------------| -| **teach week** | Add: topic preview from config + content checklist (materials exist) | -| **teach dates** | Add: holiday detection (auto-detect from calendar API) | -| **teach config** | Add: interactive editor (guided config editing) | - -### Testing Requirements - -- ✅ Unit tests (individual functions) -- ✅ Integration tests (end-to-end workflows) -- ✅ Validate in scholar-demo-course - ---- - -## 5.6. Revised Task List (Post-Review) - -### Phase 1 Tasks (v5.14.0) - -| # | Task | Effort | Priority | -|---|------|--------|----------| -| 1 | **Remove teach-init** | ⚡ 30m | High | -| | Delete standalone command entirely | | | -| 2 | **Basic teach doctor** | ⚡ 2h | High | -| | Check: yq, git, quarto, gh, .flow/teach-config.yml | | | -| 3 | **Add --help to all sub-commands** | ⚡ 1h | Medium | -| | All 10 sub-commands respond to --help | | | -| 4 | **Full teach doctor** | 🔧 3h | High | -| | Config validation, git checks, --fix flag, --json output | | | -| 5 | **Backup system implementation** | 🔧 5h | High | -| | .backups/ folders, --update, --version, retention policies | | | -| 6 | **Archive with prompts** | 🔧 2h | Medium | -| | Prompt before deleting backups, respect retention policies | | | -| 7 | **teach status enhancements** | 🔧 2h | Medium | -| | Add deployment status + backup summary | | | -| 8 | **teach deploy preview** | 🔧 1h | Medium | -| | Show what changed before creating PR | | | -| 9 | **Scholar wrapper enhancements** | 🔧 4h | High | -| | Templates + lesson plan integration | | | -| 10 | **teach init enhancements** | 🔧 3h | Medium | -| | Project templates + GitHub setup (optional flags) | | | - -**Phase 1 Total:** ~23 hours - -### Phase 2 Tasks (v5.15.0+) - -| # | Task | Effort | Priority | -|---|------|--------|----------| -| 11 | **teach week enhancements** | 🔧 2h | Low | -| | Topic preview + content checklist | | | -| 12 | **teach dates holiday detection** | 🔧 3h | Low | -| | Auto-detect holidays from calendar API | | | -| 13 | **teach config interactive editor** | 🔧 4h | Low | -| | Guided config editing with prompts | | | - -**Phase 2 Total:** ~9 hours - ---- - -## 6. Implementation Plan - -### Phase 1: Foundation (v5.14.0-alpha) - -``` -Week 1: -├── Day 1: teach doctor (basic) -│ ├── Dependency checks -│ ├── Config validation -│ └── Basic output format -│ -├── Day 2: Deprecation warnings -│ ├── teach-init → teach init -│ └── flow teach → teach -│ -└── Day 3: Help utilities - ├── _help_header() - ├── _help_section() - └── Update main dispatcher help -``` - -### Phase 2: Enhancement (v5.14.0-beta) - -``` -Week 2: -├── Day 1: Full teach doctor -│ ├── Git checks -│ ├── Scholar checks -│ ├── --fix flag -│ └── --json output -│ -├── Day 2-3: --update flags -│ ├── Backup logic -│ ├── Interactive prompts -│ └── Apply to all Scholar wrappers -│ -└── Day 4: Sub-command help - ├── teach deploy --help - ├── teach init --help - ├── teach status --help - └── All Scholar command help -``` - -### Phase 3: Polish (v5.14.0) - -``` -Week 3: -├── Documentation updates -├── Test suite expansion -├── Context-aware help -└── Release -``` - ---- - -## 7. Finalized Decisions - -All decisions have been approved during interactive brainstorm session (2026-01-18). - -| # | Question | Decision | Rationale | -|---|----------|----------|-----------| -| 1 | Command deprecation | Remove `teach-init` only | Keep `flow teach` for users who prefer explicit prefix | -| 2 | `teach doctor` scope | Full implementation | Dependencies, config, git, scholar + `--fix`, `--json` | -| 3 | Help system | Add `--help` + examples | Keep current format, just add discoverability | -| 4 | Regeneration flags | `--update`, `--version`, `--dry-run` | Flexible options for different workflows | -| 5 | Backup location | `.backups/` inside content folder | Close to source, easy to find | -| 6 | Backup naming | Timestamp (`name.2026-01-18-1430`) | Sortable, clear when created | -| 7 | Gitignore backups | User choice in config | Configurable via `backups.gitignore` in teach-config.yml | -| 8 | Assessment retention | Archive per semester | Legal/academic record preservation | -| 9 | Syllabi retention | Archive per semester | Contractual document preservation | -| 10 | Lecture retention | Current semester only | Ephemeral, easily regenerated | - ---- - -## 8. Success Metrics - -| Metric | Target | Measurement | -|--------|--------|-------------| -| `teach doctor` catches issues | 90%+ | User reports | -| Help discoverable | < 2 commands to find | User testing | -| `--update` prevents data loss | 100% | Backups created | -| Deprecation warnings shown | 100% | Test coverage | - ---- - -## 9. Test Plan - -### Unit Tests - -```zsh -# tests/test-teach-doctor.zsh -test_doctor_detects_missing_yq() { - # Mock missing yq - unset -f yq - output=$(teach doctor 2>&1) - assert_contains "$output" "yq (not found)" -} - -test_doctor_detects_missing_config() { - cd /tmp/empty-dir - output=$(teach doctor 2>&1) - assert_contains "$output" ".flow/teach-config.yml" -} - -test_doctor_quiet_mode() { - output=$(teach doctor --quiet 2>&1) - # Should only show warnings/failures - refute_contains "$output" "✓" -} -``` - -```zsh -# tests/test-teach-update-flag.zsh -test_update_creates_backup() { - mkdir -p exams/midterm-1 - teach exam "Midterm 1" --update - assert_dir_exists "exams/midterm-1.bak.*" -} - -test_force_no_backup() { - mkdir -p exams/midterm-1 - teach exam "Midterm 1" --force - refute_dir_exists "exams/midterm-1.bak.*" -} -``` - -### Integration Tests - -```bash -# E2E: teach doctor in demo course -cd ~/projects/teaching/scholar-demo-course -teach doctor -# Should pass all checks - -# E2E: teach doctor with missing yq -PATH=/bin:/usr/bin teach doctor -# Should show yq failure - -# E2E: --update flag -teach exam "Test Exam" -teach exam "Test Exam" --update -ls exams/ -# Should show test-exam/ and test-exam.bak.* -``` - ---- - -## 10. Files to Create/Modify - -### New Files - -| File | Purpose | Lines | -|------|---------|-------| -| None | (All in existing dispatcher) | | - -### Modified Files - -| File | Changes | Lines Changed | -|------|---------|---------------| -| `lib/dispatchers/teach-dispatcher.zsh` | Add `_teach_doctor()`, update help, add flag handling | ~200 | -| `commands/teach-init.zsh` | Add deprecation warning | ~5 | -| `lib/core.zsh` | Add `_help_header()`, `_help_section()` utilities | ~50 | -| `docs/reference/MASTER-DISPATCHER-GUIDE.md#teach-dispatcher` | Document new features | ~100 | - ---- - -## 11. Next Steps - -1. **Review this spec** -2. **Decide on decision points** (Section 7) -3. **Create implementation branch:** - - ```bash - git worktree add ~/.git-worktrees/flow-cli-teach-v3 \ - -b feature/teach-v3-enhancements dev - ``` - -4. **Start with Quick Wins** (teach doctor skeleton) - ---- - -## Version History - -| Version | Date | Changes | -|---------|------|---------| -| 1.0 | 2026-01-18 | Initial draft from brainstorm session | -| 2.0 | 2026-01-18 | Interactive review - finalized core decisions | -| 3.0 | 2026-01-18 | Deep interactive review - task explanations + subcommand enhancements | - ---- - -**Spec Status:** ✅ Approved (Comprehensive) -**Estimated Effort:** -- Phase 1 (v5.14.0): ~23 hours -- Phase 2 (v5.15.0+): ~9 hours -**Risk Level:** Low (incremental, backwards compatible) - -## Summary of Changes (v3.0) - -### Core Modifications - -- ❌ teach-init → **Delete entirely** (not deprecate) -- ✅ teach doctor → **Add config validation** to basic version -- ⚠️ teach archive → **Prompt before deleting** (not auto-delete) - -### New Enhancements (Phase 1) - -- **teach status**: Deployment status + backup summary -- **teach deploy**: Preview changes before PR -- **Scholar wrappers**: Templates + lesson plan integration -- **teach init**: Project templates + GitHub setup (optional flags) - -### Future Enhancements (Phase 2) - -- **teach week**: Topic preview + content checklist -- **teach dates**: Holiday detection -- **teach config**: Interactive editor - -### Testing Strategy - -- Unit tests + Integration tests + scholar-demo-course validation - -## Next Steps - -1. **Create implementation branch:** - - ```bash - git worktree add ~/.git-worktrees/flow-cli-teach-v3 \ - -b feature/teach-v3-enhancements dev - ``` - -2. **Phase 1 Implementation Order:** - - Task 1: Remove teach-init (30min) - - Task 2: Basic teach doctor + config validation (2h) - - Task 4: Full teach doctor with --fix (3h) - - Task 5: Backup system (5h) - - Task 3: Add --help flags (1h) - - Task 7: teach status enhancements (2h) - - Task 8: teach deploy preview (1h) - - Task 9: Scholar wrapper enhancements (4h) - - Task 6: Archive with prompts (2h) - - Task 10: teach init enhancements (3h) - -3. **Testing & Validation:** - - Unit tests for each task - - Integration tests in scholar-demo-course - - Update test suite in `tests/test-teach-*.zsh` diff --git a/docs/specs/SPEC-teaching-workflow.md b/docs/specs/SPEC-teaching-workflow.md deleted file mode 100644 index 7ce10b4e2..000000000 --- a/docs/specs/SPEC-teaching-workflow.md +++ /dev/null @@ -1,926 +0,0 @@ -# Teaching Feature for flow-cli - Implementation Plan - -**Feature:** Universal teaching workflow with STAT 545 as first implementation -**Status:** Planning - Awaiting Approval -**Created:** 2026-01-11 -**Branch Strategy:** feature/teaching-workflow → dev → main - ---- - -## Executive Summary - -Implement a **comprehensive teaching workflow system** in flow-cli that: -1. **STAT 545 Migration** (Phase 1) - Full two-branch workflow with 3-tier automation -2. **Generic Framework** (Phase 2) - `teach init` command for future courses -3. **Integration Layer** - Bridges to scholar (AI generation) and nexus (tracking) - -**Key Innovation:** Teaching-aware `work` command that auto-detects course context and enables course-specific shortcuts, safety guards, and status dashboards. - ---- - -## User Requirements (From Brainstorm) - -### Scope Decisions - -- ✅ **STAT 545 first**, then generalize -- ✅ **flow-cli owns workflows** (pure ZSH, <10ms) -- ✅ **Per-course config** (`.flow/teach-config.yml`) -- ✅ **Full automation** (Scripts + flow-cli + GitHub Actions) - -### Integration Decisions - -- ✅ **Scholar**: Direct calls (flow-cli wraps scholar commands) -- ✅ **Nexus**: Parallel (nexus tracks, flow-cli operates independently) -- ✅ **Architecture**: Extend `work` command (project-aware, not new dispatcher) - -### Workflow Priorities - -1. Generate with scholar (`/teaching:quiz`) -2. Convert with examark (`md → qti.zip`) -3. Test in Canvas sandbox -4. Deploy to production via git - -### Success Criteria - -1. ⚡ **Deployment < 2 min** (typo → live) -2. 📝 **Quiz workflow < 30 min** (generate → Canvas) -3. 🛡️ **Branch safety** (workflow guard prevents production edits) -4. 🤖 **Full automation** (no manual git commands) - ---- - -## Architecture Overview - -### Layer Integration - -``` -┌─────────────────────────────────────────────────────────────┐ -│ scholar (Claude Plugin) │ -│ /teaching:quiz, /teaching:exam, /teaching:assignment │ -│ → AI-powered content generation │ -└────────────────────┬────────────────────────────────────────┘ - │ - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ flow-cli (Pure ZSH) ← THIS IMPLEMENTATION │ -│ ├── work stat-545 (teaching-aware session) │ -│ ├── .flow/teach-config.yml (course configuration) │ -│ ├── scripts/ templates (deployment automation) │ -│ └── Workflow guard (prevents production edits) │ -└────────────────────┬────────────────────────────────────────┘ - │ - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ nexus CLI (Python) - Parallel Tracking │ -│ nexus teach course list, nexus teach course show │ -│ → Optional rich tracking layer │ -└─────────────────────────────────────────────────────────────┘ -``` - -### Data Flow - -``` -1. Scholar generates quiz: - /teaching:quiz "ANOVA concepts" - → ~/projects/teaching/stat-545/quizzes/week08-quiz.md - -2. flow-cli converts + deploys: - work stat-545 (detects teaching project) - s545q week08 (convert quiz to Canvas) - s545d (deploy to production) - -3. Nexus tracks (optional): - nexus teach course show stat-545 - → Shows course status, recent updates -``` - ---- - -## Phase 1: STAT 545 Migration - -### 1.1: Teaching Project Detection - -**File:** `lib/project-detector.zsh` - -**Current State:** - -```zsh -# Line 30-33: Already detects teaching projects -if [[ -f "$dir/syllabus.qmd" ]] || [[ -d "$dir/lectures" ]]; then - echo "teaching" - return 0 -fi -``` - -**Enhancement Needed:** - -```zsh -_detect_teaching_enhanced() { - local dir="$1" - - # Enhanced detection - if [[ -f "$dir/syllabus.qmd" ]] || - [[ -d "$dir/lectures" ]] || - [[ -f "$dir/.flow/teach-config.yml" ]]; then - - # Load course metadata - if [[ -f "$dir/.flow/teach-config.yml" ]]; then - _load_teaching_config "$dir" - fi - - echo "teaching" - return 0 - fi - - return 1 -} -``` - -### 1.2: Teaching Configuration - -**New File:** `.flow/teach-config.yml` (in STAT 545 repo) - -```yaml -# STAT 545 Teaching Configuration -course: - name: "STAT 545" - full_name: "Design of Experiments" - semester: "spring" - year: 2026 - instructor: "DT" - -branches: - draft: "draft" - production: "production" - -scholar: - quiz_command: "/teaching:quiz" - exam_command: "/teaching:exam" - assignment_command: "/teaching:assignment" - -examark: - enabled: true - quiz_dir: "quizzes" - exam_dir: "exams" - output_format: "qti" - -deployment: - web: - type: "github-pages" - branch: "production" - url: "https://data-wise.github.io/stat-545" - lms: - type: "canvas" - upload_manual: true # Phase 2: API integration - -automation: - quick_deploy: "scripts/quick-deploy.sh" - batch_publish: "scripts/publish-batch.sh" - quiz_convert: "scripts/quiz-to-qti.sh" - semester_archive: "scripts/semester-archive.sh" - -shortcuts: - s545: "work stat-545" - s545q: "quiz-to-qti.sh" - s545d: "quick-deploy.sh" - s545b: "publish-batch.sh" -``` - -### 1.3: Enhanced `work` Command - -**File:** `commands/work.zsh` - -**New Function:** `_work_teaching_session()` - -```zsh -_work_teaching_session() { - local project_dir="$1" - local config_file="$project_dir/.flow/teach-config.yml" - - # 1. Load config - if [[ ! -f "$config_file" ]]; then - _flow_log_error "Teaching config not found: $config_file" - return 1 - fi - - local course_name=$(yq '.course.name' "$config_file") - local current_branch=$(git -C "$project_dir" branch --show-current) - local production_branch=$(yq '.branches.production' "$config_file") - - # 2. Branch safety check - if [[ "$current_branch" == "$production_branch" ]]; then - echo "" - _flow_log_warning "⚠️ You are on PRODUCTION branch: $production_branch" - echo "" - echo " ${FLOW_COLORS[error]}Students see this branch!${FLOW_COLORS[reset]}" - echo " ${FLOW_COLORS[info]}Switch to draft branch for edits${FLOW_COLORS[reset]}" - echo "" - read -q "?Continue anyway? [y/N] " continue_anyway - echo "" - - if [[ "$continue_anyway" != "y" ]]; then - _flow_log_info "Switching to draft branch..." - git -C "$project_dir" checkout $(yq '.branches.draft' "$config_file") - fi - fi - - # 3. Show course context - _display_teaching_context "$project_dir" "$config_file" - - # 4. Load shortcuts - _load_teaching_shortcuts "$config_file" - - # 5. Open editor - cd "$project_dir" - ${EDITOR:-code} . -} - -_display_teaching_context() { - local project_dir="$1" - local config_file="$2" - - local course_name=$(yq '.course.name' "$config_file") - local semester=$(yq '.course.semester' "$config_file") - local year=$(yq '.course.year' "$config_file") - - echo "" - echo "${FLOW_COLORS[bold]}📚 $course_name - $semester $year${FLOW_COLORS[reset]}" - echo "" - - # Show current week (based on date) - local current_week=$(_calculate_current_week "$config_file") - if [[ -n "$current_week" ]]; then - echo " ${FLOW_COLORS[info]}Current Week:${FLOW_COLORS[reset]} Week $current_week" - fi - - # Show recent quiz/assignment status (from nexus if available) - if command -v nexus &>/dev/null; then - echo " ${FLOW_COLORS[info]}Recent Materials:${FLOW_COLORS[reset]}" - nexus teach course show "$course_name" 2>/dev/null | tail -5 - fi - - # Show shortcuts - echo "" - echo " ${FLOW_COLORS[bold]}Quick Commands:${FLOW_COLORS[reset]}" - yq -r '.shortcuts | to_entries | .[] | " \(.key) → \(.value)"' "$config_file" - echo "" -} - -_load_teaching_shortcuts() { - local config_file="$1" - - # Create aliases for current session - eval "$(yq -r '.shortcuts | to_entries | .[] | "alias \(.key)=\"\(.value)\""' "$config_file")" -} - -_calculate_current_week() { - local config_file="$1" - - # Simple week calculation based on semester start date - # TODO: Read semester start from config, calculate week number - - echo "8" # Placeholder -} -``` - -### 1.4: Automation Scripts (Templates) - -**New Directory:** `lib/templates/teaching/` (in flow-cli) - -#### Template 1: `quick-deploy.sh` - -```bash -#!/usr/bin/env bash -# Quick Deploy - Single commit to production -# Generated by flow-cli teaching framework - -set -euo pipefail - -# Load course config -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_DIR="$(dirname "$SCRIPT_DIR")" -CONFIG="$PROJECT_DIR/.flow/teach-config.yml" - -# Read config -DRAFT_BRANCH=$(yq '.branches.draft' "$CONFIG") -PRODUCTION_BRANCH=$(yq '.branches.production' "$CONFIG") - -# Safety check -CURRENT_BRANCH=$(git branch --show-current) -if [[ "$CURRENT_BRANCH" != "$DRAFT_BRANCH" ]]; then - echo "❌ Must be on $DRAFT_BRANCH branch" - exit 1 -fi - -# Quick deploy -echo "🚀 Quick Deploy: $DRAFT_BRANCH → $PRODUCTION_BRANCH" -echo "" - -git checkout "$PRODUCTION_BRANCH" -git merge "$DRAFT_BRANCH" --no-edit -git push origin "$PRODUCTION_BRANCH" -git checkout "$DRAFT_BRANCH" - -echo "" -echo "✅ Deployed to production" -echo "🌐 Live at: $(yq '.deployment.web.url' "$CONFIG")" -``` - -#### Template 2: `quiz-to-qti.sh` - -```bash -#!/usr/bin/env bash -# Quiz Converter - Markdown to Canvas QTI -# Uses examark for conversion - -set -euo pipefail - -QUIZ_FILE="$1" - -if [[ -z "$QUIZ_FILE" ]]; then - echo "Usage: quiz-to-qti.sh " - exit 1 -fi - -if [[ ! -f "$QUIZ_FILE" ]]; then - echo "❌ Quiz file not found: $QUIZ_FILE" - exit 1 -fi - -echo "📝 Converting quiz to Canvas format..." -examark "$QUIZ_FILE" -o "${QUIZ_FILE%.md}.qti.zip" - -echo "✅ Canvas file ready: ${QUIZ_FILE%.md}.qti.zip" -echo "📤 Upload to Canvas manually (Phase 2: API integration)" -``` - -#### Template 3: `publish-batch.sh` - -```bash -#!/usr/bin/env bash -# Batch Publish - Multiple commits to production - -set -euo pipefail - -# Load config -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_DIR="$(dirname "$SCRIPT_DIR")" -CONFIG="$PROJECT_DIR/.flow/teach-config.yml" - -DRAFT_BRANCH=$(yq '.branches.draft' "$CONFIG") -PRODUCTION_BRANCH=$(yq '.branches.production' "$CONFIG") - -# Show commits to deploy -echo "📦 Commits to deploy:" -git log "$PRODUCTION_BRANCH..$DRAFT_BRANCH" --oneline -echo "" - -read -p "Deploy all commits? [Y/n] " confirm -if [[ "$confirm" == "n" ]]; then - exit 0 -fi - -# Deploy -git checkout "$PRODUCTION_BRANCH" -git merge "$DRAFT_BRANCH" --no-edit -git push origin "$PRODUCTION_BRANCH" -git checkout "$DRAFT_BRANCH" - -echo "" -echo "✅ Batch deploy complete" -``` - -#### Template 4: `semester-archive.sh` - -```bash -#!/usr/bin/env bash -# Semester Archive - Annual transition helper - -set -euo pipefail - -# Load config -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_DIR="$(dirname "$SCRIPT_DIR")" -CONFIG="$PROJECT_DIR/.flow/teach-config.yml" - -SEMESTER=$(yq '.course.semester' "$CONFIG") -YEAR=$(yq '.course.year' "$CONFIG") -TAG="$SEMESTER-$YEAR-final" - -echo "📋 Semester Archive Tool" -echo "" -echo " Semester: $SEMESTER $YEAR" -echo " Tag: $TAG" -echo "" - -read -p "Create archive tag? [Y/n] " confirm -if [[ "$confirm" == "n" ]]; then - exit 0 -fi - -# Tag production -git tag -a "$TAG" -m "$SEMESTER $YEAR Complete" -git push --tags - -echo "" -echo "✅ Archived: $TAG" -echo "📝 Update .flow/teach-config.yml for next semester" -``` - -### 1.5: GitHub Actions Workflow - -**New File:** `.github/workflows/deploy.yml` (template) - -```yaml -name: Deploy to GitHub Pages - -on: - push: - branches: - - {{ production_branch }} # Variable from config - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Setup Quarto - uses: quarto-dev/quarto-actions/setup@v2 - - - name: Setup R - uses: r-lib/actions/setup-r@v2 - with: - r-version: '4.3.0' - - - name: Install R dependencies - run: | - install.packages(c("tidyverse", "knitr", "rmarkdown")) - shell: Rscript {0} - - - name: Render Quarto - run: quarto render - - - name: Deploy to GitHub Pages - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./_site -``` - -### 1.6: Migration Command - -**New File:** `commands/teach-init.zsh` - -```zsh -teach-init() { - local course_name="$1" - - if [[ -z "$course_name" ]]; then - _flow_log_error "Usage: teach-init " - return 1 - fi - - echo "🎓 Initializing teaching workflow for: $course_name" - echo "" - - # Detect git state - if [[ -d .git ]]; then - _teach_migrate_existing_repo "$course_name" - else - _teach_create_fresh_repo "$course_name" - fi -} - -_teach_migrate_existing_repo() { - local course_name="$1" - - echo "📋 Detected existing git repository" - echo "" - echo "Choose migration strategy:" - echo " 1. In-place conversion (rename main→production, create draft)" - echo " 2. Fresh start (initialize clean repo)" - echo "" - - read -p "Choice [1/2]: " choice - - case "$choice" in - 1) _teach_inplace_conversion "$course_name" ;; - 2) _teach_fresh_start "$course_name" ;; - *) echo "Invalid choice"; return 1 ;; - esac -} - -_teach_inplace_conversion() { - local course_name="$1" - - # Tag current state - local semester=$(date +"%Y-%m" | sed 's/01-05/spring/' | sed 's/08-12/fall/') - local year=$(date +%Y) - git tag -a "$semester-$year-pre-migration" -m "Pre-migration snapshot" - - # Rename main → production - git branch -m main production 2>/dev/null || git branch -m master production - git push -u origin production - - # Create draft from production - git checkout -b draft production - git push -u origin draft - - # Create directory structure - mkdir -p .flow scripts - - # Copy templates - _teach_install_templates "$course_name" - - echo "" - echo "✅ Migration complete" -} - -_teach_install_templates() { - local course_name="$1" - - # Copy script templates from flow-cli - local template_dir="${FLOW_PLUGIN_ROOT}/lib/templates/teaching" - - cp "$template_dir/quick-deploy.sh" scripts/ - cp "$template_dir/quiz-to-qti.sh" scripts/ - cp "$template_dir/publish-batch.sh" scripts/ - cp "$template_dir/semester-archive.sh" scripts/ - chmod +x scripts/*.sh - - # Generate config - cat > .flow/teach-config.yml < "$gh_dir/deploy.yml" - - # Commit setup - git add .flow scripts .github - git commit -m "chore: Initialize teaching workflow - -- Add .flow/teach-config.yml -- Add automation scripts -- Add GitHub Actions deployment - -Generated by flow-cli teach-init" - - git push origin draft - - echo "" - echo "✅ Templates installed" - echo "📝 Edit .flow/teach-config.yml to customize" - echo "" - echo "Next steps:" - echo " 1. work $course_name # Start teaching session" - echo " 2. Create quiz with scholar: /teaching:quiz" - echo " 3. Deploy: s545d (or your custom shortcut)" -} -``` - ---- - -## Phase 2: Generalization - -### 2.1: Future Courses - -After STAT 545 proves the pattern, any new course can use: - -```bash -cd ~/projects/teaching/stat-579 -teach-init "STAT 579" -``` - -This creates: -- `.flow/teach-config.yml` (customized for course) -- `scripts/` (automation helpers) -- `.github/workflows/deploy.yml` (CI/CD) -- Branch structure (draft + production) - -### 2.2: Shared Patterns - -All teaching courses will have: - -| Pattern | Implementation | -|---------|----------------| -| **Two-branch workflow** | draft (work) + production (students) | -| **Workflow guard** | Prevents accidental production edits | -| **Scholar integration** | Direct calls to `/teaching:*` commands | -| **examark conversion** | Markdown → Canvas QTI format | -| **Per-course shortcuts** | Loaded by `work ` command | -| **Automation scripts** | Templates customizable per course | -| **GitHub Actions** | Auto-deploy on production push | - ---- - -## Critical Files - -### New Files (flow-cli) - -| File | Purpose | Lines | -|------|---------|-------| -| `commands/teach-init.zsh` | Course scaffolding command | ~300 | -| `lib/templates/teaching/quick-deploy.sh` | Deploy script template | ~50 | -| `lib/templates/teaching/quiz-to-qti.sh` | Quiz converter template | ~30 | -| `lib/templates/teaching/publish-batch.sh` | Batch deploy template | ~40 | -| `lib/templates/teaching/semester-archive.sh` | Semester archive template | ~60 | -| `lib/templates/teaching/deploy.yml.template` | GitHub Actions template | ~40 | -| `lib/templates/teaching/teach-config.yml.template` | Config template | ~60 | - -### Modified Files (flow-cli) - -| File | Changes | Lines Changed | -|------|---------|---------------| -| `commands/work.zsh` | Add `_work_teaching_session()` | ~150 new | -| `lib/project-detector.zsh` | Enhanced teaching detection | ~30 modified | -| `flow.plugin.zsh` | Source teach-init command | +1 | - -### New Files (STAT 545 repo) - -| File | Purpose | -|------|---------| -| `.flow/teach-config.yml` | Course-specific configuration | -| `scripts/quick-deploy.sh` | From template (customizable) | -| `scripts/quiz-to-qti.sh` | From template | -| `scripts/publish-batch.sh` | From template | -| `scripts/semester-archive.sh` | From template | -| `.github/workflows/deploy.yml` | GitHub Actions CI/CD | - ---- - -## Verification & Testing - -### Unit Tests - -**New Test Suite:** `tests/test-teach-init.zsh` - -```zsh -# Test 1: teach-init creates directory structure -test_teach_init_creates_structure() { - cd /tmp/test-course - teach-init "Test Course" - - assert_directory_exists ".flow" - assert_file_exists ".flow/teach-config.yml" - assert_directory_exists "scripts" - assert_file_executable "scripts/quick-deploy.sh" -} - -# Test 2: work command detects teaching project -test_work_detects_teaching() { - cd /tmp/test-course - local project_type=$(_flow_detect_project_type .) - - assert_equals "teaching" "$project_type" -} - -# Test 3: Branch safety check -test_production_branch_warning() { - cd /tmp/test-course - git checkout production - - # Should warn when on production - output=$(work test-course 2>&1) - assert_contains "$output" "PRODUCTION branch" -} - -# Test 4: Scholar integration -test_scholar_quiz_generation() { - # Assumes scholar plugin installed - if ! command -v claude &>/dev/null; then - skip "Requires Claude Code" - fi - - cd /tmp/test-course - # Test workflow would call scholar command - # Verify quiz file created -} -``` - -### Integration Tests - -**E2E Workflow Test:** - -```bash -# 1. Initialize course -cd ~/projects/teaching/stat-545-test -teach-init "STAT 545" - -# 2. Verify structure -ls -la .flow/teach-config.yml -ls -la scripts/ - -# 3. Generate quiz (requires scholar) -/teaching:quiz "ANOVA concepts" -# → Creates quizzes/week08-quiz.md - -# 4. Convert quiz -./scripts/quiz-to-qti.sh quizzes/week08-quiz.md -# → Creates quizzes/week08-quiz.qti.zip - -# 5. Deploy -./scripts/quick-deploy.sh -# → Merges draft → production, pushes to GitHub - -# 6. Verify deployment -curl -s https://data-wise.github.io/stat-545 | grep "Week 8" -``` - -### Manual Testing Checklist - -- [ ] `teach-init` creates all files -- [ ] `work stat-545` shows teaching context -- [ ] Branch safety warning on production -- [ ] Shortcuts loaded (s545, s545q, s545d) -- [ ] Scholar commands work (`/teaching:quiz`) -- [ ] examark conversion works -- [ ] Quick deploy works (draft → production) -- [ ] GitHub Actions triggers on push -- [ ] No manual git commands needed - ---- - -## Dependencies - -### Required (flow-cli) - -- `yq` - YAML parsing (for .flow/teach-config.yml) -- `git` - Version control -- `quarto` - Course website rendering - -### Required (STAT 545 workflow) - -- `scholar` plugin - Quiz/exam generation -- `examark` - Markdown → Canvas QTI conversion -- `nexus` CLI (optional) - Course tracking - -### Optional Enhancements - -- GitHub CLI (`gh`) - For PR creation -- Canvas API - Phase 2 auto-upload - ---- - -## Implementation Order - -### Week 1: Core Framework - -1. **Day 1** (4 hours) - - Create `lib/templates/teaching/` with 4 script templates - - Create `commands/teach-init.zsh` with scaffolding logic - - Add `_work_teaching_session()` to `commands/work.zsh` - -2. **Day 2** (3 hours) - - Enhance `lib/project-detector.zsh` for teaching detection - - Add YAML config loader (`_load_teaching_config`) - - Add branch safety check (`_check_production_branch`) - -3. **Day 3** (2 hours) - - Test suite: `tests/test-teach-init.zsh` - - Documentation: `docs/guides/TEACHING-WORKFLOW.md` - - Update CLAUDE.md with teaching patterns - -### Week 2: STAT 545 Migration - -1. **Day 1** (3 hours) - - Run `teach-init` in stat-545 repo - - Customize scripts for STAT 545 specifics - - Test quick-deploy workflow - -2. **Day 2** (3 hours) - - Integrate scholar commands - - Test quiz generation → conversion → deploy - - Refine .flow/teach-config.yml - -3. **Day 3** (2 hours) - - GitHub Actions setup - - End-to-end workflow verification - - Document STAT 545 as reference example - -### Week 3: Polish & Documentation - -1. **Day 1** (2 hours) - - Edge case handling - - Error messages improvement - - Performance optimization - -2. **Day 2** (2 hours) - - Comprehensive documentation - - Tutorial: STAT 545 migration walkthrough - - Quick reference card - -3. **Day 3** (1 hour) - - Code review - - Final testing - - Ready for production - -**Total Effort:** ~22 hours over 3 weeks (flexible timeline) - ---- - -## Success Metrics - -### Performance - -- ✅ `work stat-545` responds in < 50ms (teaching context loading) -- ✅ `teach-init` completes in < 5 seconds -- ✅ Quick deploy (typo fix) completes in < 2 min -- ✅ Quiz workflow (generate → Canvas) completes in < 30 min - -### Reliability - -- ✅ Branch guard prevents 100% of accidental production edits -- ✅ All 4 automation scripts work without manual intervention -- ✅ GitHub Actions deploy succeeds on first push -- ✅ Zero manual git commands needed for daily workflow - -### Usability - -- ✅ STAT 545 instructor uses shortcuts daily (s545d, s545q) -- ✅ New quiz creation with scholar takes < 10 min -- ✅ Course context visible in terminal (week, recent materials) -- ✅ Semester transition completes in < 30 min with archive script - ---- - -## Open Questions - -1. **Scholar availability**: Is scholar plugin installed? (Required for quiz generation) -2. **Canvas API**: Should we add Canvas API upload in Phase 1 or defer to Phase 2? -3. **Week calculation**: How to determine current week? (config start date vs manual override) -4. **Testing strategy**: Should we create a test course or use STAT 545 directly? -5. **Documentation location**: STAT 545 spec file - move to stat-545 repo or keep in flow-cli/docs/specs/? - ---- - -## Next Steps (User Decision Points) - -### Before Implementation Begins - -1. **Approve this plan** (via ExitPlanMode) -2. **Confirm scholar plugin installed** (verify `/teaching:*` commands work) -3. **Choose testing approach**: - - Option A: Create test course first (safer) - - Option B: Use STAT 545 directly (faster) -4. **Decide on STAT 545 spec**: - - Keep in flow-cli (reference example) - - Move to stat-545 repo (cleaner separation) - -### After Approval - -Following workflow protocol: -1. **Commit this plan** to dev branch (docs/specs/SPEC-teaching-workflow.md) -2. **Create worktree** for feature branch -3. **Ask user to start NEW session** in worktree - -``` -✅ Plan ready for approval! - -To start implementation after approval: - cd ~/.git-worktrees/flow-cli-teaching-workflow - claude -``` - ---- - -**Plan Status:** ✅ Complete - Ready for Review -**Estimated Complexity:** Medium-High (new subsystem, multiple integrations) -**Risk Level:** Low (well-scoped, proven patterns from STAT 545 spec) diff --git a/docs/specs/SPEC-v4.9.0-installation-onboarding.md b/docs/specs/SPEC-v4.9.0-installation-onboarding.md deleted file mode 100644 index 27ca4f3ff..000000000 --- a/docs/specs/SPEC-v4.9.0-installation-onboarding.md +++ /dev/null @@ -1,524 +0,0 @@ -# SPEC: v4.9.0 - Installation & Onboarding - -**Status:** Planning -**Created:** 2026-01-05 -**Target:** v4.9.0 -**Estimated Effort:** 2-3 days -**Priority:** High ⚡ - ---- - -## Overview - -Transform flow-cli installation from "requires ZSH plugin knowledge" to "works in 30 seconds" with zero-friction onboarding. - -### Current State (v4.8.1) - -**Homebrew (Primary):** - -```bash -brew tap data-wise/tap -brew install flow-cli -# Works immediately, but users don't know what commands exist -``` - -**Plugin Managers:** - -```bash -# Requires understanding of ZSH plugin managers -antidote install data-wise/flow-cli -``` - -**Pain Points:** -- ❌ No guided onboarding after install -- ❌ Users don't know where to start -- ❌ No verification that everything works -- ❌ Missing dependencies not auto-detected -- ❌ No first-run experience - -### Target State (v4.9.0) - -**One-Liner Install:** - -```bash -curl -fsSL https://get.flow-cli.dev/install.sh | bash -# Auto-detects: Homebrew or plugin manager -# Runs: Health check + first-run wizard -# Result: User knows exactly what to do next -``` - -**First Run:** - -```bash -$ work my-project -👋 Welcome to flow-cli! Let's get you set up. - -✓ ZSH detected -✓ Git installed -⚠ fzf not found (recommended for project picker) - -Install recommended tools? [Y/n] y -✓ fzf installed -✓ bat installed - -Quick tutorial? [Y/n] y -→ Running: work my-project -→ Log wins with: win "text" -→ End session: finish - -🎉 You're all set! Type 'flow help' anytime. -``` - ---- - -## Goals - -1. **Zero-Friction Install** - One command, works everywhere -2. **Auto-Discovery** - Detect what's missing, offer to fix it -3. **Guided Onboarding** - First-run wizard with 30-second tutorial -4. **Smart Defaults** - Works out-of-box, customize later -5. **Progressive Disclosure** - Show basics first, reveal advanced features gradually - ---- - -## Components - -### 1. Install Script (`install.sh`) - -**Location:** Root of repository + hosted at `get.flow-cli.dev` -**Purpose:** One-liner installation for all environments - -**Features:** -- ✅ Auto-detect OS (macOS, Linux, WSL) -- ✅ Auto-detect install method preference - - Homebrew (if installed) → brew install - - Plugin manager (antidote, zinit, omz) → plugin install - - Manual fallback → git clone + source -- ✅ Idempotent (safe to run multiple times) -- ✅ Version pinning support (`FLOW_VERSION=v4.9.0`) -- ✅ Dry-run mode (`--dry-run`) -- ✅ Post-install verification -- ✅ Trigger first-run wizard - -**Architecture:** - -```bash -install.sh -├── detect_os() # macOS, Linux, WSL -├── detect_shell() # zsh, bash (error if not zsh) -├── detect_homebrew() # Check if brew available -├── detect_plugin_mgr() # antidote → zinit → omz → manual -├── install_homebrew() # brew tap + brew install -├── install_plugin() # Plugin manager specific -├── install_manual() # git clone + add to .zshrc -├── verify_install() # flow --version works -└── run_first_setup() # Trigger flow setup --first-run -``` - -**Error Handling:** -- Clear error messages with actionable fixes -- Rollback on failure (remove what was added) -- Support contact info on critical errors - -**Testing:** -- Docker containers (Ubuntu 22.04, 24.04, Debian, Alpine) -- macOS (Intel + Apple Silicon) -- CI integration - ---- - -### 2. Enhanced `flow doctor --fix` - -**Current State (v4.8.1):** - -```bash -$ flow doctor -✓ work command found -✓ dash command found -⚠ fzf not found - -$ flow doctor --fix -# Just shows what's missing, doesn't actually fix -``` - -**Target State (v4.9.0):** - -```bash -$ flow doctor --fix -🔍 Checking flow-cli health... - -Core Commands: ✓ All working -Dispatchers: ✓ All 8 loaded - -Recommended Tools: - ⚠ fzf not found - ⚠ bat not found - ✓ git installed - -Install missing tools? [Y/n] y - -Installing via Homebrew... - ✓ fzf installed - ✓ bat installed - -🎉 All dependencies satisfied! - -Optional enhancements available: - - eza (modern ls) [y/N] - - zoxide (smart cd) [y/N] - - delta (better git diffs) [y/N] - -Install optional tools? [y/N] n - -✓ Health check complete! -``` - -**Features:** -- ✅ Interactive mode (ask before installing) -- ✅ Batch mode (`-y` flag, install all) -- ✅ Selective install (prompt for each category) -- ✅ Detection of package manager (brew, apt, pacman, etc.) -- ✅ Progress indicators for downloads -- ✅ Verification after install -- ✅ Undo support (remove what was installed) - -**Categories:** -1. **Core** - Required for basic functionality -2. **Recommended** - Significantly improves UX (fzf, bat) -3. **Optional** - Nice-to-have enhancements (eza, zoxide, delta) - -**Implementation:** - -```zsh -# In commands/doctor.zsh -_flow_doctor_fix() { - local interactive=true - local category="all" # core, recommended, optional, all - - case "$1" in - -y|--yes) interactive=false ;; - --core) category="core" ;; - --recommended) category="recommended" ;; - --optional) category="optional" ;; - esac - - _doctor_check_and_install "core" "$interactive" - [[ "$category" != "core" ]] && _doctor_check_and_install "recommended" "$interactive" - [[ "$category" == "all" || "$category" == "optional" ]] && _doctor_check_and_install "optional" "$interactive" -} -``` - ---- - -### 3. First-Run Wizard (`flow setup`) - -**Trigger Points:** -1. After fresh install (via install.sh) -2. Manual: `flow setup` -3. First time running `work` command -4. Detection: `~/.config/flow-cli/.setup_complete` doesn't exist - -**Wizard Flow:** - -``` -┌────────────────────────────────────────────────┐ -│ 👋 Welcome to flow-cli! │ -│ │ -│ Let's get you set up in 30 seconds. │ -└────────────────────────────────────────────────┘ - -Step 1/4: Verify Installation - ✓ flow-cli installed - ✓ ZSH detected - ✓ Git configured - -Step 2/4: Install Recommended Tools - ⚠ fzf not found (enables project picker) - ⚠ bat not found (syntax highlighting) - - Install now? [Y/n] y - ✓ Installing fzf... done - ✓ Installing bat... done - -Step 3/4: Configure Projects - Where do you keep your projects? - [Default: ~/projects] - → ~/projects - - ✓ Found 12 projects in ~/projects - -Step 4/4: Quick Tutorial - Let's try the core workflow: - - 1. Start working: - $ work my-project - - 2. Log accomplishments: - $ win "Fixed the bug" - $ win "Added tests" - - 3. See your progress: - $ yay - - 4. End session: - $ finish - - Try it now? [Y/n] y - - [Interactive demo with explanations] - -┌────────────────────────────────────────────────┐ -│ 🎉 You're all set! │ -│ │ -│ Quick Reference: │ -│ work Start working │ -│ dash Project dashboard │ -│ pick Choose project │ -│ win Log accomplishment │ -│ flow help Show all commands │ -│ │ -│ Documentation: https://flow-cli.dev │ -└────────────────────────────────────────────────┘ - -Setup complete! ✓ -``` - -**Features:** -- ✅ Skippable (Ctrl-C anytime, `--skip` flag) -- ✅ Resume on interrupt (save progress) -- ✅ Non-intrusive (only runs once) -- ✅ Quick mode (`--quick` for minimal setup) -- ✅ Unattended mode (`--unattended` for CI/scripts) - -**Implementation:** - -```zsh -# commands/setup.zsh -flow_setup() { - local mode="interactive" # interactive, quick, unattended - local force=false - - # Check if already setup - if [[ -f ~/.config/flow-cli/.setup_complete ]] && ! $force; then - echo "✓ Already setup. Use --force to run again." - return 0 - fi - - case "$1" in - --first-run) mode="interactive" ;; - --quick) mode="quick" ;; - --unattended) mode="unattended" ;; - --force) force=true ;; - esac - - _setup_welcome - _setup_verify_install - _setup_install_tools "$mode" - _setup_configure_projects "$mode" - _setup_tutorial "$mode" - _setup_complete -} -``` - ---- - -### 4. Auto-Discovery & Smart Defaults - -**Project Root Detection:** - -```bash -# Try in order: -1. $FLOW_PROJECTS_ROOT (if set) -2. ~/projects (if exists) -3. ~/dev (if exists) -4. ~/code (if exists) -5. ~ (fallback, show all subdirectories) -``` - -**Shell Integration:** - -```bash -# Auto-detect shell config file -1. ~/.zshrc (most common) -2. ~/.config/zsh/.zshrc (XDG) -3. $ZDOTDIR/.zshrc (if ZDOTDIR set) -``` - -**Package Manager Detection:** - -```bash -# Order of preference -1. brew (macOS/Linux) -2. apt-get (Debian/Ubuntu) -3. pacman (Arch) -4. dnf (Fedora) -5. yum (RHEL/CentOS) -``` - ---- - -## Implementation Plan - -### Phase 1: Install Script (Day 1, ~6 hours) - -**Tasks:** -- [ ] Create `install.sh` in repository root -- [ ] Implement OS/shell detection -- [ ] Implement install method detection -- [ ] Add Homebrew install path -- [ ] Add plugin manager install paths -- [ ] Add manual install fallback -- [ ] Add verification step -- [ ] Add rollback on failure -- [ ] Test on Docker containers -- [ ] Test on macOS (Intel + Apple Silicon) - -**Deliverable:** Working `install.sh` that works on all platforms - ---- - -### Phase 2: Enhanced `flow doctor --fix` (Day 1-2, ~4 hours) - -**Tasks:** -- [ ] Extend `commands/doctor.zsh` -- [ ] Add interactive install mode -- [ ] Add batch mode (`-y` flag) -- [ ] Implement package manager detection -- [ ] Add progress indicators -- [ ] Add verification after install -- [ ] Categorize tools (core/recommended/optional) -- [ ] Add undo support -- [ ] Write tests -- [ ] Update documentation - -**Deliverable:** `flow doctor --fix` that actually installs missing tools - ---- - -### Phase 3: First-Run Wizard (Day 2-3, ~8 hours) - -**Tasks:** -- [ ] Create `commands/setup.zsh` -- [ ] Implement wizard flow -- [ ] Add step 1: Verify install -- [ ] Add step 2: Install tools -- [ ] Add step 3: Configure projects -- [ ] Add step 4: Tutorial -- [ ] Add progress saving (resume on interrupt) -- [ ] Add `.setup_complete` marker -- [ ] Integrate with `install.sh` -- [ ] Integrate with first `work` command -- [ ] Add `--quick` mode -- [ ] Add `--unattended` mode -- [ ] Write tests -- [ ] Update documentation - -**Deliverable:** Complete first-run wizard experience - ---- - -### Phase 4: Testing & Polish (Day 3, ~4 hours) - -**Tasks:** -- [ ] E2E test: Fresh install → wizard → first command -- [ ] Test all OS combinations -- [ ] Test all install methods -- [ ] Test interruption & resume -- [ ] Test idempotency (run install.sh multiple times) -- [ ] Test unattended mode for CI -- [ ] Performance testing (install speed) -- [ ] Update all documentation -- [ ] Create installation video/GIF -- [ ] Update README with new install method - -**Deliverable:** Polished, tested, documented v4.9.0 ready for release - ---- - -## Success Metrics - -### Quantitative - -- **Install time:** < 60 seconds (fresh install to first command) -- **Setup completion rate:** > 80% complete wizard -- **Error rate:** < 5% install failures -- **Time to first win:** < 2 minutes from install - -### Qualitative - -- **User feedback:** "It just worked!" -- **Support requests:** Reduction in install-related issues -- **Adoption:** More users trying flow-cli - ---- - -## Risks & Mitigations - -| Risk | Impact | Mitigation | -|------|--------|------------| -| Install script breaks on new OS | High | Extensive Docker testing, CI matrix | -| Homebrew tap conflicts | Medium | Use unique tap name, test with other taps | -| Plugin manager conflicts | Medium | Detect existing installs, offer choices | -| Network failures during install | High | Add retry logic, offline fallback | -| Wizard too long | Low | Add `--quick` mode, make skippable | - ---- - -## Documentation Updates - -**Files to Update:** -- [ ] `README.md` - Add new install method prominently -- [ ] `docs/getting-started/installation.md` - Update with install.sh -- [ ] `docs/getting-started/quick-start.md` - Reference wizard -- [ ] `docs/commands/doctor.md` - Document `--fix` enhancements -- [ ] `docs/commands/setup.md` - New file for wizard -- [ ] `CHANGELOG.md` - Add v4.9.0 entry - -**New Documentation:** -- [ ] Installation video/GIF showing full flow -- [ ] Troubleshooting guide for install issues -- [ ] Migration guide from v4.8.x - ---- - -## Future Enhancements (v4.10.0+) - -- [ ] GUI installer for non-terminal users -- [ ] Integration with dotfiles managers -- [ ] Cloud backup setup during wizard -- [ ] Team/shared configuration setup -- [ ] Plugin marketplace during setup -- [ ] AI-powered configuration suggestions - ---- - -## References - -**Similar Projects:** -- [aiterm installation](https://github.com/Data-Wise/aiterm/blob/main/install.sh) - Good reference for one-liner -- [Oh My Zsh installer](https://ohmyz.sh/#install) - Popular curl | sh pattern -- [Homebrew installer](https://brew.sh/) - Gold standard for ease of use -- [rustup](https://rustup.rs/) - Excellent wizard UX - -**Best Practices:** -- Keep install.sh < 500 lines (maintainability) -- Use POSIX-compatible shell (sh, not bash/zsh) -- Provide clear error messages -- Make everything skippable/optional -- Test on clean systems regularly - ---- - -## Next Steps - -1. ✅ Create this spec document -2. → Review spec with user -3. → Create implementation tasks -4. → Start with Phase 1 (install.sh) -5. → Iterate based on testing - ---- - -**Status:** Ready for implementation -**Estimated Timeline:** 2-3 days -**Priority:** High ⚡ diff --git a/docs/specs/SPEC-worktree-aware-pick-2025-12-30.md b/docs/specs/SPEC-worktree-aware-pick-2025-12-30.md deleted file mode 100644 index e4534d89c..000000000 --- a/docs/specs/SPEC-worktree-aware-pick-2025-12-30.md +++ /dev/null @@ -1,393 +0,0 @@ -# SPEC: Worktree-Aware Pick - -**Status:** implemented -**Created:** 2025-12-30 - ---- - -## Overview - -Enhance the `pick` command to recognize git worktrees as a first-class category (`wt`) alongside existing categories (r, dev, teach, rs, q, app). This provides unified project navigation where parallel workspaces are instantly accessible with session status visibility and smart keybindings. - ---- - -## Primary User Story - -**As a** developer using multiple git worktrees for parallel feature work -**I want** to navigate to worktrees using the same `pick` interface I use for projects -**So that** I have a unified, fast way to switch contexts without remembering different commands - -### Acceptance Criteria - -- [ ] `pick wt` lists all worktrees across all projects in `~/.git-worktrees/` -- [ ] `pick wt ` filters to a specific project's worktrees -- [ ] Worktrees display as `project (branch) 🌳 wt` in the picker -- [ ] Session status indicator shows: 🟢 recent (< 24h) / 🟡 old / ⚪ none + time -- [ ] `cd` to worktree displays git status (branch, modified, untracked counts) -- [ ] Resume feature tracks worktrees (unified session file) -- [ ] Ctrl-W toggles between projects view and worktrees view -- [ ] Ctrl-D deletes worktree from picker (with confirmation) -- [ ] Ctrl-R resumes Claude session if worktree has one -- [ ] Ctrl-O launches Claude Code (cc) in selected directory -- [ ] Ctrl-Y launches Claude Code YOLO (ccy) in selected directory -- [ ] `--no-claude` flag disables Ctrl-O/Ctrl-Y (for use by cc dispatcher) -- [ ] `pickwt` alias added for quick access - ---- - -## Secondary User Stories - -### 2. Quick Worktree Cleanup - -**As a** developer who accumulates many worktrees -**I want** to delete stale worktrees directly from the picker -**So that** I can clean up without switching context - -**Acceptance Criteria:** -- [ ] Ctrl-D in picker triggers worktree deletion -- [ ] Confirmation prompt before deletion (unless --force) -- [ ] List refreshes after successful deletion - -### 3. Claude Session Resumption - -**As a** developer working on features across multiple worktrees -**I want** to see which worktrees have active Claude sessions -**So that** I can resume work without losing context - -**Acceptance Criteria:** -- [ ] 🟢 indicator for sessions < 24h old -- [ ] 🟡 indicator for older sessions -- [ ] ⚪ no indicator if no `.claude/` directory -- [ ] Time displayed (e.g., "2h ago", "3d ago") -- [ ] Ctrl-R triggers `claude --resume` in selected worktree - -### 4. Quick Claude Launch from Picker - -**As a** developer who frequently uses Claude Code -**I want** to launch Claude directly from the picker -**So that** I can start coding immediately without extra commands - -**Acceptance Criteria:** -- [ ] Ctrl-O navigates to selection AND launches `claude --permission-mode acceptEdits` -- [ ] Ctrl-Y navigates to selection AND launches `claude --dangerously-skip-permissions` -- [ ] Works for both projects and worktrees -- [ ] Session is saved before Claude launches - ---- - -## Technical Requirements - -### Architecture - -``` -┌────────────────────────────────────────────────────────────────┐ -│ pick command │ -├─────────────────────┬──────────────────┬───────────────────────┤ -│ Category Filter │ Query/Project │ Options │ -│ (r, dev, wt...) │ (fuzzy match) │ (--fast, -a) │ -└─────────────────────┴──────────────────┴───────────────────────┘ - │ │ │ - ▼ ▼ ▼ -┌───────────────────────────────────────────────────────────────┐ -│ _proj_list_all() │ -│ ┌─────────────┐ ┌─────────────┐ ┌────────────────────────┐ │ -│ │ Standard │ │ Standard │ │ NEW: wt category │ │ -│ │ ~/projects/ │ │ scan │ │ ~/.git-worktrees/ │ │ -│ │ directories │ │ │ │ special handling │ │ -│ └─────────────┘ └─────────────┘ └────────────────────────┘ │ -└───────────────────────────────────────────────────────────────┘ - │ - ▼ -┌───────────────────────────────────────────────────────────────┐ -│ fzf picker │ -│ ┌─────────────────────────────────────────────────────────┐ │ -│ │ flow-cli 🔧 dev │ │ -│ │ mediationverse 📦 r │ │ -│ │ scribe (live-editor) 🌳 wt 🟢 2h │ │ -│ │ stat-440 🎓 teach │ │ -│ └─────────────────────────────────────────────────────────┘ │ -│ Keybindings: │ -│ [Enter=cd] [^O=cc] [^Y=ccy] [^R=resume] [^W=wt] [^D=del] │ -└───────────────────────────────────────────────────────────────┘ - │ - ▼ -┌───────────────────────────────────────────────────────────────┐ -│ Action Handler │ -│ cd + save session + show status (for worktrees) │ -└───────────────────────────────────────────────────────────────┘ -``` - -### API Design - -N/A - No API changes; this is a ZSH function enhancement. - -### Data Models - -**Session File Enhancement** (`~/.current-project-session`): - -``` -# Current format: -name|path|timestamp - -# Enhanced format: -name|path|timestamp|type -# type = "project" | "worktree" - -# Example: -live-editor-enhancements|~/.git-worktrees/scribe/live-editor-enhancements|1735600000|worktree -flow-cli|~/projects/dev-tools/flow-cli|1735599000|project -``` - -**Backward Compatibility:** Missing `type` field defaults to "project" - -### Dependencies - -| Dependency | Required | Purpose | -|------------|----------|---------| -| fzf | Yes | Interactive picker | -| git | Yes | Worktree management | -| stat | Yes | Session age calculation | -| bat | No | Enhanced status display | - ---- - -## UI/UX Specifications - -### User Flow - -``` -User invokes pick wt - │ - ▼ -┌──────────────────────────────────┐ -│ Scan ~/.git-worktrees/*/ │ -│ for all worktree directories │ -└──────────────────────────────────┘ - │ - ▼ -┌──────────────────────────────────┐ -│ For each worktree: │ -│ - Get project name (parent dir) │ -│ - Get branch name (folder name) │ -│ - Check .claude/ for session │ -│ - Format as list entry │ -└──────────────────────────────────┘ - │ - ▼ -┌──────────────────────────────────┐ -│ Display in fzf with keybindings │ -│ User selects worktree │ -└──────────────────────────────────┘ - │ - ├──[Enter]─────────────────▶ cd + show status - ├──[Ctrl-O]────────────────▶ cd + claude (cc) - ├──[Ctrl-Y]────────────────▶ cd + claude YOLO (ccy) - ├──[Ctrl-R]────────────────▶ cd + claude --resume - ├──[Ctrl-W]────────────────▶ Toggle to projects view - ├──[Ctrl-D]────────────────▶ Delete worktree (confirm) - └──[Ctrl-C]────────────────▶ Cancel -``` - -### Wireframes (ASCII) - -**Worktree List View:** - -``` -╔════════════════════════════════════════════════════════════╗ -║ 🌳 WORKTREE PICKER ║ -╚════════════════════════════════════════════════════════════╝ - - 💡 Showing all worktrees from ~/.git-worktrees/ - [Enter] cd │ [^O] cc │ [^Y] ccy │ [^R] Resume │ [^W] Projects - -> scribe (live-editor-enh) 🌳 wt 🟢 2h ago - scribe (swiftui-native) 🌳 wt 🟡 old - scribe (wonderful-wilson) 🌳 wt ⚪ - medfit (hardcore-cerf) 🌳 wt ⚪ - rmediation (condesc-shamir) 🌳 wt 🟡 old - - 5/5 ─────────────────────────────────────────────────────── -``` - -**After Navigation:** - -``` - 📂 Changed to: ~/.git-worktrees/scribe/live-editor-enhancements - - 🌿 Branch: live-editor-enhancements - 📊 Status: 3 modified, 1 untracked -``` - -### Accessibility Checklist - -- [x] Keyboard-only navigation via fzf -- [x] Clear visual indicators (icons + colors) -- [x] Consistent with existing pick interface -- [x] Help text visible in picker header -- [ ] Screen reader support (fzf limitation) - ---- - -## Open Questions - -1. **Performance:** With 20+ worktrees, should session status be cached or computed on-demand? - - **Recommendation:** Compute on-demand; worktree count typically < 10 - -2. **Naming collision:** What if project name matches worktree branch name? - - **Recommendation:** Always show `project (branch)` format, no ambiguity - -3. **Ordering:** How should worktrees sort? - - **Recommendation:** By project name, then by recency within project - -4. **Stale worktrees:** Should stale/orphaned worktrees be hidden? - - **Recommendation:** Show with ⚠️ indicator, don't hide - -5. **RESOLVED: Claude keybinding conflict with `cc pick`** - - **Problem:** `cc pick` calls `pick && claude`, so if pick has Ctrl-O/Ctrl-Y, double launch occurs - - **Solution:** Add `--no-claude` flag to pick; cc passes this flag - - See "Claude Keybinding Context Awareness" section below - ---- - -## Review Checklist - -- [ ] Primary user story is clear and testable -- [ ] All acceptance criteria are measurable -- [ ] Architecture diagram matches implementation plan -- [ ] Data model changes are backward compatible -- [ ] Keybindings don't conflict with existing fzf bindings -- [ ] Documentation locations identified -- [ ] Test cases defined (see below) - ---- - -## Implementation Notes - -### Key Implementation Details - -1. **Special category handling:** The `wt` category needs custom logic in `_proj_list_all()` since it scans `$FLOW_WORKTREE_DIR` instead of `$PROJ_BASE`. - -2. **Session status calculation:** Use `find` with `-mtime` to check for recent `.claude/*.json` files. - -3. **Keybinding gotchas:** - - Ctrl-C cannot be rebound (always cancels) - - Ctrl-W may conflict on some terminals (word delete) - - Ctrl-O opens file in some terminals (but usually safe in fzf) - - Ctrl-Y is paste in some terminals (but usually safe in fzf) - - Use `--bind` with `reload` for toggle functionality - - Use `execute-silent` + `accept` for actions that exit picker - -### Claude Keybinding Context Awareness - -**Problem:** `cc pick` and `cc yolo pick` already launch Claude after pick completes: - -```zsh -# In cc-dispatcher.zsh: -pick "$@" && claude --permission-mode acceptEdits # cc pick -pick "$@" && claude --dangerously-skip-permissions # cc yolo pick -``` - -If pick has Ctrl-O/Ctrl-Y keybindings, pressing them would cause double Claude launch. - -**Solution:** Add `--no-claude` flag to pick that disables Ctrl-O/Ctrl-Y keybindings. - -```zsh -# pick() changes: -pick() { - local no_claude_keys=0 - - # Parse --no-claude flag - if [[ "$1" == "--no-claude" ]]; then - no_claude_keys=1 - shift - fi - - # ... existing argument parsing ... - - # Build fzf keybindings conditionally - local claude_bindings="" - if [[ $no_claude_keys -eq 0 ]]; then - claude_bindings='--bind=ctrl-o:execute-silent(echo cc > '$action_file')+accept' - claude_bindings+=' --bind=ctrl-y:execute-silent(echo ccy > '$action_file')+accept' - fi - - # ... fzf call with $claude_bindings ... -} -``` - -```zsh -# cc-dispatcher.zsh changes: -# Before: -pick "$@" && claude --permission-mode acceptEdits - -# After: -pick --no-claude "$@" && claude --permission-mode acceptEdits -``` - -**Behavior Matrix:** - -| Invocation | Ctrl-O/Ctrl-Y | Result | -|------------|---------------|--------| -| `pick` | Enabled | cd + launch Claude | -| `pick wt` | Enabled | cd + launch Claude | -| `cc pick` | Disabled | cd only (cc handles Claude) | -| `cc yolo pick` | Disabled | cd only (cc handles Claude) | -| `cc wt pick` | Disabled | cd only (cc handles Claude) | - -**Header changes based on context:** - -``` -# Normal pick: -[Enter] cd │ [^O] cc │ [^Y] ccy │ [^R] Resume - -# pick --no-claude (called from cc): -[Enter] cd │ [^S] Status │ [^L] Log │ [^W] Worktrees -``` - -1. **Format string:** Use printf with fixed widths for alignment: - - ```zsh - printf "%-25s %-5s %-3s %s\n" "$name" "$icon" "$type" "$session" - ``` - -### Test Cases - -| Test | Input | Expected | -|------|-------|----------| -| List all worktrees | `pick wt` | Shows all worktrees from ~/.git-worktrees | -| Filter by project | `pick wt scribe` | Shows only scribe's worktrees | -| No worktrees | `pick wt` (empty dir) | Shows "No worktrees found" message | -| Direct jump | `pick live-editor` | Navigates to matching worktree | -| Session indicator | worktree with .claude/ | Shows 🟢/🟡 with time | -| Delete worktree | Ctrl-D on selection | Confirms and removes | -| Toggle view | Ctrl-W in picker | Switches to project list | -| Launch Claude | Ctrl-O on selection | cd + launches `claude --permission-mode acceptEdits` | -| Launch Claude YOLO | Ctrl-Y on selection | cd + launches `claude --dangerously-skip-permissions` | -| Resume Claude | Ctrl-R on selection | cd + launches `claude --resume` | -| No Claude keys | `pick --no-claude` | Ctrl-O/Ctrl-Y bindings not present | -| cc pick integration | `cc pick` | pick called with --no-claude, Claude launched by cc | - -### Files to Modify - -| File | Changes | Priority | -|------|---------|----------| -| `commands/pick.zsh` | Add wt category, worktree lister, keybindings, `--no-claude` flag | P0 | -| `lib/dispatchers/cc-dispatcher.zsh` | Pass `--no-claude` to all `pick` calls | P0 | -| `completions/_pick` | Add `wt` to category completions, `--no-claude` option | P1 | -| `do../reference/MASTER-DISPATCHER-GUIDE.md` | Document `pick wt` | P1 | -| `CLAUDE.md` | Update Quick Reference section | P2 | -| `docs/tutorials/` | Add worktree navigation tutorial | P2 | - ---- - -## History - -| Date | Change | -|------|--------| -| 2025-12-30 | Initial spec from brainstorm session | -| 2025-12-30 | Added Ctrl-O (cc) and Ctrl-Y (ccy) keybindings for Claude launch | -| 2025-12-30 | Added `--no-claude` flag to prevent double-launch when called from cc dispatcher | - ---- - -*Captured from /workflow:brainstorm deep feat save* diff --git a/docs/specs/SPEC-wt-workflow-enhancement-2026-01-17.md b/docs/specs/SPEC-wt-workflow-enhancement-2026-01-17.md deleted file mode 100644 index 9b38de0db..000000000 --- a/docs/specs/SPEC-wt-workflow-enhancement-2026-01-17.md +++ /dev/null @@ -1,310 +0,0 @@ -# SPEC: WT Workflow Enhancement - -**Feature:** Enhanced worktree listing and pick wt delete/update actions -**Status:** Draft -**Created:** 2026-01-17 -**From Brainstorm:** Deep interactive session -**Target Release:** flow-cli v5.13.0 or v5.14.0 -**Estimated Effort:** 6-8 hours across 3 phases - ---- - -## Metadata - -| Field | Value | -|-------|-------| -| **Status** | Draft | -| **Priority** | Medium (improves daily workflow) | -| **Complexity** | Medium (6-8 hours) | -| **Risk Level** | Low (enhances existing commands) | -| **Dependencies** | fzf 0.40+, existing wt-dispatcher, pick.zsh | -| **Target Users** | Developers using worktrees for parallel development | -| **Branch Strategy** | feature/wt-enhancement → dev → main | - ---- - -## Overview - -Enhance the `wt` workflow with: - -1. **Better `wt` default** - Formatted list with status icons and session indicators (replacing `cd + ls`) -2. **`pick wt` delete action** - Multi-select deletion with preview confirmation and optional branch cleanup -3. **`pick wt` update action** - Cache refresh with immediate formatted output -4. **Filter support** - `wt ` to filter worktrees by project name - ---- - -## User Stories - -### Primary Story: Quick Worktree Overview - -**As a** developer working with multiple worktrees -**I want to** see a formatted overview when I type `wt` -**So that I** can quickly assess worktree status without navigating away - -### Acceptance Criteria - -- [ ] `wt` (no args) shows formatted table with branch, status, session, path -- [ ] Status icons: ✅ active, 🧹 merged, ⚠️ stale, 🏠 main -- [ ] Session indicators: 🟢 active, 🟡 recent, ⚪ none -- [ ] `wt ` filters to show only that project's worktrees -- [ ] Output fits terminal width gracefully - -### Secondary Stories - -**Story 2: Batch Worktree Cleanup** -- As a developer with many merged worktrees -- I want to delete multiple worktrees at once with confirmation -- So that I can clean up efficiently without repetitive commands - -**Story 3: Cache Refresh** -- As a developer who just created/removed worktrees -- I want to refresh the pick cache and see the updated list -- So that pick wt shows accurate information immediately - ---- - -## Architecture - -### Component Flow - -``` -┌─────────────────────────────────────────────────────────────────┐ -│ User: wt │ -└─────────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────┐ -│ wt-dispatcher.zsh │ -│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ -│ │ wt (no args) │ │ wt │ │ wt list/status │ │ -│ │ → _wt_overview()│ │ → _wt_overview │ │ → existing │ │ -│ │ NEW │ │ + filter │ │ │ │ -│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ -└─────────────────────────────────────────────────────────────────┘ - -┌─────────────────────────────────────────────────────────────────┐ -│ User: pick wt + ctrl-x (delete) │ -└─────────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────┐ -│ pick.zsh │ -│ ┌─────────────────────────────────────────────────────────────┐ │ -│ │ _pick_wt_with_actions() NEW │ │ -│ │ - fzf with --multi --bind 'ctrl-x:...,ctrl-r:...' │ │ -│ │ - Preview pane shows worktree details │ │ -│ │ - ctrl-x: execute delete flow │ │ -│ │ - ctrl-r: execute refresh flow │ │ -│ └─────────────────────────────────────────────────────────────┘ │ -│ │ │ -│ ▼ │ -│ ┌─────────────────────────────────────────────────────────────┐ │ -│ │ _pick_wt_delete() NEW │ │ -│ │ - Confirm each worktree in preview │ │ -│ │ - Ask: "Also delete branch? [y/N]" │ │ -│ │ - Execute git worktree remove │ │ -│ │ - Optionally git branch -d │ │ -│ │ - Invalidate cache │ │ -│ └─────────────────────────────────────────────────────────────┘ │ -└─────────────────────────────────────────────────────────────────┘ -``` - ---- - -## API Design - -### New/Modified Commands - -| Command | Current | New Behavior | -|---------|---------|--------------| -| `wt` | `cd + ls -la` | Formatted overview table | -| `wt ` | N/A | Filtered overview (e.g., `wt flow`) | -| `wt list` | `git worktree list` | No change (raw git output) | -| `wt status` | Full status view | No change | -| `pick wt` | Select → cd | Select → cd, OR ctrl-x delete, OR ctrl-r refresh | - -### Keybindings for `pick wt` - -| Key | Action | Description | -|-----|--------|-------------| -| `Enter` | Navigate | cd to selected worktree (existing) | -| `Tab` | Multi-select | Toggle selection for batch operations | -| `ctrl-x` | Delete | Delete selected worktree(s) with confirmation | -| `ctrl-r` | Refresh | Refresh cache and show formatted `wt` list | -| `ctrl-c` / `Esc` | Cancel | Exit picker | - ---- - -## Data Models - -### Worktree Display Format - -``` -🌳 Worktrees (3 total) -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - BRANCH STATUS SESSION PATH - ────────────────────────── ──────────── ───────── ───────────────── - main 🏠 main 🟢 ~/projects/flow-cli - feature/teaching-flags ✅ active 🟡 ~/.git-worktrees/flow-cli/... - feature/old-feature 🧹 merged ⚪ ~/.git-worktrees/flow-cli/... - -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -💡 Tip: wt to filter | pick wt for interactive -``` - -### Session Status Detection - -| Indicator | Meaning | Detection | -|-----------|---------|-----------| -| 🟢 | Active Claude session | `.claude/` exists + recent activity | -| 🟡 | Recent session (< 24h) | Session file mtime < 24h | -| ⚪ | No session | No `.claude/` or old session | - ---- - -## Dependencies - -| Dependency | Version | Purpose | -|------------|---------|---------| -| fzf | 0.40+ | Multi-select, keybindings, preview | -| git | 2.30+ | Worktree commands | - ---- - -## UI/UX Specifications - -### Delete Flow (ctrl-x in pick wt) - -``` -┌─────────────────────────────────────────────────────────────────┐ -│ Selected for deletion: │ -│ │ -│ 1. feature/old-feature ~/.git-worktrees/flow-cli/feature-...│ -│ 2. bugfix/fixed-issue ~/.git-worktrees/flow-cli/bugfix-... │ -│ │ -├─────────────────────────────────────────────────────────────────┤ -│ Delete worktree 1/2: feature/old-feature? │ -│ │ -│ [y] Yes, delete worktree │ -│ [n] No, skip this one │ -│ [a] Yes to all remaining │ -│ [q] Quit (cancel all) │ -│ │ -│ Your choice: _ │ -└─────────────────────────────────────────────────────────────────┘ -``` - -After each worktree deletion: - -``` -✓ Removed worktree: ~/.git-worktrees/flow-cli/feature-old-feature - -Also delete branch 'feature/old-feature'? [y/N]: _ -``` - -### Refresh Flow (ctrl-r in pick wt) - -``` -⟳ Refreshing worktree cache... -✓ Cache cleared - -🌳 Worktrees (3 total) -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -[shows formatted wt output] -``` - -### Accessibility - -- [ ] All actions have keyboard shortcuts (no mouse required) -- [ ] Color-coded status has text fallback (✅/🧹/⚠️ icons) -- [ ] Confirmation prompts are clear and unambiguous -- [ ] Error messages explain recovery steps - ---- - -## Open Questions - -1. **Should `wt` also cache its output?** Currently `wt status` rescans every time. Could be slow for many worktrees. -2. **Should ctrl-x work from main picker or require explicit `pick wt delete` subcommand?** Keybinding in main picker is more discoverable. - ---- - -## Implementation Plan - -### Phase 1: Enhanced `wt` Default (2h) - -- [ ] Create `_wt_overview()` function in wt-dispatcher.zsh -- [ ] Add session indicator detection (reuse from pick.zsh) -- [ ] Add filter argument support (`wt `) -- [ ] Update `wt` case to call `_wt_overview()` when no args -- [ ] Add help text mentioning filter and pick wt - -### Phase 2: pick wt Actions (3-4h) - -- [ ] Add fzf keybindings: `--bind 'ctrl-x:...,ctrl-r:...'` -- [ ] Implement `_pick_wt_delete()` with confirmation flow -- [ ] Implement branch deletion prompt after worktree removal -- [ ] Implement `_pick_wt_refresh()` calling cache invalidate + wt -- [ ] Add multi-select support with Tab -- [ ] Update preview pane to show action hints - -### Phase 3: Testing & Polish (1-2h) - -- [ ] Add tests for `wt` with and without filter -- [ ] Add tests for delete flow (mock confirmation) -- [ ] Add tests for refresh flow -- [ ] Update help system with new keybindings -- [ ] Update WT-DISPATCHER-REFERENCE.md -- [ ] Update ARCHITECTURE-DIAGRAMS.md with new flows - ---- - -## Testing Strategy - -### Unit Tests - -```bash -test_wt_overview_no_filter() -test_wt_overview_with_filter() -test_wt_overview_empty_worktrees() -test_wt_session_detection() -``` - -### Integration Tests - -```bash -test_pick_wt_delete_single() -test_pick_wt_delete_multi() -test_pick_wt_delete_with_branch() -test_pick_wt_refresh() -``` - ---- - -## Review Checklist - -- [ ] Backward compatible with existing `wt` commands -- [ ] All new keybindings documented in help -- [ ] Delete confirmation is safe (no accidental deletion) -- [ ] Session indicators match pick wt behavior -- [ ] Filter argument works with all project names -- [ ] Tests cover happy path and error cases -- [ ] Documentation updated - ---- - -## History - -| Date | Change | Author | -|------|--------|--------| -| 2026-01-17 | Initial spec from deep brainstorm | Claude + DT | - ---- - -## Related Documents - -- [WT-DISPATCHER-REFERENCE.md](../reference/MASTER-DISPATCHER-GUIDE.md#wt-dispatcher) -- [PICK-COMMAND-REFERENCE.md](../reference/MASTER-DISPATCHER-GUIDE.md) -- [ARCHITECTURE-DIAGRAMS.md](../diagrams/ARCHITECTURE-DIAGRAMS.md) diff --git a/docs/teaching/index.md b/docs/teaching/index.md index 11a35f3e9..f1dedd758 100644 --- a/docs/teaching/index.md +++ b/docs/teaching/index.md @@ -72,10 +72,11 @@ The flow-cli teaching workflow provides a complete solution for managing Quarto- ### Key Capabilities -1. **Fast Deployment** (< 2 minutes) - - Branch-based draft/production workflow - - Preview changes before publishing - - Automated GitHub Pages deployment +1. **Fast Deployment** (8-15 seconds with direct mode) + - Direct merge: `teach deploy -d` (8-15s, no PR) + - PR workflow: `teach deploy` (45-90s, review + checks) + - Deploy history tracking and safe rollback + - Preview with `--dry-run` before deploying 2. **Health Monitoring** - Dependency verification (`teach doctor`) @@ -136,7 +137,9 @@ teach exam "Topic Name" teach quiz "Topic Name" teach slides "Topic Name" -# Deploy to production +# Deploy to production (fast direct mode) +teach deploy -d +# Or via PR workflow teach deploy ``` @@ -224,5 +227,5 @@ teach plan help --- -**Last Updated:** 2026-01-31 -**Version:** v6.2.0 +**Last Updated:** 2026-02-04 +**Version:** v6.4.0 diff --git a/docs/tutorials/14-teach-dispatcher.md b/docs/tutorials/14-teach-dispatcher.md index 772f7f10f..2941e3de2 100644 --- a/docs/tutorials/14-teach-dispatcher.md +++ b/docs/tutorials/14-teach-dispatcher.md @@ -40,7 +40,7 @@ By the end of this tutorial, you will: 1. Initialize a teaching workflow for your course 2. Understand config validation and fix common errors -3. Deploy changes to production in < 2 minutes +3. Deploy changes to production in seconds (`-d`) or via PR 4. Use the branch-based draft/production workflow 5. Generate teaching content with Scholar integration 6. Archive semesters for future reference @@ -250,7 +250,7 @@ teach deploy 2. Merges `draft` → `production` 3. Pushes to GitHub 4. GitHub Actions builds the site -5. Students see updates in < 2 minutes +5. Students see updates in seconds (direct) or < 2 minutes (PR) ### Step 4.4: Check Current Week diff --git a/docs/tutorials/31-teach-deploy-v2.md b/docs/tutorials/31-teach-deploy-v2.md new file mode 100644 index 000000000..fc256ec57 --- /dev/null +++ b/docs/tutorials/31-teach-deploy-v2.md @@ -0,0 +1,338 @@ +--- +tags: + - tutorial + - teaching + - deploy +--- + +# Tutorial 31: Fast Deployments with teach deploy v2 + +> Learn to deploy your course website in seconds instead of minutes. + +## What You'll Learn + +- Deploy directly to production without PRs (8-15 seconds) +- Use smart auto-generated commit messages +- Preview changes before deploying +- View deployment history +- Rollback problematic deployments +- Run deployments in CI/automation + +![teach deploy v2 Demo](../demos/tutorials/tutorial-teach-deploy.gif) + +## Prerequisites + +- flow-cli v6.4.0+ +- Git, yq, and gh CLI installed +- A course project with `.flow/teach-config.yml` +- Draft and production branches configured (e.g., `dev` and `gh-pages`) + +## Step 1: Your First Direct Deploy + +The traditional PR workflow takes 45-90 seconds: + +```bash +# The old way (slow) +teach deploy +``` + +This creates a PR, waits for GitHub checks, merges, and deploys. For quick updates, this is overkill. + +Try the new direct deploy: + +```bash +# The new way (fast - 8-15 seconds) +teach deploy --direct +``` + +Expected output: + +``` +🚀 Direct Deploy to gh-pages + +Changes to deploy: + M lectures/week-05.qmd + M _quarto.yml + +Commit message: content: week-05 lecture + +[✓] All safety checks passed +[✓] Pushed to gh-pages +[✓] Deployment recorded + +🎉 Deploy complete in 12s +``` + +Use `-d` as a shortcut for `--direct`. + +## Step 2: Smart Commit Messages + +Deploy automatically generates commit messages from your file paths: + +Edit a lecture file: + +```bash +# After editing lectures/week-05.qmd +teach deploy -d +# → "content: week-05 lecture" +``` + +Edit a config file: + +```bash +# After editing _quarto.yml +teach deploy -d +# → "config: quarto settings" +``` + +Edit multiple files: + +```bash +# After editing 3 different files +teach deploy -d +# → "deploy: 3 file updates" +``` + +Override with a custom message: + +```bash +teach deploy -d -m "Fix typo in regression notes" +# → Uses your custom message +``` + +## Step 3: Preview with Dry-Run + +Always preview before deploying to production: + +```bash +teach deploy --dry-run --direct +``` + +Expected output: + +``` +🔍 Deploy Preview (DRY RUN) + +Changes to deploy: + M lectures/week-05.qmd + M lectures/week-06.qmd + +Commit message: deploy: 2 file updates +Target branch: gh-pages + +Would execute: + 1. git push origin gh-pages + 2. Record deployment history + 3. Trigger GitHub Pages + +🔹 No changes made (dry-run mode) +``` + +This shows exactly what would happen without actually deploying. + +## Step 4: Deploy History + +View your past deployments: + +```bash +teach deploy --history +``` + +Output: + +``` +Recent Deployments +───────────────────────────────────────────────────────── + + # Date Commit Message Files Type + ───────────────────────────────────────────────────────────────────────── + 1 2026-02-03 14:23 a3f8d92 content: week-05 lecture 1 direct + 2 2026-02-03 10:15 b2e4c81 config: quarto settings 1 direct + 3 2026-02-02 16:30 c9a1f45 deploy: 3 file updates 3 direct + 4 2026-02-02 09:00 d8b2c33 Weekly content update 12 pr + +Recent: 4 deploys (3 direct, 1 PR) | Avg time: 11s +``` + +Limit results: + +```bash +teach deploy --history --limit 10 +``` + +View specific deployment: + +```bash +teach deploy --history --show 1 +``` + +The history is stored in `.flow/deploy-history.yml`. + +## Step 5: Rollback a Deployment + +Made a mistake? Roll back to the previous state: + +```bash +teach deploy --rollback 1 +``` + +This performs a **forward rollback** using `git revert`: + +``` +🔄 Rolling back deployment #1 + +Previous state: + Commit: a3f8d92 + Message: content: week-05 lecture + Date: 2026-02-03 14:23 + +Creating revert commit... +[✓] Reverted a3f8d92 +[✓] Pushed to gh-pages +[✓] Rollback recorded in history + +🎉 Rollback complete +``` + +Safety guarantees: + +- ✅ **Non-destructive** - Creates new revert commit (preserves history) +- ✅ **Traceable** - Rollback recorded in deploy history +- ✅ **Safe for collaboration** - Works even if others have pulled + +Roll back an older deployment: + +```bash +teach deploy --rollback 3 +# Reverts deployment #3 from history (use --history to see index) +``` + +## Step 6: CI Mode for Automation + +Run deployments in scripts or CI without interactive prompts: + +```bash +teach deploy --ci -d -m "Automated weekly deploy" +``` + +The `--ci` flag: + +- ✅ Disables all interactive prompts +- ✅ Auto-detects TTY (no manual flag needed in CI) +- ✅ Exits with proper status codes (0 = success, 1 = failure) + +Example GitHub Actions workflow: + +```yaml +- name: Deploy course website + run: teach deploy --ci -d -m "Automated deploy from CI" +``` + +## Step 7: Combining Flags + +Practical flag combinations for common workflows: + +Quick deploy with automatic tagging: + +```bash +teach deploy -d --auto-tag +# Deploys and creates a git tag (v1.2.0, v1.2.1, etc.) +``` + +CI direct deploy with custom message: + +```bash +teach deploy --ci -d -m "Weekly content update" +# Non-interactive, direct, custom message +``` + +Preview partial deploy: + +```bash +teach deploy --dry-run lectures/week-05.qmd +# Preview deploying just one file +``` + +Safe production deploy: + +```bash +teach deploy --dry-run -d && teach deploy -d +# Preview first, then deploy +``` + +## Step 8: Understanding Deploy Modes + +Deploy v2 supports two modes: + +### Direct Mode (Fast - 8-15s) + +```bash +teach deploy --direct +``` + +- ✅ No PR created +- ✅ Direct push to production branch +- ✅ Minimal GitHub API calls +- ⚠️ Use for: quick fixes, content updates, trusted changes + +### PR Mode (Safe - 45-90s) + +```bash +teach deploy +``` + +- ✅ Creates PR for review +- ✅ Runs GitHub checks +- ✅ Audit trail +- ⚠️ Use for: major changes, breaking updates, collaborative courses + +Choose based on your needs: + +| Scenario | Mode | Command | +|----------|------|---------| +| Fix typo | Direct | `teach deploy -d -m "Fix typo"` | +| Weekly update | Direct | `teach deploy -d` | +| New semester | PR | `teach deploy` | +| Major redesign | PR | `teach deploy` | + +## What You Learned + +You now know how to: + +1. ✅ Deploy in 8-15 seconds with `--direct` +2. ✅ Use smart auto-generated commit messages +3. ✅ Preview changes with `--dry-run` +4. ✅ View deployment history with `--history` +5. ✅ Rollback problematic deployments with `--rollback` +6. ✅ Automate deployments with `--ci` +7. ✅ Combine flags for powerful workflows +8. ✅ Choose between direct and PR modes + +## Tips + +- **Preview first.** Use `--dry-run` before deploying to production. +- **Check history.** Use `--history` to track what you've deployed. +- **Rollback safely.** Use `--rollback` instead of manual git reverts. +- **Automate wisely.** Use `--ci` for scripts, but keep `--direct` for manual deploys. + +## Quick Reference + +```bash +teach dep -d # Fast direct deploy +teach dep --dry-run # Preview changes +teach dep --history # View past deploys +teach dep --rollback 1 # Undo last deploy +teach dep --ci -d # CI mode +teach dep -d -m "msg" # Custom message +``` + +## Next Steps + +- See [REFCARD-DEPLOY-V2.md](../reference/REFCARD-DEPLOY-V2.md) for complete flag reference +- Read [TEACH-DEPLOY-GUIDE.md](../guides/TEACH-DEPLOY-GUIDE.md) for advanced workflows +- Try `teach deploy --history` to track your deployments +- Explore `teach deploy --rollback` for safe recovery + +--- + +*v6.4.0 - teach deploy v2 command* diff --git a/flow.plugin.zsh b/flow.plugin.zsh index 5ed79f6d0..d2d705c99 100644 --- a/flow.plugin.zsh +++ b/flow.plugin.zsh @@ -134,7 +134,7 @@ _flow_plugin_init # Export loaded marker export FLOW_PLUGIN_LOADED=1 -export FLOW_VERSION="6.1.0" +export FLOW_VERSION="6.4.0" # Register exit hook for plugin cleanup add-zsh-hook zshexit _flow_plugin_cleanup diff --git a/lib/deploy-history-helpers.zsh b/lib/deploy-history-helpers.zsh new file mode 100644 index 000000000..60879757e --- /dev/null +++ b/lib/deploy-history-helpers.zsh @@ -0,0 +1,189 @@ +#!/usr/bin/env zsh +# deploy-history-helpers.zsh - Append-only YAML deploy history tracking +# +# Provides functions for recording and querying deployment history +# stored at .flow/deploy-history.yml within a teaching course repo. +# +# Design decisions: +# - Append-only writes (>>) for _deploy_history_append — never rewrites the file +# - yq used only for READING (list, get, count) +# - History file is git-tracked +# - Timestamps in ISO 8601 with timezone +# - Commit hashes truncated to 8 characters +# +# Functions: +# _deploy_history_append - Record a new deploy entry +# _deploy_history_list - Display recent deploys as a formatted table +# _deploy_history_get - Retrieve a specific entry by display index +# _deploy_history_count - Return total number of recorded deploys + +# --- Append ----------------------------------------------------------- + +# Append deploy entry to history file +# Usage: _deploy_history_append [pr_number] [tag] [duration] +_deploy_history_append() { + local mode="$1" + local commit_hash="$2" + local commit_before="$3" + local branch_from="$4" + local branch_to="$5" + local file_count="${6:-0}" + local commit_message="$7" + local pr_number="${8:-null}" + local tag="${9:-null}" + local duration="${10:-0}" + + local history_file=".flow/deploy-history.yml" + local timestamp + timestamp=$(date '+%Y-%m-%dT%H:%M:%S%z') + local user + user=$(whoami) + + # Initialise file with top-level key when it doesn't exist yet + if [[ ! -f "$history_file" ]]; then + mkdir -p .flow + echo "deploys:" > "$history_file" + fi + + # Escape single quotes in all string fields so YAML stays valid + local safe_message="${commit_message//\'/\'\'}" + local safe_mode="${mode//\'/\'\'}" + local safe_branch_from="${branch_from//\'/\'\'}" + local safe_branch_to="${branch_to//\'/\'\'}" + local safe_user="${user//\'/\'\'}" + + # Append entry using heredoc — no yq rewrite + cat >> "$history_file" << EOF + - timestamp: '${timestamp}' + mode: '${safe_mode}' + commit_hash: '${commit_hash:0:8}' + commit_before: '${commit_before:0:8}' + branch_from: '${safe_branch_from}' + branch_to: '${safe_branch_to}' + file_count: ${file_count} + commit_message: '${safe_message}' + pr_number: ${pr_number} + tag: ${tag} + user: '${safe_user}' + duration_seconds: ${duration} +EOF + + return 0 +} + +# --- List ------------------------------------------------------------- + +# List recent deployments from history +# Usage: _deploy_history_list [count] +# Output: Formatted table of recent deploys +_deploy_history_list() { + local count="${1:-5}" + local history_file=".flow/deploy-history.yml" + + if [[ ! -f "$history_file" ]]; then + echo " No deployment history found." + echo " Deploy with 'teach deploy' to start tracking." + return 1 + fi + + local total_deploys + total_deploys=$(yq '.deploys | length' "$history_file" 2>/dev/null) + + if [[ -z "$total_deploys" || "$total_deploys" -eq 0 ]]; then + echo " No deployments recorded." + return 1 + fi + + echo "" + echo " Recent deployments:" + echo "" + printf " %-4s %-18s %-8s %-6s %s\n" "#" "When" "Mode" "Files" "Message" + printf " %-4s %-18s %-8s %-6s %s\n" "---" "------------------" "--------" "------" "-------" + + # Walk in reverse order (most recent first), capped at $count + local start_idx=$(( total_deploys - 1 )) + local end_idx=$(( total_deploys - count )) + [[ $end_idx -lt 0 ]] && end_idx=0 + + local display_num=1 + for (( i = start_idx; i >= end_idx; i-- )); do + local ts mode files msg + ts=$(yq ".deploys[$i].timestamp" "$history_file" 2>/dev/null) + mode=$(yq ".deploys[$i].mode" "$history_file" 2>/dev/null) + files=$(yq ".deploys[$i].file_count" "$history_file" 2>/dev/null) + msg=$(yq ".deploys[$i].commit_message" "$history_file" 2>/dev/null) + + # Shorten timestamp: "2026-02-03T14:30" -> "2026-02-03 14:30" + local short_ts="${ts:0:16}" + short_ts="${short_ts//T/ }" + + # Truncate long messages + [[ ${#msg} -gt 40 ]] && msg="${msg:0:37}..." + + printf " %-4s %-18s %-8s %-6s %s\n" "$display_num" "$short_ts" "$mode" "$files" "$msg" + (( display_num++ )) + done + + echo "" + return 0 +} + +# --- Get -------------------------------------------------------------- + +# Get deploy entry by display index (1 = most recent) +# Usage: _deploy_history_get +# Output: Sets DEPLOY_HIST_* variables for the caller +_deploy_history_get() { + local display_idx="$1" + local history_file=".flow/deploy-history.yml" + + if [[ ! -f "$history_file" ]]; then + return 1 + fi + + local total_deploys + total_deploys=$(yq '.deploys | length' "$history_file" 2>/dev/null) + + if [[ -z "$total_deploys" || "$total_deploys" -eq 0 ]]; then + return 1 + fi + + # Convert display index (1 = newest) to zero-based array index + local array_idx=$(( total_deploys - display_idx )) + + if [[ $array_idx -lt 0 || $array_idx -ge $total_deploys ]]; then + return 1 + fi + + # Export entry fields into caller's scope + DEPLOY_HIST_TIMESTAMP=$(yq ".deploys[$array_idx].timestamp" "$history_file" 2>/dev/null) + DEPLOY_HIST_MODE=$(yq ".deploys[$array_idx].mode" "$history_file" 2>/dev/null) + DEPLOY_HIST_COMMIT=$(yq ".deploys[$array_idx].commit_hash" "$history_file" 2>/dev/null) + DEPLOY_HIST_COMMIT_BEFORE=$(yq ".deploys[$array_idx].commit_before" "$history_file" 2>/dev/null) + DEPLOY_HIST_BRANCH_FROM=$(yq ".deploys[$array_idx].branch_from" "$history_file" 2>/dev/null) + DEPLOY_HIST_BRANCH_TO=$(yq ".deploys[$array_idx].branch_to" "$history_file" 2>/dev/null) + DEPLOY_HIST_FILE_COUNT=$(yq ".deploys[$array_idx].file_count" "$history_file" 2>/dev/null) + DEPLOY_HIST_MESSAGE=$(yq ".deploys[$array_idx].commit_message" "$history_file" 2>/dev/null) + DEPLOY_HIST_PR=$(yq ".deploys[$array_idx].pr_number" "$history_file" 2>/dev/null) + DEPLOY_HIST_TAG=$(yq ".deploys[$array_idx].tag" "$history_file" 2>/dev/null) + DEPLOY_HIST_USER=$(yq ".deploys[$array_idx].user" "$history_file" 2>/dev/null) + DEPLOY_HIST_DURATION=$(yq ".deploys[$array_idx].duration_seconds" "$history_file" 2>/dev/null) + + return 0 +} + +# --- Count ------------------------------------------------------------ + +# Get total deploy count +# Usage: _deploy_history_count +# Output: Prints the count to stdout +_deploy_history_count() { + local history_file=".flow/deploy-history.yml" + + if [[ ! -f "$history_file" ]]; then + echo "0" + return + fi + + yq '.deploys | length' "$history_file" 2>/dev/null || echo "0" +} diff --git a/lib/deploy-rollback-helpers.zsh b/lib/deploy-rollback-helpers.zsh new file mode 100644 index 000000000..e66eed78e --- /dev/null +++ b/lib/deploy-rollback-helpers.zsh @@ -0,0 +1,229 @@ +#!/usr/bin/env zsh +# +# Deploy Rollback Helpers (teach deploy v2) +# Purpose: Forward rollback via git revert with history tracking +# +# Design decisions: +# - Forward rollback only (git revert) — never destructive git reset +# - Rollback of a PR deploy pushes a direct revert commit (not a revert PR) +# - Rollback is recorded in deploy history with mode "rollback" +# - CI mode requires explicit index (no interactive picker) +# - On revert conflict, stays on target branch for manual resolution +# +# Functions: +# _deploy_rollback - Main rollback with interactive picker +# _deploy_perform_rollback - Execute forward rollback via git revert + +# ============================================================================ +# MAIN ROLLBACK FUNCTION +# ============================================================================ + +# Rollback a deployment by reverting its commit on production +# Usage: _deploy_rollback [N] [--ci] +# N = display index from history (1 = most recent). If omitted, shows interactive picker. +_deploy_rollback() { + local target_idx="" + local ci_mode=false + + # Parse flags + while [[ $# -gt 0 ]]; do + case "$1" in + --ci) ci_mode=true; shift ;; + [0-9]*) target_idx="$1"; shift ;; + *) shift ;; + esac + done + + # Source history helpers if not loaded + if ! typeset -f _deploy_history_list >/dev/null 2>&1; then + local helper_path="${0:A:h}/deploy-history-helpers.zsh" + if [[ -f "$helper_path" ]]; then + source "$helper_path" + else + _teach_error "Deploy history helpers not found" + return 1 + fi + fi + + # Check history exists + local total=$(_deploy_history_count) + if [[ "$total" -eq 0 ]]; then + echo "" + echo "${FLOW_COLORS[warn]} No deployment history found${FLOW_COLORS[reset]}" + echo " Deploy first with 'teach deploy' to build history." + return 1 + fi + + # If no target specified, show interactive picker + if [[ -z "$target_idx" ]]; then + if [[ "$ci_mode" == "true" ]]; then + _teach_error "CI mode requires explicit rollback index: teach deploy --rollback 1" + return 1 + fi + + _deploy_history_list 5 + + echo -n "${FLOW_COLORS[prompt]} Rollback which deployment? [1]: ${FLOW_COLORS[reset]}" + read -r target_idx + [[ -z "$target_idx" ]] && target_idx=1 + fi + + # Validate index + if [[ ! "$target_idx" =~ ^[0-9]+$ ]] || [[ "$target_idx" -lt 1 ]] || [[ "$target_idx" -gt "$total" ]]; then + _teach_error "Invalid deployment index: $target_idx" \ + "Use a number between 1 and $total" + return 1 + fi + + # Get deploy entry + if ! _deploy_history_get "$target_idx"; then + _teach_error "Failed to read deployment #$target_idx" + return 1 + fi + + # Show what we're rolling back + echo "" + echo "${FLOW_COLORS[info]} Rollback Target${FLOW_COLORS[reset]}" + echo "${FLOW_COLORS[dim]}─────────────────────────────────────────────────${FLOW_COLORS[reset]}" + echo " Deploy: #$target_idx" + echo " Mode: $DEPLOY_HIST_MODE" + echo " Commit: $DEPLOY_HIST_COMMIT" + echo " Message: $DEPLOY_HIST_MESSAGE" + echo " When: $DEPLOY_HIST_TIMESTAMP" + echo "" + + # Confirm (unless CI mode) + if [[ "$ci_mode" != "true" ]]; then + echo -n "${FLOW_COLORS[prompt]} Proceed with rollback? [y/N]: ${FLOW_COLORS[reset]}" + read -r confirm + case "$confirm" in + y|Y|yes|Yes|YES) ;; + *) echo " Rollback cancelled."; return 1 ;; + esac + fi + + # Perform the rollback + _deploy_perform_rollback "$DEPLOY_HIST_COMMIT" "$DEPLOY_HIST_BRANCH_TO" "$DEPLOY_HIST_MESSAGE" "$ci_mode" + return $? +} + +# ============================================================================ +# ROLLBACK EXECUTION +# ============================================================================ + +# Execute forward rollback via git revert +# Usage: _deploy_perform_rollback +_deploy_perform_rollback() { + local commit_hash="$1" + local target_branch="$2" + local original_message="$3" + local ci_mode="${4:-false}" + local start_time=$SECONDS + + local current_branch=$(_git_current_branch) + + echo "" + echo "${FLOW_COLORS[info]} Rolling back...${FLOW_COLORS[reset]}" + echo "${FLOW_COLORS[dim]}─────────────────────────────────────────────────${FLOW_COLORS[reset]}" + + # Save state for history — capture the TARGET branch HEAD (not current branch) + local commit_before=$(git rev-parse "$target_branch" 2>/dev/null) + + # Switch to target branch (usually production) + if [[ "$current_branch" != "$target_branch" ]]; then + local checkout_err + checkout_err=$(git checkout "$target_branch" 2>&1) || { + _teach_error "Failed to switch to $target_branch" "$checkout_err" + return 1 + } + echo "${FLOW_COLORS[success]} [ok]${FLOW_COLORS[reset]} Switched to $target_branch" + fi + + # Pull latest + git pull origin "$target_branch" --ff-only 2>/dev/null + + # Find the full commit hash from the short hash + local full_hash + full_hash=$(git rev-parse "$commit_hash" 2>/dev/null) + if [[ -z "$full_hash" ]]; then + _teach_error "Commit $commit_hash not found" + git checkout "$current_branch" 2>/dev/null + return 1 + fi + + # Perform git revert (forward rollback) + # Detect merge commits (>1 parent) — git revert requires -m 1 for merges + local revert_message="revert: rollback deploy ($original_message)" + local parent_count + parent_count=$(git cat-file -p "$full_hash" 2>/dev/null | grep -c "^parent ") + + local revert_err + if [[ $parent_count -gt 1 ]]; then + # Merge commit: specify parent 1 (the branch merged INTO) + revert_err=$(git revert "$full_hash" -m 1 --no-edit 2>&1) + else + revert_err=$(git revert "$full_hash" --no-edit 2>&1) + fi + + if [[ $? -ne 0 ]]; then + _teach_error "Revert failed" "$revert_err" + echo "" + echo "${FLOW_COLORS[dim]} Tip: Resolve conflicts manually, then commit${FLOW_COLORS[reset]}" + echo "${FLOW_COLORS[dim]} Or abort with: git revert --abort${FLOW_COLORS[reset]}" + # Don't switch back — let user resolve + return 1 + fi + + # Amend the revert commit message to be more descriptive + git commit --amend -m "$revert_message" 2>/dev/null + echo "${FLOW_COLORS[success]} [ok]${FLOW_COLORS[reset]} Reverted commit $commit_hash" + + # Push to origin + local push_err + push_err=$(git push origin "$target_branch" 2>&1) + if [[ $? -ne 0 ]]; then + _teach_error "Failed to push revert to origin" "$push_err" + git checkout "$current_branch" 2>/dev/null + return 1 + fi + echo "${FLOW_COLORS[success]} [ok]${FLOW_COLORS[reset]} Pushed to origin/$target_branch" + + local commit_after=$(git rev-parse HEAD 2>/dev/null) + + # Switch back to original branch + if [[ "$current_branch" != "$target_branch" ]]; then + git checkout "$current_branch" 2>/dev/null + echo "${FLOW_COLORS[success]} [ok]${FLOW_COLORS[reset]} Back on $current_branch" + fi + + local elapsed=$(( SECONDS - start_time )) + + # Record rollback in deploy history + if typeset -f _deploy_history_append >/dev/null 2>&1; then + local file_count=0 + file_count=$(git diff --name-only "${full_hash}^" "$full_hash" 2>/dev/null | wc -l | tr -d ' ') + _deploy_history_append \ + "rollback" \ + "$commit_after" \ + "$commit_before" \ + "$current_branch" \ + "$target_branch" \ + "$file_count" \ + "$revert_message" \ + "null" \ + "null" \ + "$elapsed" + fi + + echo "" + echo "${FLOW_COLORS[success]} Rollback complete${FLOW_COLORS[reset]}" + echo " Reverted deployment commit $commit_hash in ${elapsed}s" + + # Export for callers + DEPLOY_COMMIT_BEFORE="$commit_before" + DEPLOY_COMMIT_AFTER="$commit_after" + DEPLOY_DURATION="$elapsed" + DEPLOY_MODE="rollback" + + return 0 +} diff --git a/lib/dispatchers/teach-deploy-enhanced.zsh b/lib/dispatchers/teach-deploy-enhanced.zsh index d290f6b15..8e39c3cf0 100644 --- a/lib/dispatchers/teach-deploy-enhanced.zsh +++ b/lib/dispatchers/teach-deploy-enhanced.zsh @@ -9,8 +9,328 @@ # - Index management (ADD/UPDATE/REMOVE) # - Auto-commit + Auto-tag # - Cross-reference validation +# - CI mode (--ci flag or auto-detect non-TTY) # +# ============================================================================ +# SHARED PREFLIGHT CHECKS +# ============================================================================ + +# Shared preflight checks for all deploy modes +# Returns 0 if all checks pass +# Sets: DEPLOY_DRAFT_BRANCH, DEPLOY_PROD_BRANCH, DEPLOY_COURSE_NAME, DEPLOY_AUTO_PR, DEPLOY_REQUIRE_CLEAN +_deploy_preflight_checks() { + local ci_mode="${1:-false}" + + # Check if in git repo + if ! _git_in_repo; then + _teach_error "Not in a git repository" \ + "Initialize git first with: git init" + return 1 + fi + + # Check config file + local config_file=".flow/teach-config.yml" + if [[ ! -f "$config_file" ]]; then + _teach_error ".flow/teach-config.yml not found" \ + "Run 'teach init' to create the configuration" + return 1 + fi + + # Read config (export for caller) + DEPLOY_DRAFT_BRANCH=$(yq '.git.draft_branch // .branches.draft // "draft"' "$config_file" 2>/dev/null) || DEPLOY_DRAFT_BRANCH="draft" + DEPLOY_PROD_BRANCH=$(yq '.git.production_branch // .branches.production // "main"' "$config_file" 2>/dev/null) || DEPLOY_PROD_BRANCH="main" + DEPLOY_AUTO_PR=$(yq '.git.auto_pr // true' "$config_file" 2>/dev/null) || DEPLOY_AUTO_PR="true" + DEPLOY_REQUIRE_CLEAN=$(yq '.git.require_clean // true' "$config_file" 2>/dev/null) || DEPLOY_REQUIRE_CLEAN="true" + DEPLOY_COURSE_NAME=$(yq '.course.name // "Teaching Project"' "$config_file" 2>/dev/null) || DEPLOY_COURSE_NAME="Teaching Project" + + # Output header + echo "" + echo "${FLOW_COLORS[info]} Pre-flight Checks${FLOW_COLORS[reset]}" + echo "${FLOW_COLORS[dim]}─────────────────────────────────────────────────${FLOW_COLORS[reset]}" + + # Check: git repo + echo "${FLOW_COLORS[success]} [ok]${FLOW_COLORS[reset]} Git repository" + + # Check: config + echo "${FLOW_COLORS[success]} [ok]${FLOW_COLORS[reset]} Config file found" + + # Check: on draft branch + local current_branch=$(_git_current_branch) + if [[ "$current_branch" != "$DEPLOY_DRAFT_BRANCH" ]]; then + if [[ "$ci_mode" == "true" ]]; then + echo "${FLOW_COLORS[error]} [!!]${FLOW_COLORS[reset]} Not on $DEPLOY_DRAFT_BRANCH branch (on: $current_branch)" + _teach_error "Not on $DEPLOY_DRAFT_BRANCH branch (on: $current_branch)" \ + "CI mode cannot switch branches. Ensure correct branch before running." + return 1 + fi + echo "${FLOW_COLORS[error]} [!!]${FLOW_COLORS[reset]} Not on $DEPLOY_DRAFT_BRANCH branch (on: $current_branch)" + echo "" + echo -n "${FLOW_COLORS[prompt]} Switch to $DEPLOY_DRAFT_BRANCH? [Y/n]:${FLOW_COLORS[reset]} " + read -r switch_confirm + case "$switch_confirm" in + n|N|no|No|NO) return 1 ;; + *) + git checkout "$DEPLOY_DRAFT_BRANCH" || { + _teach_error "Failed to switch to $DEPLOY_DRAFT_BRANCH" + return 1 + } + echo "${FLOW_COLORS[success]} [ok]${FLOW_COLORS[reset]} Switched to $DEPLOY_DRAFT_BRANCH" + ;; + esac + else + echo "${FLOW_COLORS[success]} [ok]${FLOW_COLORS[reset]} On $DEPLOY_DRAFT_BRANCH branch" + fi + + # Check: working tree clean + if [[ "$DEPLOY_REQUIRE_CLEAN" == "true" ]] && ! _git_is_clean; then + echo "${FLOW_COLORS[error]} [!!]${FLOW_COLORS[reset]} Working tree dirty" + return 1 + else + echo "${FLOW_COLORS[success]} [ok]${FLOW_COLORS[reset]} Working tree clean" + fi + + # Check: no conflicts with production + if _git_detect_production_conflicts "$DEPLOY_DRAFT_BRANCH" "$DEPLOY_PROD_BRANCH" 2>/dev/null; then + echo "${FLOW_COLORS[success]} [ok]${FLOW_COLORS[reset]} No production conflicts" + else + echo "${FLOW_COLORS[warn]} [!!]${FLOW_COLORS[reset]} Production has new commits" + if [[ "$ci_mode" == "true" ]]; then + _teach_error "CI mode: production conflicts detected. Resolve manually." + return 1 + fi + fi + + return 0 +} + +# ============================================================================ +# DIRECT MERGE MODE +# ============================================================================ + +# Direct merge mode: merge draft -> production without PR +# Usage: _deploy_direct_merge +# Returns: 0 on success, 1 on failure +# This is the fast path (8-15s vs 45-90s for PR mode) +_deploy_direct_merge() { + local draft_branch="$1" + local prod_branch="$2" + local commit_message="$3" + local ci_mode="${4:-false}" + local start_time=$SECONDS + + echo "" + echo "${FLOW_COLORS[info]} Direct merge: $draft_branch -> $prod_branch${FLOW_COLORS[reset]}" + echo "${FLOW_COLORS[dim]}─────────────────────────────────────────────────${FLOW_COLORS[reset]}" + + # Guard: working tree must be clean before branch switch + if ! _git_is_clean; then + _teach_error "Working tree must be clean for direct merge" \ + "Commit or stash changes first" + return 1 + fi + + # Save the PRODUCTION branch HEAD for rollback reference (not current branch) + local commit_before=$(git rev-parse "$prod_branch" 2>/dev/null) + + # Ensure draft is pushed to remote first + local push_err + push_err=$(git push origin "$draft_branch" 2>&1) + if [[ $? -ne 0 ]]; then + # If push fails, might be nothing to push (ok) or real error + if ! _git_is_synced 2>/dev/null; then + _teach_error "Failed to push $draft_branch to origin" "$push_err" + return 1 + fi + fi + echo "${FLOW_COLORS[success]} [ok]${FLOW_COLORS[reset]} $draft_branch pushed to origin" + + # Switch to production branch + local checkout_err + checkout_err=$(git checkout "$prod_branch" 2>&1) || { + _teach_error "Failed to switch to $prod_branch" "$checkout_err" + return 1 + } + + # Pull latest production + local pull_err + pull_err=$(git pull origin "$prod_branch" --ff-only 2>&1) || { + # If ff-only fails, try regular pull + pull_err=$(git pull origin "$prod_branch" 2>&1) || { + _teach_error "Failed to pull latest $prod_branch" "$pull_err" + git checkout "$draft_branch" 2>/dev/null + return 1 + } + } + + # Merge draft into production + local merge_err + merge_err=$(git merge "$draft_branch" --no-edit -m "$commit_message" 2>&1) + if [[ $? -ne 0 ]]; then + _teach_error "Merge conflict! Aborting merge." "$merge_err" + git merge --abort 2>/dev/null + git checkout "$draft_branch" 2>/dev/null + echo "" + echo "${FLOW_COLORS[dim]} Tip: Resolve conflicts manually or use PR mode${FLOW_COLORS[reset]}" + return 1 + fi + echo "${FLOW_COLORS[success]} [ok]${FLOW_COLORS[reset]} Merged successfully" + + # Push production to origin + local push_prod_err + push_prod_err=$(git push origin "$prod_branch" 2>&1) + if [[ $? -ne 0 ]]; then + _teach_error "Failed to push $prod_branch to origin" "$push_prod_err" + git checkout "$draft_branch" 2>/dev/null + return 1 + fi + echo "${FLOW_COLORS[success]} [ok]${FLOW_COLORS[reset]} Pushed to origin/$prod_branch" + + # Get the new commit hash + local commit_after=$(git rev-parse HEAD 2>/dev/null) + + # Switch back to draft branch + git checkout "$draft_branch" 2>/dev/null || { + _teach_error "Warning: Failed to switch back to $draft_branch" + } + + local elapsed=$(( SECONDS - start_time )) + + echo "" + echo "${FLOW_COLORS[success]} Done in ${elapsed}s${FLOW_COLORS[reset]}" + + # Export for history tracking + DEPLOY_COMMIT_BEFORE="$commit_before" + DEPLOY_COMMIT_AFTER="$commit_after" + DEPLOY_DURATION="$elapsed" + DEPLOY_MODE="direct" + + return 0 +} + +# ============================================================================ +# DRY-RUN REPORT +# ============================================================================ + +# Dry-run report: preview deploy without executing +# Usage: _deploy_dry_run_report +_deploy_dry_run_report() { + local draft_branch="$1" + local prod_branch="$2" + local course_name="$3" + local direct_mode="${4:-false}" + local commit_message="${5:-}" + + echo "" + echo "${FLOW_COLORS[warn]} DRY RUN — No changes will be made${FLOW_COLORS[reset]}" + echo "${FLOW_COLORS[dim]}─────────────────────────────────────────────────${FLOW_COLORS[reset]}" + + # Show files that would be deployed + local files_changed + files_changed=$(git diff --name-status "$prod_branch"..."$draft_branch" 2>/dev/null) + + if [[ -n "$files_changed" ]]; then + local file_count=$(echo "$files_changed" | wc -l | tr -d ' ') + echo "" + echo " Would deploy $file_count files:" + + while IFS=$'\t' read -r fstatus file; do + case "$fstatus" in + M) echo " ${FLOW_COLORS[warn]}M${FLOW_COLORS[reset]} $file" ;; + A) echo " ${FLOW_COLORS[success]}A${FLOW_COLORS[reset]} $file" ;; + D) echo " ${FLOW_COLORS[error]}D${FLOW_COLORS[reset]} $file" ;; + R*) echo " ${FLOW_COLORS[info]}R${FLOW_COLORS[reset]} $file" ;; + *) echo " $fstatus $file" ;; + esac + done <<< "$files_changed" + else + echo "" + echo " No changes to deploy." + return 0 + fi + + # Show commit message + echo "" + if [[ -n "$commit_message" ]]; then + echo " Would commit: \"$commit_message\"" + elif typeset -f _generate_smart_commit_message >/dev/null 2>&1; then + local smart_msg=$(_generate_smart_commit_message "$draft_branch" "$prod_branch") + echo " Would commit: \"$smart_msg\"" + else + echo " Would commit: \"deploy: $course_name update\"" + fi + + # Show mode + echo "" + if [[ "$direct_mode" == "true" ]]; then + echo " Would merge: $draft_branch -> $prod_branch (direct mode)" + else + echo " Would create: PR from $draft_branch -> $prod_branch" + fi + + # Show history entry + local deploy_count=0 + if typeset -f _deploy_history_count >/dev/null 2>&1; then + deploy_count=$(_deploy_history_count) + fi + local next_num=$(( deploy_count + 1 )) + echo " Would log: deploy #$next_num to .flow/deploy-history.yml" + + # Show .STATUS update hint + if [[ -f ".STATUS" ]]; then + echo " Would update: .STATUS" + fi + + echo "" + echo "${FLOW_COLORS[dim]} Run without --dry-run to execute${FLOW_COLORS[reset]}" + + return 0 +} + +# ============================================================================ +# .STATUS FILE UPDATE +# ============================================================================ + +# Update .STATUS file after deployment +# Sets deploy_count, last_deploy, and teaching_week (if determinable) +_deploy_update_status_file() { + local status_file=".STATUS" + [[ ! -f "$status_file" ]] && return 0 # Non-destructive: skip if absent + + local deploy_count + if typeset -f _deploy_history_count >/dev/null 2>&1; then + deploy_count=$(_deploy_history_count) + else + deploy_count="" + fi + + # Update last_deploy + if command -v yq >/dev/null 2>&1; then + local today=$(date '+%Y-%m-%d') + yq -i ".last_deploy = \"$today\"" "$status_file" 2>/dev/null + if [[ -n "$deploy_count" ]]; then + yq -i ".deploy_count = $deploy_count" "$status_file" 2>/dev/null + fi + + # Attempt teaching_week from semester_info.start_date + local start_date + start_date=$(yq '.semester_info.start_date // ""' .flow/teach-config.yml 2>/dev/null) + if [[ -n "$start_date" && "$start_date" != "null" ]]; then + local start_epoch today_epoch week_num + start_epoch=$(date -j -f "%Y-%m-%d" "$start_date" "+%s" 2>/dev/null) + today_epoch=$(date "+%s") + if [[ -n "$start_epoch" ]]; then + week_num=$(( (today_epoch - start_epoch) / 604800 + 1 )) + if [[ $week_num -ge 1 && $week_num -le 20 ]]; then + yq -i ".teaching_week = $week_num" "$status_file" 2>/dev/null + fi + fi + fi + + echo " ${FLOW_COLORS[dim]}.STATUS updated${FLOW_COLORS[reset]}" + fi +} + # ============================================================================ # ENHANCED TEACH DEPLOY - WITH PARTIAL DEPLOYMENT SUPPORT # ============================================================================ @@ -23,14 +343,31 @@ _teach_deploy_enhanced() { local auto_tag=false local skip_index=false local check_prereqs=false + local ci_mode=false + local custom_message="" + local dry_run=false + + # Auto-detect CI mode: no TTY means non-interactive + if [[ ! -t 0 ]]; then + ci_mode=true + fi # Parse flags and files while [[ $# -gt 0 ]]; do case "$1" in - --direct-push) + --ci) + ci_mode=true + shift + ;; + --direct|-d|--direct-push) direct_push=true shift ;; + --message|-m) + shift + custom_message="$1" + shift + ;; --auto-commit) auto_commit=true shift @@ -47,6 +384,36 @@ _teach_deploy_enhanced() { check_prereqs=true shift ;; + --dry-run|--preview) + dry_run=true + shift + ;; + --rollback) + shift + local rollback_idx="" + # Check if next arg is a number (optional index) + if [[ $# -gt 0 && "$1" =~ ^[0-9]+$ ]]; then + rollback_idx="$1" + shift + fi + # Dispatch to rollback immediately (no preflight needed) + if [[ "$ci_mode" == "true" ]]; then + _deploy_rollback "$rollback_idx" --ci + else + _deploy_rollback "$rollback_idx" + fi + return $? + ;; + --history) + shift + local history_count=10 + if [[ $# -gt 0 && "$1" =~ ^[0-9]+$ ]]; then + history_count="$1" + shift + fi + _deploy_history_list "$history_count" + return $? + ;; --help|-h|help) _teach_deploy_enhanced_help return 0 @@ -76,61 +443,28 @@ _teach_deploy_enhanced() { done # ============================================ - # PRE-FLIGHT CHECKS + # PRE-FLIGHT CHECKS (shared function) # ============================================ - # Check if in git repo - if ! _git_in_repo; then - _teach_error "Not in a git repository" \ - "Initialize git first with: git init" - return 1 - fi - - # Check if config file exists - local config_file=".flow/teach-config.yml" - if [[ ! -f "$config_file" ]]; then - _teach_error ".flow/teach-config.yml not found" \ - "Run 'teach init' to create the configuration" - return 1 - fi - - # Read git configuration from teach-config.yml - local draft_branch prod_branch auto_pr require_clean - draft_branch=$(yq '.git.draft_branch // .branches.draft // "draft"' "$config_file" 2>/dev/null) || draft_branch="draft" - prod_branch=$(yq '.git.production_branch // .branches.production // "main"' "$config_file" 2>/dev/null) || prod_branch="main" - auto_pr=$(yq '.git.auto_pr // true' "$config_file" 2>/dev/null) || auto_pr="true" - require_clean=$(yq '.git.require_clean // true' "$config_file" 2>/dev/null) || require_clean="true" - - # Read course info - local course_name - course_name=$(yq '.course.name // "Teaching Project"' "$config_file" 2>/dev/null) || course_name="Teaching Project" - - echo "" - echo "${FLOW_COLORS[info]}🔍 Pre-flight Checks${FLOW_COLORS[reset]}" - echo "${FLOW_COLORS[dim]}─────────────────────────────────────────────────${FLOW_COLORS[reset]}" + _deploy_preflight_checks "$ci_mode" || return 1 - # Check 1: Verify we're on draft branch - local current_branch=$(_git_current_branch) - if [[ "$current_branch" != "$draft_branch" ]]; then - echo "${FLOW_COLORS[error]}✗${FLOW_COLORS[reset]} Not on $draft_branch branch (currently on: $current_branch)" - echo "" - echo -n "${FLOW_COLORS[prompt]}Switch to $draft_branch branch? [Y/n]:${FLOW_COLORS[reset]} " - read -r switch_confirm + # Read exported variables from preflight + local draft_branch="$DEPLOY_DRAFT_BRANCH" + local prod_branch="$DEPLOY_PROD_BRANCH" + local course_name="$DEPLOY_COURSE_NAME" + local auto_pr="$DEPLOY_AUTO_PR" + local require_clean="$DEPLOY_REQUIRE_CLEAN" - case "$switch_confirm" in - n|N|no|No|NO) - return 1 - ;; - *) - git checkout "$draft_branch" || { - _teach_error "Failed to switch to $draft_branch" - return 1 - } - echo "${FLOW_COLORS[success]}✓${FLOW_COLORS[reset]} Switched to $draft_branch" - ;; - esac - else - echo "${FLOW_COLORS[success]}✓${FLOW_COLORS[reset]} On $draft_branch branch" + # ============================================ + # DRY-RUN MODE + # ============================================ + if [[ "$dry_run" == "true" && "$partial_deploy" != "true" ]]; then + local smart_msg="" + if [[ -n "$custom_message" ]]; then + smart_msg="$custom_message" + fi + _deploy_dry_run_report "$draft_branch" "$prod_branch" "$course_name" "$direct_push" "$smart_msg" + return 0 fi # ============================================ @@ -173,6 +507,10 @@ _teach_deploy_enhanced() { if _validate_cross_references "${deploy_files[@]}"; then echo "${FLOW_COLORS[success]}✓${FLOW_COLORS[reset]} All cross-references valid" else + if [[ "$ci_mode" == "true" ]]; then + _teach_error "CI mode: broken cross-references detected. Fix before deploying." + return 1 + fi echo "" echo -n "${FLOW_COLORS[prompt]}Continue with broken references? [y/N]:${FLOW_COLORS[reset]} " read -r continue_confirm @@ -210,18 +548,37 @@ _teach_deploy_enhanced() { if [[ $dep_count -gt 0 ]]; then echo "" echo "${FLOW_COLORS[info]}Found $dep_count additional dependencies${FLOW_COLORS[reset]}" - echo -n "${FLOW_COLORS[prompt]}Include dependencies in deployment? [Y/n]:${FLOW_COLORS[reset]} " - read -r include_deps - case "$include_deps" in - n|N|no|No|NO) - # Keep only original files - all_files=("${deploy_files[@]}") - ;; - *) - # Use all files including dependencies - deploy_files=("${all_files[@]}") - ;; - esac + if [[ "$ci_mode" == "true" ]]; then + # CI mode: auto-include dependencies + deploy_files=("${all_files[@]}") + else + echo -n "${FLOW_COLORS[prompt]}Include dependencies in deployment? [Y/n]:${FLOW_COLORS[reset]} " + read -r include_deps + case "$include_deps" in + n|N|no|No|NO) + # Keep only original files + all_files=("${deploy_files[@]}") + ;; + *) + # Use all files including dependencies + deploy_files=("${all_files[@]}") + ;; + esac + fi + fi + + # Dry-run: show what would happen and exit (partial deploy) + if [[ "$dry_run" == "true" ]]; then + echo "" + echo "${FLOW_COLORS[warn]} DRY RUN — No changes will be made${FLOW_COLORS[reset]}" + echo "" + echo " Would deploy ${#deploy_files[@]} files:" + for file in "${deploy_files[@]}"; do + echo " $file" + done + echo "" + echo "${FLOW_COLORS[dim]} Run without --dry-run to execute${FLOW_COLORS[reset]}" + return 0 fi # Check for uncommitted changes in deploy files @@ -242,8 +599,8 @@ _teach_deploy_enhanced() { done echo "" - if [[ "$auto_commit" == "true" ]]; then - # Auto-commit mode + if [[ "$auto_commit" == "true" || "$ci_mode" == "true" ]]; then + # Auto-commit mode (or CI mode) echo "${FLOW_COLORS[info]}Auto-commit mode enabled${FLOW_COLORS[reset]}" local commit_msg="Update: $(date +%Y-%m-%d)" @@ -290,23 +647,32 @@ _teach_deploy_enhanced() { fi # Push to remote - echo "" - echo -n "${FLOW_COLORS[prompt]}Push to origin/$draft_branch? [Y/n]:${FLOW_COLORS[reset]} " - read -r push_confirm - - case "$push_confirm" in - n|N|no|No|NO) - echo "Deployment cancelled" + if [[ "$ci_mode" == "true" ]]; then + # CI mode: auto-push + if _git_push_current_branch; then + echo "${FLOW_COLORS[success]}✓${FLOW_COLORS[reset]} Pushed to origin/$draft_branch" + else return 1 - ;; - *) - if _git_push_current_branch; then - echo "${FLOW_COLORS[success]}✓${FLOW_COLORS[reset]} Pushed to origin/$draft_branch" - else + fi + else + echo "" + echo -n "${FLOW_COLORS[prompt]}Push to origin/$draft_branch? [Y/n]:${FLOW_COLORS[reset]} " + read -r push_confirm + + case "$push_confirm" in + n|N|no|No|NO) + echo "Deployment cancelled" return 1 - fi - ;; - esac + ;; + *) + if _git_push_current_branch; then + echo "${FLOW_COLORS[success]}✓${FLOW_COLORS[reset]} Pushed to origin/$draft_branch" + else + return 1 + fi + ;; + esac + fi # Auto-tag if requested if [[ "$auto_tag" == "true" ]]; then @@ -324,23 +690,33 @@ _teach_deploy_enhanced() { pr_body+="- $file\n" done - echo "" - echo -n "${FLOW_COLORS[prompt]}Create pull request? [Y/n]:${FLOW_COLORS[reset]} " - read -r pr_confirm - - case "$pr_confirm" in - n|N|no|No|NO) - echo "PR creation skipped" - ;; - *) - if _git_create_deploy_pr "$draft_branch" "$prod_branch" "$pr_title" "$pr_body"; then - echo "" - echo "${FLOW_COLORS[success]}✅ Pull Request Created${FLOW_COLORS[reset]}" - else - return 1 - fi - ;; - esac + if [[ "$ci_mode" == "true" ]]; then + # CI mode: auto-create PR + if _git_create_deploy_pr "$draft_branch" "$prod_branch" "$pr_title" "$pr_body"; then + echo "" + echo "${FLOW_COLORS[success]}✅ Pull Request Created${FLOW_COLORS[reset]}" + else + return 1 + fi + else + echo "" + echo -n "${FLOW_COLORS[prompt]}Create pull request? [Y/n]:${FLOW_COLORS[reset]} " + read -r pr_confirm + + case "$pr_confirm" in + n|N|no|No|NO) + echo "PR creation skipped" + ;; + *) + if _git_create_deploy_pr "$draft_branch" "$prod_branch" "$pr_title" "$pr_body"; then + echo "" + echo "${FLOW_COLORS[success]}✅ Pull Request Created${FLOW_COLORS[reset]}" + else + return 1 + fi + ;; + esac + fi fi echo "" @@ -355,6 +731,61 @@ _teach_deploy_enhanced() { # Fall back to original _teach_deploy implementation # This preserves the existing full-site deployment workflow + # ============================================ + # DEPLOY MODE DISPATCH + # ============================================ + + if [[ "$direct_push" == "true" ]]; then + # Direct merge mode (fast path, 8-15s) + local smart_message + if [[ -n "$custom_message" ]]; then + smart_message="$custom_message" + elif typeset -f _generate_smart_commit_message >/dev/null 2>&1; then + smart_message=$(_generate_smart_commit_message "$draft_branch" "$prod_branch") + else + smart_message="deploy: $course_name update" + fi + + echo "" + echo "${FLOW_COLORS[info]} Smart commit: $smart_message${FLOW_COLORS[reset]}" + + _deploy_direct_merge "$draft_branch" "$prod_branch" "$smart_message" "$ci_mode" || return 1 + + # Auto-tag if requested + if [[ "$auto_tag" == "true" ]]; then + local tag="deploy-$(date +%Y-%m-%d-%H%M)" + git tag "$tag" 2>/dev/null + git push origin "$tag" 2>/dev/null + echo "${FLOW_COLORS[success]} [ok]${FLOW_COLORS[reset]} Tagged as $tag" + fi + + # Record in deploy history + if typeset -f _deploy_history_append >/dev/null 2>&1; then + local _commit_after="${DEPLOY_COMMIT_AFTER:-$(git rev-parse --short=8 HEAD 2>/dev/null)}" + local _commit_before="${DEPLOY_COMMIT_BEFORE:-}" + local _file_count=$(git diff --name-only HEAD~1 HEAD 2>/dev/null | wc -l | tr -d ' ') + local _elapsed="${DEPLOY_DURATION:-0}" + _deploy_history_append "direct" "$_commit_after" "$_commit_before" "$draft_branch" "$prod_branch" "$_file_count" "$smart_message" "null" "null" "$_elapsed" + echo " ${FLOW_COLORS[dim]}History logged: #$(( $(_deploy_history_count) )) ($(date '+%Y-%m-%d %H:%M'))${FLOW_COLORS[reset]}" + fi + + # Update .STATUS file + _deploy_update_status_file 2>/dev/null + + echo "" + echo "${FLOW_COLORS[success]} Direct deployment complete${FLOW_COLORS[reset]}" + + # Show site URL if available + local site_url + site_url=$(yq '.site.url // ""' .flow/teach-config.yml 2>/dev/null) + if [[ -n "$site_url" && "$site_url" != "null" ]]; then + echo " Site: $site_url" + fi + + _deploy_cleanup_globals + return 0 + fi + # Check 2: Verify no uncommitted changes (if required) if [[ "$require_clean" == "true" ]]; then if ! _git_is_clean; then @@ -371,22 +802,31 @@ _teach_deploy_enhanced() { # Check 3: Check for unpushed commits if _git_has_unpushed_commits; then echo "${FLOW_COLORS[warn]}⚠️ ${FLOW_COLORS[reset]} Unpushed commits detected" - echo "" - echo -n "${FLOW_COLORS[prompt]}Push to origin/$draft_branch first? [Y/n]:${FLOW_COLORS[reset]} " - read -r push_confirm + if [[ "$ci_mode" == "true" ]]; then + # CI mode: auto-push + if _git_push_current_branch; then + echo "${FLOW_COLORS[success]}✓${FLOW_COLORS[reset]} Pushed to origin/$draft_branch" + else + return 1 + fi + else + echo "" + echo -n "${FLOW_COLORS[prompt]}Push to origin/$draft_branch first? [Y/n]:${FLOW_COLORS[reset]} " + read -r push_confirm - case "$push_confirm" in - n|N|no|No|NO) - echo "${FLOW_COLORS[warn]}Continuing without push...${FLOW_COLORS[reset]}" - ;; - *) - if _git_push_current_branch; then - echo "${FLOW_COLORS[success]}✓${FLOW_COLORS[reset]} Pushed to origin/$draft_branch" - else - return 1 - fi - ;; - esac + case "$push_confirm" in + n|N|no|No|NO) + echo "${FLOW_COLORS[warn]}Continuing without push...${FLOW_COLORS[reset]}" + ;; + *) + if _git_push_current_branch; then + echo "${FLOW_COLORS[success]}✓${FLOW_COLORS[reset]} Pushed to origin/$draft_branch" + else + return 1 + fi + ;; + esac + fi else echo "${FLOW_COLORS[success]}✓${FLOW_COLORS[reset]} Remote is up-to-date" fi @@ -396,6 +836,10 @@ _teach_deploy_enhanced() { echo "${FLOW_COLORS[success]}✓${FLOW_COLORS[reset]} No conflicts with production" else echo "${FLOW_COLORS[warn]}⚠️ ${FLOW_COLORS[reset]} Production ($prod_branch) has new commits" + if [[ "$ci_mode" == "true" ]]; then + _teach_error "CI mode: production conflicts detected. Resolve manually." + return 1 + fi echo "" echo "${FLOW_COLORS[prompt]}Production branch has updates. Rebase first?${FLOW_COLORS[reset]}" echo "" @@ -473,38 +917,49 @@ _teach_deploy_enhanced() { # Create PR echo "" if [[ "$auto_pr" == "true" ]]; then - echo "${FLOW_COLORS[prompt]}Create pull request?${FLOW_COLORS[reset]}" - echo "" - echo " ${FLOW_COLORS[dim]}[1]${FLOW_COLORS[reset]} Yes - Create PR (Recommended)" - echo " ${FLOW_COLORS[dim]}[2]${FLOW_COLORS[reset]} Push to $draft_branch only (no PR)" - echo " ${FLOW_COLORS[dim]}[3]${FLOW_COLORS[reset]} Cancel" - echo "" - echo -n "${FLOW_COLORS[prompt]}Your choice [1-3]:${FLOW_COLORS[reset]} " - read -r pr_choice - - case "$pr_choice" in - 1) + if [[ "$ci_mode" == "true" ]]; then + # CI mode: auto-create PR + echo "" + if _git_create_deploy_pr "$draft_branch" "$prod_branch" "$pr_title" "$pr_body"; then echo "" - if _git_create_deploy_pr "$draft_branch" "$prod_branch" "$pr_title" "$pr_body"; then - echo "" - echo "${FLOW_COLORS[success]}✅ Pull Request Created${FLOW_COLORS[reset]}" - else - return 1 - fi - ;; - 2) - if _git_push_current_branch; then + echo "${FLOW_COLORS[success]}✅ Pull Request Created${FLOW_COLORS[reset]}" + else + return 1 + fi + else + echo "${FLOW_COLORS[prompt]}Create pull request?${FLOW_COLORS[reset]}" + echo "" + echo " ${FLOW_COLORS[dim]}[1]${FLOW_COLORS[reset]} Yes - Create PR (Recommended)" + echo " ${FLOW_COLORS[dim]}[2]${FLOW_COLORS[reset]} Push to $draft_branch only (no PR)" + echo " ${FLOW_COLORS[dim]}[3]${FLOW_COLORS[reset]} Cancel" + echo "" + echo -n "${FLOW_COLORS[prompt]}Your choice [1-3]:${FLOW_COLORS[reset]} " + read -r pr_choice + + case "$pr_choice" in + 1) echo "" - echo "${FLOW_COLORS[success]}✓${FLOW_COLORS[reset]} Pushed to origin/$draft_branch" - else + if _git_create_deploy_pr "$draft_branch" "$prod_branch" "$pr_title" "$pr_body"; then + echo "" + echo "${FLOW_COLORS[success]}✅ Pull Request Created${FLOW_COLORS[reset]}" + else + return 1 + fi + ;; + 2) + if _git_push_current_branch; then + echo "" + echo "${FLOW_COLORS[success]}✓${FLOW_COLORS[reset]} Pushed to origin/$draft_branch" + else + return 1 + fi + ;; + 3|*) + echo "Deployment cancelled" return 1 - fi - ;; - 3|*) - echo "Deployment cancelled" - return 1 - ;; - esac + ;; + esac + fi else if _git_push_current_branch; then echo "" @@ -513,11 +968,36 @@ _teach_deploy_enhanced() { return 1 fi fi + + # Record PR deploy in history + if typeset -f _deploy_history_append >/dev/null 2>&1; then + local _pr_commit=$(git rev-parse --short=8 HEAD 2>/dev/null) + local _pr_file_count=$(git diff --name-only "$prod_branch"..."$draft_branch" 2>/dev/null | wc -l | tr -d ' ') + local _pr_message="deploy: $course_name PR update" + _deploy_history_append "pr" "$_pr_commit" "" "$draft_branch" "$prod_branch" "$_pr_file_count" "$_pr_message" "null" "null" "0" + echo " ${FLOW_COLORS[dim]}History logged: #$(( $(_deploy_history_count) )) ($(date '+%Y-%m-%d %H:%M'))${FLOW_COLORS[reset]}" + fi + + # Update .STATUS file + _deploy_update_status_file 2>/dev/null + + _deploy_cleanup_globals +} + +# Clean up DEPLOY_* global variables to avoid polluting the shell environment +_deploy_cleanup_globals() { + unset DEPLOY_DRAFT_BRANCH DEPLOY_PROD_BRANCH DEPLOY_COURSE_NAME + unset DEPLOY_AUTO_PR DEPLOY_REQUIRE_CLEAN + unset DEPLOY_COMMIT_BEFORE DEPLOY_COMMIT_AFTER DEPLOY_DURATION DEPLOY_MODE + unset DEPLOY_HIST_TIMESTAMP DEPLOY_HIST_MODE DEPLOY_HIST_COMMIT + unset DEPLOY_HIST_COMMIT_BEFORE DEPLOY_HIST_BRANCH_FROM DEPLOY_HIST_BRANCH_TO + unset DEPLOY_HIST_FILE_COUNT DEPLOY_HIST_MESSAGE DEPLOY_HIST_PR + unset DEPLOY_HIST_TAG DEPLOY_HIST_USER DEPLOY_HIST_DURATION } # Help for enhanced teach deploy _teach_deploy_enhanced_help() { - echo "teach deploy - Deploy teaching content via PR workflow" + echo "teach deploy - Deploy teaching content to production" echo "" echo "Usage:" echo " teach deploy [files...] [options]" @@ -526,41 +1006,113 @@ _teach_deploy_enhanced_help() { echo " files Files or directories to deploy (partial deploy mode)" echo "" echo "Options:" + echo " --direct, -d Direct merge (no PR, fast path: 8-15s)" + echo " --message, -m MSG Custom commit message for deploy" + echo " --ci Force non-interactive (CI) mode" echo " --auto-commit Auto-commit uncommitted changes" echo " --auto-tag Auto-tag deployment with timestamp" echo " --skip-index Skip index management prompts" echo " --check-prereqs Run prerequisite validation before deploy (blocks on errors)" - echo " --direct-push Bypass PR and push directly to production (advanced)" + echo " --dry-run, --preview Preview what would happen without making changes" + echo " --rollback [N] Rollback deployment N (1=most recent, interactive if omitted)" + echo " --history [N] Show last N deployments (default: 10)" + echo " --direct-push Alias for --direct (backward compatible)" echo " --help, -h Show this help message" echo "" echo "Deployment Modes:" echo " Full Site (default):" - echo " teach deploy # Deploy all changes" + echo " teach deploy # Deploy all changes via PR" + echo "" + echo " Direct Merge (fast path):" + echo " teach deploy -d # Direct merge, no PR (8-15s)" + echo " teach deploy --direct # Same as -d" + echo " teach deploy -d -m \"Week 5\" # Direct merge with message" + echo " teach deploy -d --auto-tag # Direct merge + tag" echo "" echo " Partial Deploy:" echo " teach deploy lectures/week-05.qmd # Deploy single file" echo " teach deploy lectures/ # Deploy entire directory" echo " teach deploy file1.qmd file2.qmd # Deploy multiple files" echo "" + echo " CI Mode:" + echo " teach deploy --ci # Non-interactive (auto-yes)" + echo " teach deploy --ci -d # CI + direct merge" + echo " echo | teach deploy # Auto-detected (no TTY)" + echo "" + echo " Dry Run (preview):" + echo " teach deploy --dry-run # Preview full site deploy" + echo " teach deploy --preview -d # Preview direct merge" + echo " teach deploy --dry-run lectures/ # Preview partial deploy" + echo "" echo "Features:" + echo " • Direct merge mode (--direct): merge draft->prod without PR (8-15s)" + echo " • PR workflow (default): create PR for review (45-90s)" echo " • Dependency tracking (sourced files, cross-references)" echo " • Index management (ADD/UPDATE/REMOVE links)" echo " • Cross-reference validation" echo " • Auto-commit with custom message" echo " • Auto-tag with timestamp" + echo " • CI mode for automated pipelines" + echo " • Smart commit messages (auto-generated from changes)" + echo " • Dry-run mode (--dry-run/--preview): preview without changes" + echo " • Rollback (--rollback): forward rollback via git revert" + echo " • Deploy history (--history): track all deployments" + echo " • .STATUS file auto-update after deploy" + echo "" + echo "Direct Merge vs PR:" + echo " --direct Merge draft->prod locally, push (8-15s, solo instructor)" + echo " (default) Create GitHub PR for review (45-90s, team workflow)" + echo "" + echo "CI Mode Behavior:" + echo " When --ci is passed (or no TTY detected):" + echo " • Branch switch → fail (must be on correct branch)" + echo " • Push confirmation → auto-yes" + echo " • PR creation → auto-yes" + echo " • Include deps → auto-yes" + echo " • Commit message → auto-generate" + echo " • Broken references → fail" + echo " • Production conflict → fail" echo "" echo "Examples:" + echo " # Quick deploy (direct merge, no PR)" + echo " teach deploy -d" + echo "" + echo " # Direct deploy with custom message" + echo " teach deploy -d -m \"Add Week 5 lecture on ANOVA\"" + echo "" + echo " # Direct deploy with auto-tag" + echo " teach deploy --direct --auto-tag" + echo "" echo " # Partial deploy with auto features" echo " teach deploy lectures/week-05.qmd --auto-commit --auto-tag" echo "" echo " # Deploy directory with index updates" echo " teach deploy lectures/" echo "" - echo " # Full site deploy (traditional workflow)" + echo " # Full site deploy via PR (traditional workflow)" echo " teach deploy" echo "" echo " # Deploy with prerequisite validation" echo " teach deploy --check-prereqs" + echo "" + echo " # CI pipeline deploy (direct, no interaction)" + echo " teach deploy --ci -d --auto-commit --auto-tag" + echo "" + echo " # Dry run (preview what would happen)" + echo " teach deploy --dry-run" + echo " teach deploy --preview -d" + echo "" + echo " # Dry run partial deploy" + echo " teach deploy --dry-run lectures/week-05.qmd" + echo "" + echo " # Rollback" + echo " teach deploy --rollback # Interactive picker" + echo " teach deploy --rollback 1 # Rollback most recent" + echo " teach deploy --rollback 2 --ci # Rollback 2nd most recent (CI)" + echo "" + echo " # History" + echo " teach deploy --history # Show last 10 deploys" + echo " teach deploy --history 20 # Show last 20 deploys" } # ============================================================================ diff --git a/lib/dispatchers/teach-dispatcher.zsh b/lib/dispatchers/teach-dispatcher.zsh index 4436b9fec..1e6d4139d 100644 --- a/lib/dispatchers/teach-dispatcher.zsh +++ b/lib/dispatchers/teach-dispatcher.zsh @@ -72,6 +72,20 @@ if [[ -z "$_FLOW_TEACH_DEPLOY_ENHANCED_LOADED" ]]; then typeset -g _FLOW_TEACH_DEPLOY_ENHANCED_LOADED=1 fi +# Source deploy history helpers (v6.4.0 - teach deploy v2) +if [[ -z "$_FLOW_DEPLOY_HISTORY_LOADED" ]]; then + local deploy_history_path="${0:A:h:h}/deploy-history-helpers.zsh" + [[ -f "$deploy_history_path" ]] && source "$deploy_history_path" + typeset -g _FLOW_DEPLOY_HISTORY_LOADED=1 +fi + +# Source deploy rollback helpers (v6.4.0 - teach deploy v2) +if [[ -z "$_FLOW_DEPLOY_ROLLBACK_LOADED" ]]; then + local deploy_rollback_path="${0:A:h:h}/deploy-rollback-helpers.zsh" + [[ -f "$deploy_rollback_path" ]] && source "$deploy_rollback_path" + typeset -g _FLOW_DEPLOY_ROLLBACK_LOADED=1 +fi + # Source profile helpers (Phase 2 - Wave 1: Profile Management) if [[ -z "$_FLOW_PROFILE_HELPERS_LOADED" ]]; then local profile_helpers_path="${0:A:h:h}/profile-helpers.zsh" @@ -1794,354 +1808,6 @@ _teach_auto_commit_workflow() { fi } -# ============================================================================ -# TEACH DEPLOY - BRANCH-AWARE PR WORKFLOW (Phase 2 - v5.11.0+) -# ============================================================================ - -# Deploy teaching content from draft to production via PR -# Usage: _teach_deploy [--direct-push] -_teach_deploy() { - local direct_push=false - - # Parse flags - while [[ $# -gt 0 ]]; do - case "$1" in - --direct-push) - direct_push=true - shift - ;; - --help|-h|help) - _teach_deploy_help - return 0 - ;; - *) - _teach_error "Unknown flag: $1" "Run 'teach deploy --help' for usage" - return 1 - ;; - esac - done - - # Check if in git repo - if ! _git_in_repo; then - _teach_error "Not in a git repository" \ - "Initialize git first with: git init" - return 1 - fi - - # Check if config file exists first (standard location: .flow/teach-config.yml) - local config_file=".flow/teach-config.yml" - if [[ ! -f "$config_file" ]]; then - _teach_error ".flow/teach-config.yml not found" \ - "Run 'teach init' to create the configuration" - return 1 - fi - - # Read git configuration from teach-config.yml with fallback values - local draft_branch prod_branch auto_pr require_clean - draft_branch=$(yq '.git.draft_branch // .branches.draft // "draft"' "$config_file" 2>/dev/null) || draft_branch="draft" - prod_branch=$(yq '.git.production_branch // .branches.production // "main"' "$config_file" 2>/dev/null) || prod_branch="main" - auto_pr=$(yq '.git.auto_pr // true' "$config_file" 2>/dev/null) || auto_pr="true" - require_clean=$(yq '.git.require_clean // true' "$config_file" 2>/dev/null) || require_clean="true" - - # Read workflow configuration (Phase 4 - v5.11.0+) - local teaching_mode auto_push - teaching_mode=$(yq '.workflow.teaching_mode // false' "$config_file" 2>/dev/null) || teaching_mode="false" - auto_push=$(yq '.workflow.auto_push // false' "$config_file" 2>/dev/null) || auto_push="false" - - # Read course info for PR title - local course_name - course_name=$(yq '.course.name // "Teaching Project"' "$config_file" 2>/dev/null) || course_name="Teaching Project" - - echo "" - echo "${FLOW_COLORS[info]}🔍 Pre-flight Checks${FLOW_COLORS[reset]}" - echo "${FLOW_COLORS[dim]}─────────────────────────────────────────────────${FLOW_COLORS[reset]}" - - # Check 1: Verify we're on draft branch - local current_branch=$(_git_current_branch) - if [[ "$current_branch" != "$draft_branch" ]]; then - echo "${FLOW_COLORS[error]}✗${FLOW_COLORS[reset]} Not on $draft_branch branch (currently on: $current_branch)" - echo "" - echo -n "${FLOW_COLORS[prompt]}Switch to $draft_branch branch? [Y/n]:${FLOW_COLORS[reset]} " - read -r switch_confirm - - case "$switch_confirm" in - n|N|no|No|NO) - return 1 - ;; - *) - git checkout "$draft_branch" || { - _teach_error "Failed to switch to $draft_branch" - return 1 - } - echo "${FLOW_COLORS[success]}✓${FLOW_COLORS[reset]} Switched to $draft_branch" - ;; - esac - else - echo "${FLOW_COLORS[success]}✓${FLOW_COLORS[reset]} On $draft_branch branch" - fi - - # Check 2: Verify no uncommitted changes (if required) - if [[ "$require_clean" == "true" ]]; then - if ! _git_is_clean; then - echo "${FLOW_COLORS[error]}✗${FLOW_COLORS[reset]} Uncommitted changes detected" - echo "" - echo " ${FLOW_COLORS[dim]}Commit or stash changes before deploying${FLOW_COLORS[reset]}" - echo " ${FLOW_COLORS[dim]}Or disable with: git.require_clean: false${FLOW_COLORS[reset]}" - return 1 - else - echo "${FLOW_COLORS[success]}✓${FLOW_COLORS[reset]} No uncommitted changes" - fi - fi - - # Check 3: Check for unpushed commits (Phase 4 - teaching mode aware) - if _git_has_unpushed_commits; then - echo "${FLOW_COLORS[warn]}⚠️ ${FLOW_COLORS[reset]} Unpushed commits detected" - echo "" - - # Teaching mode: auto-push if enabled, otherwise prompt - if [[ "$teaching_mode" == "true" && "$auto_push" == "true" ]]; then - echo "${FLOW_COLORS[info]}🎓 Teaching mode: Auto-pushing...${FLOW_COLORS[reset]}" - if _git_push_current_branch; then - echo "${FLOW_COLORS[success]}✓${FLOW_COLORS[reset]} Pushed to origin/$draft_branch" - else - return 1 - fi - else - # Standard mode or teaching mode without auto_push: prompt user - echo -n "${FLOW_COLORS[prompt]}Push to origin/$draft_branch first? [Y/n]:${FLOW_COLORS[reset]} " - read -r push_confirm - - case "$push_confirm" in - n|N|no|No|NO) - echo "${FLOW_COLORS[warn]}Continuing without push...${FLOW_COLORS[reset]}" - ;; - *) - if _git_push_current_branch; then - echo "${FLOW_COLORS[success]}✓${FLOW_COLORS[reset]} Pushed to origin/$draft_branch" - else - return 1 - fi - ;; - esac - fi - else - echo "${FLOW_COLORS[success]}✓${FLOW_COLORS[reset]} Remote is up-to-date" - fi - - # Check 4: Conflict detection - if _git_detect_production_conflicts "$draft_branch" "$prod_branch"; then - echo "${FLOW_COLORS[success]}✓${FLOW_COLORS[reset]} No conflicts with production" - else - local commits_ahead=$(git rev-list --count "origin/${prod_branch}..origin/${draft_branch}" 2>/dev/null || echo 0) - echo "${FLOW_COLORS[warn]}⚠️ ${FLOW_COLORS[reset]} Production ($prod_branch) has new commits" - echo "" - echo "${FLOW_COLORS[prompt]}Production branch has updates. Rebase first?${FLOW_COLORS[reset]}" - echo "" - echo " ${FLOW_COLORS[dim]}[1]${FLOW_COLORS[reset]} Yes - Rebase $draft_branch onto $prod_branch (Recommended)" - echo " ${FLOW_COLORS[dim]}[2]${FLOW_COLORS[reset]} No - Continue anyway (may have merge conflicts in PR)" - echo " ${FLOW_COLORS[dim]}[3]${FLOW_COLORS[reset]} Cancel deployment" - echo "" - echo -n "${FLOW_COLORS[prompt]}Your choice [1-3]:${FLOW_COLORS[reset]} " - read -r rebase_choice - - case "$rebase_choice" in - 1) - if _git_rebase_onto_production "$draft_branch" "$prod_branch"; then - echo "${FLOW_COLORS[success]}✓${FLOW_COLORS[reset]} Rebase successful" - else - return 1 - fi - ;; - 2) - echo "${FLOW_COLORS[warn]}Continuing without rebase...${FLOW_COLORS[reset]}" - ;; - 3|*) - echo "Deployment cancelled" - return 1 - ;; - esac - fi - - echo "" - - # Generate PR details - local commit_count=$(_git_get_commit_count "$draft_branch" "$prod_branch") - local pr_title="Deploy: $course_name Updates" - local pr_body=$(_git_generate_pr_body "$draft_branch" "$prod_branch") - - # Show PR preview - echo "${FLOW_COLORS[info]}📋 Pull Request Preview${FLOW_COLORS[reset]}" - echo "${FLOW_COLORS[dim]}─────────────────────────────────────────────────${FLOW_COLORS[reset]}" - echo "" - echo "${FLOW_COLORS[bold]}Title:${FLOW_COLORS[reset]} $pr_title" - echo "${FLOW_COLORS[bold]}From:${FLOW_COLORS[reset]} $draft_branch → $prod_branch" - echo "${FLOW_COLORS[bold]}Commits:${FLOW_COLORS[reset]} $commit_count" - echo "" - - # ============================================ - # DEPLOYMENT PREVIEW (v5.14.0 - Task 8) - # ============================================ - echo "" - echo "${FLOW_COLORS[info]}📋 Changes Preview${FLOW_COLORS[reset]}" - echo "${FLOW_COLORS[dim]}─────────────────────────────────────────────────${FLOW_COLORS[reset]}" - - # Show files changed summary - local files_changed=$(git diff --name-status "$prod_branch"..."$draft_branch" 2>/dev/null) - if [[ -n "$files_changed" ]]; then - echo "" - echo "${FLOW_COLORS[dim]}Files Changed:${FLOW_COLORS[reset]}" - while IFS=$'\t' read -r file_status file; do - case "$file_status" in - M) echo " ${FLOW_COLORS[warn]}M${FLOW_COLORS[reset]} $file" ;; - A) echo " ${FLOW_COLORS[success]}A${FLOW_COLORS[reset]} $file" ;; - D) echo " ${FLOW_COLORS[error]}D${FLOW_COLORS[reset]} $file" ;; - R*) echo " ${FLOW_COLORS[info]}R${FLOW_COLORS[reset]} $file" ;; - *) echo " ${FLOW_COLORS[muted]}$file_status${FLOW_COLORS[reset]} $file" ;; - esac - done <<< "$files_changed" - - # Count changes by type - local modified=$(echo "$files_changed" | grep -c "^M" || echo 0) - local added=$(echo "$files_changed" | grep -c "^A" || echo 0) - local deleted=$(echo "$files_changed" | grep -c "^D" || echo 0) - local total=$(echo "$files_changed" | wc -l | tr -d ' ') - - echo "" - echo "${FLOW_COLORS[dim]}Summary: $total files ($added added, $modified modified, $deleted deleted)${FLOW_COLORS[reset]}" - else - echo "${FLOW_COLORS[muted]}No changes detected${FLOW_COLORS[reset]}" - fi - - # Offer to view full diff - echo "" - echo -n "${FLOW_COLORS[prompt]}View full diff? [y/N]:${FLOW_COLORS[reset]} " - read -r view_diff - - case "$view_diff" in - y|Y|yes|Yes|YES) - echo "" - echo "${FLOW_COLORS[info]}Showing diff...${FLOW_COLORS[reset]}" - echo "${FLOW_COLORS[dim]}─────────────────────────────────────────────────${FLOW_COLORS[reset]}" - - # Show colorized diff (if available) - if command -v delta >/dev/null 2>&1; then - git diff "$prod_branch"..."$draft_branch" | delta - elif git config --get core.pager >/dev/null 2>&1; then - git diff "$prod_branch"..."$draft_branch" - else - git --no-pager diff --color=always "$prod_branch"..."$draft_branch" | less -R - fi - - echo "" - echo "${FLOW_COLORS[dim]}─────────────────────────────────────────────────${FLOW_COLORS[reset]}" - ;; - *) - # Skip viewing diff - ;; - esac - - echo "" - - # Decide whether to create PR or direct push - if [[ "$direct_push" == "true" ]]; then - echo "${FLOW_COLORS[warn]}⚠️ Direct push mode (bypassing PR)${FLOW_COLORS[reset]}" - echo "" - echo -n "${FLOW_COLORS[prompt]}Push directly to $prod_branch? [y/N]:${FLOW_COLORS[reset]} " - read -r direct_confirm - - case "$direct_confirm" in - y|Y|yes|Yes|YES) - git push origin "$draft_branch:$prod_branch" && \ - echo "${FLOW_COLORS[success]}✅ Pushed to $prod_branch${FLOW_COLORS[reset]}" || \ - return 1 - ;; - *) - echo "Direct push cancelled" - return 1 - ;; - esac - elif [[ "$auto_pr" == "true" ]]; then - # Create PR workflow - echo "${FLOW_COLORS[prompt]}Create pull request?${FLOW_COLORS[reset]}" - echo "" - echo " ${FLOW_COLORS[dim]}[1]${FLOW_COLORS[reset]} Yes - Create PR (Recommended)" - echo " ${FLOW_COLORS[dim]}[2]${FLOW_COLORS[reset]} Push to $draft_branch only (no PR)" - echo " ${FLOW_COLORS[dim]}[3]${FLOW_COLORS[reset]} Cancel" - echo "" - echo -n "${FLOW_COLORS[prompt]}Your choice [1-3]:${FLOW_COLORS[reset]} " - read -r pr_choice - - case "$pr_choice" in - 1) - echo "" - if _git_create_deploy_pr "$draft_branch" "$prod_branch" "$pr_title" "$pr_body"; then - echo "" - echo "${FLOW_COLORS[success]}✅ Pull Request Created${FLOW_COLORS[reset]}" - echo "${FLOW_COLORS[dim]}─────────────────────────────────────────────────${FLOW_COLORS[reset]}" - echo "" - echo " Next steps:" - echo " 1. Review PR on GitHub" - echo " 2. Merge when ready" - echo " 3. Site will auto-deploy after merge" - echo "" - else - return 1 - fi - ;; - 2) - if _git_push_current_branch; then - echo "" - echo "${FLOW_COLORS[success]}✓${FLOW_COLORS[reset]} Pushed to origin/$draft_branch" - echo " ${FLOW_COLORS[dim]}Create PR manually on GitHub when ready${FLOW_COLORS[reset]}" - else - return 1 - fi - ;; - 3|*) - echo "Deployment cancelled" - return 1 - ;; - esac - else - # auto_pr is false - just push to draft - if _git_push_current_branch; then - echo "" - echo "${FLOW_COLORS[success]}✓${FLOW_COLORS[reset]} Pushed to origin/$draft_branch" - echo " ${FLOW_COLORS[dim]}Create PR manually on GitHub${FLOW_COLORS[reset]}" - else - return 1 - fi - fi -} - -# Help for teach deploy -_teach_deploy_help() { - echo "teach deploy - Deploy teaching content via PR workflow" - echo "" - echo "Usage: teach deploy [options]" - echo "" - echo "Options:" - echo " --direct-push Bypass PR and push directly to production (advanced)" - echo " --help, -h Show this help message" - echo "" - echo "Workflow:" - echo " 1. Verify on draft branch" - echo " 2. Check for uncommitted changes" - echo " 3. Detect conflicts with production" - echo " 4. Create pull request (draft → production)" - echo "" - echo "Configuration (teach-config.yml):" - echo " git:" - echo " draft_branch: draft # Development branch" - echo " production_branch: main # Production branch" - echo " auto_pr: true # Auto-create PR" - echo " require_clean: true # Require clean state" - echo "" - echo "Examples:" - echo " teach deploy # Standard PR workflow" - echo " teach deploy --direct-push # Bypass PR (not recommended)" -} - # ============================================================================ # GIT CLEANUP WORKFLOW (Phase 3 - v5.11.0+) # ============================================================================ @@ -4054,92 +3720,6 @@ ${FLOW_COLORS[muted]}SEE ALSO:${FLOW_COLORS[reset]} EOF } -_teach_deploy_help() { - cat < EOF } +# ============================================================================= +# Function: _generate_smart_commit_message +# Purpose: Generate descriptive commit message from changed files +# ============================================================================= +# Arguments: +# $1 - (optional) Draft branch name +# $2 - (optional) Production branch name +# If both given, diffs between branches; otherwise uses staged/modified files +# +# Returns: +# 0 - Always succeeds +# +# Output: +# stdout - Single-line commit message like "content: week-05 lecture, assignment 3" +# +# Categories: +# lectures/*.qmd → "lecture" (extracts week number) +# labs/*.qmd → "lab" +# assignments/*.qmd → "assignment" (extracts number) +# exams/*.qmd → "exam" +# projects/*.qmd → "project" +# scripts/*.R|*.py → "script" +# home_*.qmd → "index" +# _quarto.yml → "config" +# _metadata.yml → "config" +# .flow/*.yml → "config" +# .STATUS → "config" +# *.css|*.scss → "style" +# images/*|img/* → "media" +# data/* → "data" +# *.qmd → "content" (catch-all) +# * → "misc" (catch-all) +# +# Prefix logic: +# If >50% files are one category, use that as prefix +# Otherwise use "deploy" +# +# Example: +# msg=$(_generate_smart_commit_message "draft" "gh-pages") +# # → "content: week-05 lecture, assignment 3" +# +# msg=$(_generate_smart_commit_message) +# # Uses staged files → "config: quarto settings, metadata" +# +# Notes: +# - Pure ZSH implementation (no external tools except git) +# - Messages truncated to 72 characters +# - Ported from STAT-545's generate_smart_message() logic +# ============================================================================= +_generate_smart_commit_message() { + local draft_branch="${1:-}" + local prod_branch="${2:-}" + local changed_files=() + + # Get changed files + if [[ -n "$draft_branch" && -n "$prod_branch" ]]; then + # Files different between branches + changed_files=(${(f)"$(git diff --name-only "$prod_branch"..."$draft_branch" 2>/dev/null)"}) + else + # Staged files + changed_files=(${(f)"$(git diff --cached --name-only 2>/dev/null)"}) + # If nothing staged, use modified files + if [[ ${#changed_files[@]} -eq 0 ]]; then + changed_files=(${(f)"$(git diff --name-only 2>/dev/null)"}) + fi + fi + + # If no files, generic message + if [[ ${#changed_files[@]} -eq 0 ]]; then + echo "deploy: update" + return 0 + fi + + # Categorize files + local -A categories # category -> count + local -a descriptions # human-readable descriptions + local total=${#changed_files[@]} + + for file in "${changed_files[@]}"; do + [[ -z "$file" ]] && continue + local basename="${file:t}" # filename only + local dirname="${file:h}" # directory only + local ext="${file:e}" # extension + + case "$file" in + lectures/*.qmd) + categories[content]=$(( ${categories[content]:-0} + 1 )) + # Extract week number + if [[ "$basename" =~ "week-([0-9]+)" ]]; then + descriptions+=("week-${match[1]} lecture") + else + descriptions+=("${basename%.qmd} lecture") + fi + ;; + labs/*.qmd) + categories[content]=$(( ${categories[content]:-0} + 1 )) + if [[ "$basename" =~ "week-([0-9]+)" ]]; then + descriptions+=("week-${match[1]} lab") + else + descriptions+=("${basename%.qmd} lab") + fi + ;; + assignments/*.qmd) + categories[content]=$(( ${categories[content]:-0} + 1 )) + if [[ "$basename" =~ "([0-9]+)" ]]; then + descriptions+=("assignment ${match[1]}") + else + descriptions+=("${basename%.qmd} assignment") + fi + ;; + exams/*.qmd) + categories[content]=$(( ${categories[content]:-0} + 1 )) + descriptions+=("${basename%.qmd} exam") + ;; + projects/*.qmd) + categories[content]=$(( ${categories[content]:-0} + 1 )) + descriptions+=("${basename%.qmd} project") + ;; + scripts/*.R|scripts/*.py) + categories[content]=$(( ${categories[content]:-0} + 1 )) + descriptions+=("${basename} script") + ;; + home_*.qmd) + categories[config]=$(( ${categories[config]:-0} + 1 )) + descriptions+=("index update") + ;; + _quarto.yml|_metadata.yml) + categories[config]=$(( ${categories[config]:-0} + 1 )) + descriptions+=("${basename}") + ;; + .flow/*.yml) + categories[config]=$(( ${categories[config]:-0} + 1 )) + descriptions+=("flow config") + ;; + .STATUS) + categories[config]=$(( ${categories[config]:-0} + 1 )) + descriptions+=("status") + ;; + *.css|*.scss) + categories[style]=$(( ${categories[style]:-0} + 1 )) + descriptions+=("style") + ;; + images/*|img/*) + categories[content]=$(( ${categories[content]:-0} + 1 )) + descriptions+=("media") + ;; + data/*) + categories[data]=$(( ${categories[data]:-0} + 1 )) + descriptions+=("data") + ;; + *.qmd) + categories[content]=$(( ${categories[content]:-0} + 1 )) + descriptions+=("${basename%.qmd}") + ;; + *) + categories[misc]=$(( ${categories[misc]:-0} + 1 )) + ;; + esac + done + + # Determine prefix from dominant category + local prefix="deploy" + local max_count=0 + for cat count in ${(kv)categories}; do + if [[ $count -gt $max_count ]]; then + max_count=$count + prefix="$cat" + fi + done + + # If dominant is >50% of total, use it; otherwise "deploy" + if [[ $max_count -le $(( total / 2 )) ]]; then + prefix="deploy" + fi + + # "misc" isn't a good prefix + [[ "$prefix" == "misc" ]] && prefix="deploy" + + # Deduplicate descriptions + local -a unique_descs=() + local -A seen_descs + for desc in "${descriptions[@]}"; do + [[ -z "$desc" ]] && continue + if [[ -z "${seen_descs[$desc]:-}" ]]; then + seen_descs[$desc]=1 + unique_descs+=("$desc") + fi + done + + # Build message body + local body="" + if [[ ${#unique_descs[@]} -eq 0 ]]; then + body="update" + elif [[ $total -gt 10 && ${#unique_descs[@]} -gt 5 ]]; then + body="full site update ($total files)" + elif [[ ${#unique_descs[@]} -le 3 ]]; then + body="${(j:, :)unique_descs}" + else + # Take first 3, add "+N more" + local first_three=("${unique_descs[@]:0:3}") + local remaining=$(( ${#unique_descs[@]} - 3 )) + body="${(j:, :)first_three} +${remaining} more" + fi + + # Truncate to 72 chars + local message="${prefix}: ${body}" + if [[ ${#message} -gt 72 ]]; then + message="${message:0:69}..." + fi + + echo "$message" + return 0 +} + # ============================================================================= # Function: _git_is_clean # Purpose: Check if working directory has no uncommitted changes diff --git a/mkdocs.yml b/mkdocs.yml index 146253bdc..0c9cc5693 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -157,6 +157,7 @@ nav: - Content Creation: guides/INTELLIGENT-CONTENT-ANALYSIS.md - Scholar Wrappers: guides/SCHOLAR-WRAPPERS-GUIDE.md - Deployment: guides/TEACH-DEPLOY-GUIDE.md + - Deploy v2 (Direct, History, Rollback): tutorials/31-teach-deploy-v2.md - Git Integration: tutorials/19-teaching-git-integration.md - Visual Workflow Guide: guides/TEACHING-WORKFLOW-VISUAL.md - Features: @@ -184,6 +185,7 @@ nav: - LaTeX Macros: reference/REFCARD-MACROS.md - Lesson Plans: reference/REFCARD-TEACH-PLAN.md - Prompts: reference/REFCARD-PROMPTS.md + - Deploy v2: reference/REFCARD-DEPLOY-V2.md - Scholar Flags: reference/REFCARD-SCHOLAR-FLAGS.md - Troubleshooting: guides/TEACHING-TROUBLESHOOTING.md - Help System: guides/HELP-SYSTEM-GUIDE.md diff --git a/package.json b/package.json index a529b2e82..6a46007f0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flow-cli", - "version": "6.2.0", + "version": "6.4.0", "description": "ADHD-optimized ZSH workflow plugin", "private": true, "scripts": { diff --git a/tests/dogfood-teach-deploy-v2.zsh b/tests/dogfood-teach-deploy-v2.zsh new file mode 100755 index 000000000..7ba18f6e0 --- /dev/null +++ b/tests/dogfood-teach-deploy-v2.zsh @@ -0,0 +1,949 @@ +#!/usr/bin/env zsh +# dogfood-teach-deploy-v2.zsh - Non-interactive dogfooding for teach deploy v2 +# Run with: zsh tests/dogfood-teach-deploy-v2.zsh +# +# Tests the REAL plugin functions against the demo course fixture. +# Loads the full plugin (source flow.plugin.zsh) -- not mocked. +# +# Sections: +# 1. Plugin Load Verification (4 tests) +# 2. Help Output (6 tests) +# 3. Smart Commit Messages (6 tests) +# 4. Deploy History Helpers (8 tests) +# 5. Deploy Rollback Helpers (3 tests) +# 6. Preflight Checks (Demo Course) (6 tests) +# 7. Dry-Run Preview (5 tests) +# 8. .STATUS Updates (4 tests) +# 9. Flag Parsing (5 tests) +# 10. Full Deploy Lifecycle E2E (4 tests) +# Total: ~51 tests + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +CYAN='\033[0;36m' +DIM='\033[2m' +RESET='\033[0m' + +# Counters +typeset -g TESTS_RUN=0 +typeset -g TESTS_PASSED=0 +typeset -g TESTS_FAILED=0 +typeset -g TESTS_SKIPPED=0 + +# Get script directory +SCRIPT_DIR="${0:A:h}" +PROJECT_ROOT="${SCRIPT_DIR}/.." +DEMO_COURSE="$PROJECT_ROOT/tests/fixtures/demo-course" + +# Global temp dirs to clean up +typeset -ga _DOGFOOD_TEMP_DIRS=() + +cleanup_all() { + for d in "${_DOGFOOD_TEMP_DIRS[@]}"; do + [[ -d "$d" ]] && rm -rf "$d" + done +} +trap cleanup_all EXIT + +# Load plugin +echo "${CYAN}Loading flow-cli plugin...${RESET}" +source "$PROJECT_ROOT/flow.plugin.zsh" 2>/dev/null || { + echo "${RED}ERROR: Failed to load plugin${RESET}" + exit 1 +} +echo "${GREEN}Plugin loaded${RESET}" +echo "" + +# ============================================================================ +# Test runner -- exit 0=pass, 77=skip, other=fail +# ============================================================================ +run_test() { + local test_name="$1" + local test_func="$2" + + TESTS_RUN=$((TESTS_RUN + 1)) + echo -n "${CYAN}[$TESTS_RUN] $test_name...${RESET} " + + local output + output=$(eval "$test_func" 2>&1) + local exit_code=$? + + if [[ $exit_code -eq 0 ]]; then + echo "${GREEN}PASS${RESET}" + TESTS_PASSED=$((TESTS_PASSED + 1)) + elif [[ $exit_code -eq 77 ]]; then + echo "${YELLOW}SKIP${RESET}" + TESTS_SKIPPED=$((TESTS_SKIPPED + 1)) + else + echo "${RED}FAIL${RESET}" + echo " ${DIM}Output: ${output:0:200}${RESET}" + TESTS_FAILED=$((TESTS_FAILED + 1)) + fi +} + +# ============================================================================ +# yq probe -- some sandboxed environments block yq inside functions +# ============================================================================ +_YQ_AVAILABLE=false +if command -v yq >/dev/null 2>&1; then + _probe=$(echo "test: value" | yq '.test' 2>/dev/null) + [[ "$_probe" == "value" ]] && _YQ_AVAILABLE=true + unset _probe +fi + +if [[ "$_YQ_AVAILABLE" != "true" ]]; then + echo "${YELLOW}Warning: yq not available -- some tests will be skipped${RESET}" + echo "" +fi + +# ============================================================================ +# HELPER: Create sandboxed git repo with draft/main branches +# Returns the temp dir path on stdout +# ============================================================================ +_create_test_repo() { + local tmpdir=$(mktemp -d) + _DOGFOOD_TEMP_DIRS+=("$tmpdir") + + ( + cd "$tmpdir" + git init -q + git config user.email "test@example.com" + git config user.name "Test" + + # Initial commit on main + mkdir -p .flow lectures + cat > .flow/teach-config.yml <<'YAML' +course: + name: 'TEST-200' +semester_info: + start_date: '2026-08-26' +YAML + echo "# Test Course" > README.md + git add -A && git commit -q -m "init" + + # Create draft branch with content + git checkout -q -b draft + echo "---\ntitle: Week 1\n---\n# Lecture" > lectures/week-01.qmd + git add -A && git commit -q -m "add week-01 lecture" + ) >/dev/null 2>&1 + + echo "$tmpdir" +} + +# ============================================================================ +# HELPER: Create test repo as a copy of demo course +# ============================================================================ +_create_demo_repo() { + local tmpdir=$(mktemp -d) + local remotedir=$(mktemp -d) + _DOGFOOD_TEMP_DIRS+=("$tmpdir" "$remotedir") + + ( + # Create bare remote first + cd "$remotedir" && git init --bare -q + ) >/dev/null 2>&1 + + ( + cp -R "$DEMO_COURSE"/. "$tmpdir"/ + cd "$tmpdir" + git init -q + git config user.email "test@example.com" + git config user.name "Test" + git add -A && git commit -q -m "init demo course" + + # Set up remote and push main + git remote add origin "$remotedir" + git push -q origin main 2>/dev/null + + # Create draft branch + git checkout -q -b draft + # Add a small change so there is something to deploy + echo "\n## Updated" >> lectures/week-01.qmd + git add -A && git commit -q -m "update week-01" + git push -q origin draft 2>/dev/null + ) >/dev/null 2>&1 + + echo "$tmpdir" +} + +# ============================================================================ +# SECTION 1: Plugin Load Verification +# ============================================================================ +echo "${CYAN}--- Section 1: Plugin Load Verification ---${RESET}" + +run_test "Core deploy v2 functions are loaded" ' + typeset -f _teach_deploy_enhanced >/dev/null 2>&1 || return 1 + typeset -f _deploy_preflight_checks >/dev/null 2>&1 || return 1 + typeset -f _deploy_direct_merge >/dev/null 2>&1 || return 1 + typeset -f _deploy_dry_run_report >/dev/null 2>&1 || return 1 +' + +run_test "Deploy history functions are loaded" ' + typeset -f _deploy_history_append >/dev/null 2>&1 || return 1 + typeset -f _deploy_history_list >/dev/null 2>&1 || return 1 + typeset -f _deploy_history_count >/dev/null 2>&1 || return 1 + typeset -f _deploy_history_get >/dev/null 2>&1 || return 1 +' + +run_test "Deploy rollback functions are loaded" ' + typeset -f _deploy_rollback >/dev/null 2>&1 || return 1 + typeset -f _deploy_perform_rollback >/dev/null 2>&1 || return 1 +' + +run_test "Smart commit and status helpers are loaded" ' + typeset -f _generate_smart_commit_message >/dev/null 2>&1 || return 1 + typeset -f _deploy_update_status_file >/dev/null 2>&1 || return 1 +' + +echo "" + +# ============================================================================ +# SECTION 2: Help Output +# ============================================================================ +echo "${CYAN}--- Section 2: Help Output ---${RESET}" + +run_test "Help function produces output" ' + local output + output=$(_teach_deploy_enhanced_help 2>&1) + [[ -n "$output" ]] || return 1 +' + +run_test "Help contains --direct flag" ' + local output + output=$(_teach_deploy_enhanced_help 2>&1) + [[ "$output" == *"--direct"* ]] || return 1 +' + +run_test "Help contains --rollback flag" ' + local output + output=$(_teach_deploy_enhanced_help 2>&1) + [[ "$output" == *"--rollback"* ]] || return 1 +' + +run_test "Help contains --history flag" ' + local output + output=$(_teach_deploy_enhanced_help 2>&1) + [[ "$output" == *"--history"* ]] || return 1 +' + +run_test "Help contains --dry-run flag" ' + local output + output=$(_teach_deploy_enhanced_help 2>&1) + [[ "$output" == *"--dry-run"* ]] || return 1 +' + +run_test "Help contains --ci flag" ' + local output + output=$(_teach_deploy_enhanced_help 2>&1) + [[ "$output" == *"--ci"* ]] || return 1 +' + +echo "" + +# ============================================================================ +# SECTION 3: Smart Commit Messages +# ============================================================================ +echo "${CYAN}--- Section 3: Smart Commit Messages ---${RESET}" + +run_test "Lecture files produce content: prefix" ' + local tmpdir=$(_create_test_repo) + local msg + msg=$(cd "$tmpdir" && _generate_smart_commit_message "draft" "main" 2>/dev/null) + [[ "$msg" == content:* ]] || return 1 +' + +run_test "Config files produce config: prefix" ' + local tmpdir=$(mktemp -d) + _DOGFOOD_TEMP_DIRS+=("$tmpdir") + ( + cd "$tmpdir" + git init -q + git config user.email "test@example.com" + git config user.name "Test" + echo "initial" > README.md + git add -A && git commit -q -m "init" + git checkout -q -b draft + echo "project:" > _quarto.yml + echo "theme: custom" > .flow/config.yml + mkdir -p .flow + echo "flow:" > .flow/config.yml + git add -A && git commit -q -m "add config" + ) >/dev/null 2>&1 + local msg + msg=$(cd "$tmpdir" && _generate_smart_commit_message "draft" "main" 2>/dev/null) + [[ "$msg" == *"config"* ]] || return 1 +' + +run_test "Assignment files produce content with assignment" ' + local tmpdir=$(mktemp -d) + _DOGFOOD_TEMP_DIRS+=("$tmpdir") + ( + cd "$tmpdir" + git init -q + git config user.email "test@example.com" + git config user.name "Test" + echo "initial" > README.md + git add -A && git commit -q -m "init" + git checkout -q -b draft + mkdir -p assignments + echo "---\ntitle: HW3\n---" > assignments/hw3.qmd + git add -A && git commit -q -m "add assignment" + ) >/dev/null 2>&1 + local msg + msg=$(cd "$tmpdir" && _generate_smart_commit_message "draft" "main" 2>/dev/null) + [[ "$msg" == *"assignment"* ]] || return 1 +' + +run_test "Mixed files produce deploy: prefix" ' + local tmpdir=$(mktemp -d) + _DOGFOOD_TEMP_DIRS+=("$tmpdir") + ( + cd "$tmpdir" + git init -q + git config user.email "test@example.com" + git config user.name "Test" + echo "initial" > README.md + git add -A && git commit -q -m "init" + git checkout -q -b draft + mkdir -p lectures data .flow + echo "lecture" > lectures/week-01.qmd + echo "data" > data/dataset.csv + echo "config:" > _quarto.yml + echo "theme" > style.css + git add -A && git commit -q -m "add mixed" + ) >/dev/null 2>&1 + local msg + msg=$(cd "$tmpdir" && _generate_smart_commit_message "draft" "main" 2>/dev/null) + # Mixed content should not be 100% one category so prefix is "deploy" or a dominant cat + [[ -n "$msg" ]] || return 1 +' + +run_test "No changes produce fallback message" ' + local tmpdir=$(mktemp -d) + _DOGFOOD_TEMP_DIRS+=("$tmpdir") + ( + cd "$tmpdir" + git init -q + git config user.email "test@example.com" + git config user.name "Test" + echo "initial" > README.md + git add -A && git commit -q -m "init" + git checkout -q -b draft + # No changes between draft and main + ) >/dev/null 2>&1 + local msg + msg=$(cd "$tmpdir" && _generate_smart_commit_message "draft" "main" 2>/dev/null) + [[ "$msg" == "deploy: update" ]] || return 1 +' + +run_test "Message truncates at 72 characters" ' + local tmpdir=$(mktemp -d) + _DOGFOOD_TEMP_DIRS+=("$tmpdir") + ( + cd "$tmpdir" + git init -q + git config user.email "test@example.com" + git config user.name "Test" + echo "initial" > README.md + git add -A && git commit -q -m "init" + git checkout -q -b draft + mkdir -p lectures + for i in {01..20}; do + echo "week $i" > "lectures/week-$i-very-long-name-for-testing.qmd" + done + git add -A && git commit -q -m "add many lectures" + ) >/dev/null 2>&1 + local msg + msg=$(cd "$tmpdir" && _generate_smart_commit_message "draft" "main" 2>/dev/null) + [[ ${#msg} -le 72 ]] || return 1 +' + +echo "" + +# ============================================================================ +# SECTION 4: Deploy History Helpers +# ============================================================================ +echo "${CYAN}--- Section 4: Deploy History Helpers ---${RESET}" + +run_test "History append creates file when missing" ' + local tmpdir=$(mktemp -d) + _DOGFOOD_TEMP_DIRS+=("$tmpdir") + ( + cd "$tmpdir" + _deploy_history_append "direct" "abc12345" "def67890" "draft" "main" "5" "test deploy" "null" "null" "10" + ) >/dev/null 2>&1 + [[ -f "$tmpdir/.flow/deploy-history.yml" ]] || return 1 +' + +run_test "History append writes valid YAML with deploys key" ' + [[ "$_YQ_AVAILABLE" == "true" ]] || return 77 + local tmpdir=$(mktemp -d) + _DOGFOOD_TEMP_DIRS+=("$tmpdir") + ( + cd "$tmpdir" + _deploy_history_append "direct" "abc12345" "def67890" "draft" "main" "5" "test deploy" "null" "null" "10" + ) >/dev/null 2>&1 + local top_key + top_key=$(yq "has(\"deploys\")" "$tmpdir/.flow/deploy-history.yml" 2>/dev/null) + [[ "$top_key" == "true" ]] || return 1 +' + +run_test "History count returns correct count after appends" ' + [[ "$_YQ_AVAILABLE" == "true" ]] || return 77 + local tmpdir=$(mktemp -d) + _DOGFOOD_TEMP_DIRS+=("$tmpdir") + ( + cd "$tmpdir" + _deploy_history_append "direct" "aaaa1111" "" "draft" "main" "3" "first deploy" "null" "null" "8" + _deploy_history_append "direct" "bbbb2222" "aaaa1111" "draft" "main" "5" "second deploy" "null" "null" "12" + _deploy_history_append "pr" "cccc3333" "bbbb2222" "draft" "main" "2" "third deploy" "42" "null" "60" + ) >/dev/null 2>&1 + local count + count=$(cd "$tmpdir" && _deploy_history_count 2>/dev/null) + [[ "$count" == "3" ]] || return 1 +' + +run_test "History list produces table output" ' + [[ "$_YQ_AVAILABLE" == "true" ]] || return 77 + local tmpdir=$(mktemp -d) + _DOGFOOD_TEMP_DIRS+=("$tmpdir") + ( + cd "$tmpdir" + _deploy_history_append "direct" "aaaa1111" "" "draft" "main" "3" "first deploy" "null" "null" "8" + _deploy_history_append "direct" "bbbb2222" "aaaa1111" "draft" "main" "5" "second deploy" "null" "null" "12" + ) >/dev/null 2>&1 + local output + output=$(cd "$tmpdir" && _deploy_history_list 5 2>&1) + [[ "$output" == *"Recent deployments"* ]] || return 1 + [[ "$output" == *"#"* ]] || return 1 +' + +run_test "History get retrieves most recent entry (index 1)" ' + [[ "$_YQ_AVAILABLE" == "true" ]] || return 77 + local tmpdir=$(mktemp -d) + _DOGFOOD_TEMP_DIRS+=("$tmpdir") + ( + cd "$tmpdir" + _deploy_history_append "direct" "aaaa1111" "" "draft" "main" "3" "first deploy" "null" "null" "8" + _deploy_history_append "pr" "bbbb2222" "aaaa1111" "draft" "main" "7" "latest deploy" "99" "null" "45" + ) >/dev/null 2>&1 + local result + result=$( + cd "$tmpdir" + _deploy_history_get 1 + echo "$DEPLOY_HIST_MODE" + ) + [[ "$result" == "pr" ]] || return 1 +' + +run_test "History get with index 2 retrieves second most recent" ' + [[ "$_YQ_AVAILABLE" == "true" ]] || return 77 + local tmpdir=$(mktemp -d) + _DOGFOOD_TEMP_DIRS+=("$tmpdir") + ( + cd "$tmpdir" + _deploy_history_append "direct" "aaaa1111" "" "draft" "main" "3" "first deploy" "null" "null" "8" + _deploy_history_append "pr" "bbbb2222" "aaaa1111" "draft" "main" "7" "latest deploy" "99" "null" "45" + ) >/dev/null 2>&1 + local result + result=$( + cd "$tmpdir" + _deploy_history_get 2 + echo "$DEPLOY_HIST_MODE" + ) + [[ "$result" == "direct" ]] || return 1 +' + +run_test "Single quotes in commit message are escaped" ' + local tmpdir=$(mktemp -d) + _DOGFOOD_TEMP_DIRS+=("$tmpdir") + ( + cd "$tmpdir" + _deploy_history_append "direct" "abc12345" "" "draft" "main" "1" "it'\''s a test" "null" "null" "5" + ) >/dev/null 2>&1 + # File should exist and not have broken YAML + [[ -f "$tmpdir/.flow/deploy-history.yml" ]] || return 1 + # Basic check: file has the escaped content (double single quotes) + grep -q "it" "$tmpdir/.flow/deploy-history.yml" || return 1 +' + +run_test "Multiple appends do not corrupt history file" ' + [[ "$_YQ_AVAILABLE" == "true" ]] || return 77 + local tmpdir=$(mktemp -d) + _DOGFOOD_TEMP_DIRS+=("$tmpdir") + ( + cd "$tmpdir" + for i in {1..5}; do + _deploy_history_append "direct" "hash000$i" "" "draft" "main" "$i" "deploy number $i" "null" "null" "$((i * 10))" + done + ) >/dev/null 2>&1 + # Use _deploy_history_count which calls yq internally -- verifies parsability + local count + count=$(cd "$tmpdir" && _deploy_history_count 2>/dev/null) + [[ "$count" == "5" ]] || return 1 +' + +echo "" + +# ============================================================================ +# SECTION 5: Deploy Rollback Helpers +# ============================================================================ +echo "${CYAN}--- Section 5: Deploy Rollback Helpers ---${RESET}" + +run_test "Rollback in CI mode without index returns error" ' + local tmpdir=$(mktemp -d) + _DOGFOOD_TEMP_DIRS+=("$tmpdir") + ( + cd "$tmpdir" + git init -q + git config user.email "test@example.com" + git config user.name "Test" + echo "x" > f.txt && git add -A && git commit -q -m "init" + mkdir -p .flow + _deploy_history_append "direct" "abc12345" "" "draft" "main" "1" "test" "null" "null" "5" + ) >/dev/null 2>&1 + local output + output=$(cd "$tmpdir" && _deploy_rollback --ci 2>&1) + local rc=$? + [[ $rc -ne 0 ]] || return 1 + [[ "$output" == *"CI mode requires explicit"* ]] || return 1 +' + +run_test "Rollback with invalid index returns error" ' + [[ "$_YQ_AVAILABLE" == "true" ]] || return 77 + local tmpdir=$(mktemp -d) + _DOGFOOD_TEMP_DIRS+=("$tmpdir") + ( + cd "$tmpdir" + git init -q + git config user.email "test@example.com" + git config user.name "Test" + echo "x" > f.txt && git add -A && git commit -q -m "init" + mkdir -p .flow + _deploy_history_append "direct" "abc12345" "" "draft" "main" "1" "test" "null" "null" "5" + ) >/dev/null 2>&1 + local output + output=$(cd "$tmpdir" && _deploy_rollback 99 --ci 2>&1) + local rc=$? + [[ $rc -ne 0 ]] || return 1 + [[ "$output" == *"Invalid"* ]] || return 1 +' + +run_test "Rollback with no history returns error" ' + local tmpdir=$(mktemp -d) + _DOGFOOD_TEMP_DIRS+=("$tmpdir") + ( + cd "$tmpdir" + git init -q + git config user.email "test@example.com" + git config user.name "Test" + echo "x" > f.txt && git add -A && git commit -q -m "init" + ) >/dev/null 2>&1 + local output + output=$(cd "$tmpdir" && _deploy_rollback 1 --ci 2>&1) + local rc=$? + [[ $rc -ne 0 ]] || return 1 + [[ "$output" == *"No deployment history"* ]] || return 1 +' + +echo "" + +# ============================================================================ +# SECTION 6: Preflight Checks with Demo Course +# ============================================================================ +echo "${CYAN}--- Section 6: Preflight Checks (Demo Course) ---${RESET}" + +run_test "Preflight succeeds in demo-course git repo on draft branch" ' + [[ "$_YQ_AVAILABLE" == "true" ]] || return 77 + local tmpdir=$(_create_demo_repo) + local output + output=$(cd "$tmpdir" && _deploy_preflight_checks "true" 2>&1) + local rc=$? + [[ $rc -eq 0 ]] || return 1 +' + +run_test "Preflight sets DEPLOY_COURSE_NAME to STAT-101" ' + [[ "$_YQ_AVAILABLE" == "true" ]] || return 77 + local tmpdir=$(_create_demo_repo) + local name + name=$( + cd "$tmpdir" + _deploy_preflight_checks "true" >/dev/null 2>&1 + echo "$DEPLOY_COURSE_NAME" + ) + [[ "$name" == "STAT-101" ]] || return 1 +' + +run_test "Preflight sets DEPLOY_DRAFT_BRANCH to draft (default)" ' + [[ "$_YQ_AVAILABLE" == "true" ]] || return 77 + local tmpdir=$(_create_demo_repo) + local branch + branch=$( + cd "$tmpdir" + _deploy_preflight_checks "true" >/dev/null 2>&1 + echo "$DEPLOY_DRAFT_BRANCH" + ) + [[ "$branch" == "draft" ]] || return 1 +' + +run_test "Preflight sets DEPLOY_PROD_BRANCH to main (default)" ' + [[ "$_YQ_AVAILABLE" == "true" ]] || return 77 + local tmpdir=$(_create_demo_repo) + local branch + branch=$( + cd "$tmpdir" + _deploy_preflight_checks "true" >/dev/null 2>&1 + echo "$DEPLOY_PROD_BRANCH" + ) + [[ "$branch" == "main" ]] || return 1 +' + +run_test "Preflight fails outside git repo" ' + local tmpdir=$(mktemp -d) + _DOGFOOD_TEMP_DIRS+=("$tmpdir") + mkdir -p "$tmpdir/.flow" + echo "course:\n name: X" > "$tmpdir/.flow/teach-config.yml" + local output + output=$(cd "$tmpdir" && _deploy_preflight_checks "true" 2>&1) + local rc=$? + [[ $rc -ne 0 ]] || return 1 +' + +run_test "Preflight fails without .flow/teach-config.yml" ' + local tmpdir=$(mktemp -d) + _DOGFOOD_TEMP_DIRS+=("$tmpdir") + ( + cd "$tmpdir" + git init -q + git config user.email "test@example.com" + git config user.name "Test" + echo "x" > f.txt && git add -A && git commit -q -m "init" + ) >/dev/null 2>&1 + local output + output=$(cd "$tmpdir" && _deploy_preflight_checks "true" 2>&1) + local rc=$? + [[ $rc -ne 0 ]] || return 1 + [[ "$output" == *"teach-config.yml"* ]] || return 1 +' + +echo "" + +# ============================================================================ +# SECTION 7: Dry-Run Preview +# ============================================================================ +echo "${CYAN}--- Section 7: Dry-Run Preview ---${RESET}" + +run_test "Dry-run output contains DRY RUN" ' + [[ "$_YQ_AVAILABLE" == "true" ]] || return 77 + local tmpdir=$(_create_demo_repo) + local output + output=$(cd "$tmpdir" && _teach_deploy_enhanced --dry-run --ci 2>&1) + [[ "$output" == *"DRY RUN"* ]] || return 1 +' + +run_test "Dry-run with --direct mentions direct mode" ' + [[ "$_YQ_AVAILABLE" == "true" ]] || return 77 + local tmpdir=$(_create_demo_repo) + local output + output=$(cd "$tmpdir" && _teach_deploy_enhanced --dry-run --direct --ci 2>&1) + [[ "$output" == *"direct"* ]] || return 1 +' + +run_test "Dry-run does NOT modify git state" ' + [[ "$_YQ_AVAILABLE" == "true" ]] || return 77 + local tmpdir=$(_create_demo_repo) + local hash_before hash_after + hash_before=$(cd "$tmpdir" && git rev-parse HEAD 2>/dev/null) + (cd "$tmpdir" && _teach_deploy_enhanced --dry-run --ci) >/dev/null 2>&1 + hash_after=$(cd "$tmpdir" && git rev-parse HEAD 2>/dev/null) + [[ "$hash_before" == "$hash_after" ]] || return 1 +' + +run_test "Dry-run with custom message shows the message" ' + [[ "$_YQ_AVAILABLE" == "true" ]] || return 77 + local tmpdir=$(_create_demo_repo) + local output + output=$(cd "$tmpdir" && _teach_deploy_enhanced --dry-run -m "Custom deploy msg" --ci 2>&1) + [[ "$output" == *"Custom deploy msg"* ]] || return 1 +' + +run_test "Dry-run shows file count" ' + [[ "$_YQ_AVAILABLE" == "true" ]] || return 77 + local tmpdir=$(_create_demo_repo) + local output + output=$(cd "$tmpdir" && _teach_deploy_enhanced --dry-run --ci 2>&1) + # Output should mention a number of files + [[ "$output" == *"file"* ]] || return 1 +' + +echo "" + +# ============================================================================ +# SECTION 8: .STATUS Updates +# ============================================================================ +echo "${CYAN}--- Section 8: .STATUS Updates ---${RESET}" + +run_test "Status update skips when no .STATUS file (returns 0)" ' + local tmpdir=$(mktemp -d) + _DOGFOOD_TEMP_DIRS+=("$tmpdir") + ( + cd "$tmpdir" + git init -q + git config user.email "test@example.com" + git config user.name "Test" + echo "x" > f.txt && git add -A && git commit -q -m "init" + ) >/dev/null 2>&1 + (cd "$tmpdir" && _deploy_update_status_file) >/dev/null 2>&1 + local rc=$? + [[ $rc -eq 0 ]] || return 1 + # Should NOT create a .STATUS file + [[ ! -f "$tmpdir/.STATUS" ]] || return 1 +' + +run_test "Status update writes last_deploy when .STATUS exists" ' + [[ "$_YQ_AVAILABLE" == "true" ]] || return 77 + local tmpdir=$(mktemp -d) + _DOGFOOD_TEMP_DIRS+=("$tmpdir") + ( + cd "$tmpdir" + git init -q + git config user.email "test@example.com" + git config user.name "Test" + mkdir -p .flow + echo "course:\n name: X" > .flow/teach-config.yml + echo "status: active" > .STATUS + git add -A && git commit -q -m "init" + ) >/dev/null 2>&1 + (cd "$tmpdir" && _deploy_update_status_file) >/dev/null 2>&1 + local last_deploy + last_deploy=$(yq '.last_deploy' "$tmpdir/.STATUS" 2>/dev/null) + [[ -n "$last_deploy" && "$last_deploy" != "null" ]] || return 1 +' + +run_test "Status update writes deploy_count when history exists" ' + [[ "$_YQ_AVAILABLE" == "true" ]] || return 77 + local tmpdir=$(mktemp -d) + _DOGFOOD_TEMP_DIRS+=("$tmpdir") + ( + cd "$tmpdir" + git init -q + git config user.email "test@example.com" + git config user.name "Test" + mkdir -p .flow + echo "course:\n name: X" > .flow/teach-config.yml + echo "status: active" > .STATUS + _deploy_history_append "direct" "abc12345" "" "draft" "main" "3" "test" "null" "null" "5" + _deploy_history_append "direct" "def67890" "abc12345" "draft" "main" "2" "test2" "null" "null" "8" + git add -A && git commit -q -m "init" + ) >/dev/null 2>&1 + (cd "$tmpdir" && _deploy_update_status_file) >/dev/null 2>&1 + local count + count=$(yq '.deploy_count' "$tmpdir/.STATUS" 2>/dev/null) + [[ "$count" == "2" ]] || return 1 +' + +_test_status_teaching_week() { + [[ "$_YQ_AVAILABLE" == "true" ]] || return 77 + local tmpdir=$(mktemp -d) + _DOGFOOD_TEMP_DIRS+=("$tmpdir") + local two_weeks_ago + two_weeks_ago=$(date -v-14d "+%Y-%m-%d" 2>/dev/null || date -d "14 days ago" "+%Y-%m-%d" 2>/dev/null) + if [[ -z "$two_weeks_ago" ]]; then + return 77 # Skip if date math fails (non-macOS) + fi + ( + cd "$tmpdir" + git init -q + git config user.email "test@example.com" + git config user.name "Test" + mkdir -p .flow + printf "course:\n name: WEEK-TEST\nsemester_info:\n start_date: '%s'\n" "$two_weeks_ago" > .flow/teach-config.yml + echo "status: active" > .STATUS + git add -A && git commit -q -m "init" + ) >/dev/null 2>&1 + (cd "$tmpdir" && _deploy_update_status_file) >/dev/null 2>&1 + local week + week=$(yq '.teaching_week' "$tmpdir/.STATUS" 2>/dev/null) + # Should be a positive number (2 or 3 depending on rounding) + [[ -n "$week" && "$week" != "null" && "$week" -ge 1 ]] || return 1 +} +run_test "Status update calculates teaching_week from start_date" '_test_status_teaching_week' + +echo "" + +# ============================================================================ +# SECTION 9: Flag Parsing +# ============================================================================ +echo "${CYAN}--- Section 9: Flag Parsing ---${RESET}" + +run_test "--help produces help output without error" ' + local output + output=$(_teach_deploy_enhanced --help 2>&1) + local rc=$? + [[ $rc -eq 0 ]] || return 1 + [[ "$output" == *"teach deploy"* ]] || return 1 +' + +run_test "Unknown flag --bogus returns error" ' + local output + output=$(_teach_deploy_enhanced --bogus 2>&1) + local rc=$? + [[ $rc -ne 0 ]] || return 1 + [[ "$output" == *"Unknown flag"* ]] || return 1 +' + +run_test "--direct-push is accepted (backward compat alias)" ' + [[ "$_YQ_AVAILABLE" == "true" ]] || return 77 + local tmpdir=$(_create_demo_repo) + # Use dry-run to test flag acceptance without side effects + local output + output=$(cd "$tmpdir" && _teach_deploy_enhanced --dry-run --direct-push --ci 2>&1) + # Should not say "Unknown flag" + [[ "$output" != *"Unknown flag"* ]] || return 1 +' + +run_test "--rollback dispatches without full preflight" ' + local tmpdir=$(mktemp -d) + _DOGFOOD_TEMP_DIRS+=("$tmpdir") + ( + cd "$tmpdir" + git init -q + git config user.email "test@example.com" + git config user.name "Test" + echo "x" > f.txt && git add -A && git commit -q -m "init" + # No .flow/teach-config.yml -- preflight would fail if called + ) >/dev/null 2>&1 + local output + output=$(cd "$tmpdir" && _teach_deploy_enhanced --rollback 1 --ci 2>&1) + # Should fail with "No deployment history" NOT "teach-config.yml not found" + [[ "$output" == *"history"* || "$output" == *"History"* ]] || return 1 + [[ "$output" != *"teach-config.yml not found"* ]] || return 1 +' + +run_test "--history dispatches without full preflight" ' + local tmpdir=$(mktemp -d) + _DOGFOOD_TEMP_DIRS+=("$tmpdir") + ( + cd "$tmpdir" + # No git repo, no config -- should just report no history + ) >/dev/null 2>&1 + local output + output=$(cd "$tmpdir" && _teach_deploy_enhanced --history 2>&1) + # Should mention no history, NOT preflight failure + [[ "$output" == *"No deploy"* || "$output" == *"history"* ]] || return 1 + [[ "$output" != *"teach-config.yml not found"* ]] || return 1 +' + +echo "" + +# ============================================================================ +# SECTION 10: Full Deploy Lifecycle E2E +# ============================================================================ +echo "${CYAN}--- Section 10: Full Deploy Lifecycle E2E ---${RESET}" + +run_test "Direct deploy in sandboxed repo completes successfully" ' + [[ "$_YQ_AVAILABLE" == "true" ]] || return 77 + local tmpdir=$(_create_test_repo) + + # Direct deploy needs a remote to push to. + # Create a bare remote alongside. + local remote_dir=$(mktemp -d) + _DOGFOOD_TEMP_DIRS+=("$remote_dir") + ( + cd "$remote_dir" && git init --bare -q + ) >/dev/null 2>&1 + ( + cd "$tmpdir" + git remote add origin "$remote_dir" + # Push both branches to the bare remote + git push -q origin main 2>/dev/null + git push -q origin draft 2>/dev/null + ) >/dev/null 2>&1 + + local output + output=$(cd "$tmpdir" && _teach_deploy_enhanced --direct --ci 2>&1) + local rc=$? + [[ $rc -eq 0 ]] || return 1 +' + +run_test "After direct deploy, history file exists with 1 entry" ' + [[ "$_YQ_AVAILABLE" == "true" ]] || return 77 + local tmpdir=$(_create_test_repo) + + local remote_dir=$(mktemp -d) + _DOGFOOD_TEMP_DIRS+=("$remote_dir") + (cd "$remote_dir" && git init --bare -q) >/dev/null 2>&1 + ( + cd "$tmpdir" + git remote add origin "$remote_dir" + git push -q origin main 2>/dev/null + git push -q origin draft 2>/dev/null + ) >/dev/null 2>&1 + + (cd "$tmpdir" && _teach_deploy_enhanced --direct --ci) >/dev/null 2>&1 + + [[ -f "$tmpdir/.flow/deploy-history.yml" ]] || return 1 + local count + count=$(cd "$tmpdir" && _deploy_history_count 2>/dev/null) + [[ "$count" == "1" ]] || return 1 +' + +run_test "History list after deploy shows the entry" ' + [[ "$_YQ_AVAILABLE" == "true" ]] || return 77 + local tmpdir=$(_create_test_repo) + + local remote_dir=$(mktemp -d) + _DOGFOOD_TEMP_DIRS+=("$remote_dir") + (cd "$remote_dir" && git init --bare -q) >/dev/null 2>&1 + ( + cd "$tmpdir" + git remote add origin "$remote_dir" + git push -q origin main 2>/dev/null + git push -q origin draft 2>/dev/null + ) >/dev/null 2>&1 + + (cd "$tmpdir" && _teach_deploy_enhanced --direct --ci) >/dev/null 2>&1 + + local output + output=$(cd "$tmpdir" && _deploy_history_list 5 2>&1) + [[ "$output" == *"direct"* ]] || return 1 + [[ "$output" == *"Recent deployments"* ]] || return 1 +' + +run_test "Dry-run does NOT create history entry" ' + [[ "$_YQ_AVAILABLE" == "true" ]] || return 77 + local tmpdir=$(_create_demo_repo) + # Start without history + [[ ! -f "$tmpdir/.flow/deploy-history.yml" ]] || rm -f "$tmpdir/.flow/deploy-history.yml" + + (cd "$tmpdir" && _teach_deploy_enhanced --dry-run --direct --ci) >/dev/null 2>&1 + + # History file should NOT exist (dry-run does not record) + if [[ -f "$tmpdir/.flow/deploy-history.yml" ]]; then + local count + count=$(cd "$tmpdir" && _deploy_history_count 2>/dev/null) + [[ "$count" == "0" ]] || return 1 + fi + return 0 +' + +echo "" + +# ============================================================================ +# Summary +# ============================================================================ +echo "=================================================" +echo "" +if [[ $TESTS_FAILED -eq 0 ]]; then + echo "${GREEN}All $TESTS_PASSED/$TESTS_RUN tests passed${RESET}" + [[ $TESTS_SKIPPED -gt 0 ]] && echo " ${YELLOW}($TESTS_SKIPPED skipped -- yq not available in sandbox)${RESET}" +else + echo "${RED}$TESTS_FAILED/$TESTS_RUN tests failed${RESET}" + echo " ${GREEN}$TESTS_PASSED passed${RESET}, ${RED}$TESTS_FAILED failed${RESET}" + [[ $TESTS_SKIPPED -gt 0 ]] && echo " ${YELLOW}$TESTS_SKIPPED skipped${RESET}" +fi +echo "" + +exit $TESTS_FAILED diff --git a/tests/e2e-teach-deploy-v2.zsh b/tests/e2e-teach-deploy-v2.zsh new file mode 100755 index 000000000..0e8d0f2f6 --- /dev/null +++ b/tests/e2e-teach-deploy-v2.zsh @@ -0,0 +1,500 @@ +#!/usr/bin/env zsh +# e2e-teach-deploy-v2.zsh - End-to-end tests for teach deploy v2 +# Tests full deploy lifecycle using sandboxed git repos + +# Test framework setup +PASS=0 +FAIL=0 +SKIP=0 + +_test_pass() { ((PASS++)); echo " ✅ $1"; } +_test_fail() { ((FAIL++)); echo " ❌ $1: $2"; } +_test_skip() { ((SKIP++)); echo " ⏭️ $1 (skipped)"; } + +# ============================================================================ +# SETUP +# ============================================================================ + +SCRIPT_DIR="${0:A:h}" +PROJECT_ROOT="${SCRIPT_DIR:h}" +TEST_DIR=$(mktemp -d) +ORIGINAL_DIR=$(pwd) + +cleanup() { + cd "$ORIGINAL_DIR" + rm -rf "$TEST_DIR" +} +trap cleanup EXIT + +# Minimal FLOW_COLORS for non-interactive tests +typeset -gA FLOW_COLORS +FLOW_COLORS[info]="" +FLOW_COLORS[success]="" +FLOW_COLORS[error]="" +FLOW_COLORS[warn]="" +FLOW_COLORS[dim]="" +FLOW_COLORS[bold]="" +FLOW_COLORS[reset]="" +FLOW_COLORS[prompt]="" +FLOW_COLORS[muted]="" + +# Source all needed libraries +source "$PROJECT_ROOT/lib/core.zsh" 2>/dev/null || true +source "$PROJECT_ROOT/lib/git-helpers.zsh" 2>/dev/null || true +source "$PROJECT_ROOT/lib/deploy-history-helpers.zsh" 2>/dev/null || true +source "$PROJECT_ROOT/lib/deploy-rollback-helpers.zsh" 2>/dev/null || true +source "$PROJECT_ROOT/lib/dispatchers/teach-deploy-enhanced.zsh" 2>/dev/null || true + +# Stub missing functions +if ! typeset -f _teach_error >/dev/null 2>&1; then + _teach_error() { echo "ERROR: $1" >&2; } +fi +if ! typeset -f _git_in_repo >/dev/null 2>&1; then + _git_in_repo() { git rev-parse --git-dir >/dev/null 2>&1; } +fi +if ! typeset -f _git_current_branch >/dev/null 2>&1; then + _git_current_branch() { git branch --show-current 2>/dev/null; } +fi +if ! typeset -f _git_is_clean >/dev/null 2>&1; then + _git_is_clean() { [[ -z "$(git status --porcelain 2>/dev/null)" ]]; } +fi +if ! typeset -f _git_detect_production_conflicts >/dev/null 2>&1; then + _git_detect_production_conflicts() { return 0; } +fi +if ! typeset -f _git_has_unpushed_commits >/dev/null 2>&1; then + _git_has_unpushed_commits() { return 1; } +fi +if ! typeset -f _git_push_current_branch >/dev/null 2>&1; then + _git_push_current_branch() { git push origin "$(git branch --show-current)" 2>/dev/null; } +fi +if ! typeset -f _git_is_synced >/dev/null 2>&1; then + _git_is_synced() { return 0; } +fi + +# Helper: create full E2E repo with bare remote, draft + main branches +setup_e2e_repo() { + local bare_dir=$(mktemp -d "$TEST_DIR/bare-XXXXXX") + local work_dir=$(mktemp -d "$TEST_DIR/e2e-XXXXXX") + rm -rf "$work_dir" # clone needs empty target + + # Create bare remote + ( + cd "$bare_dir" + git init -q --bare + ) >/dev/null 2>&1 + + # Clone working dir + git clone -q "$bare_dir" "$work_dir" >/dev/null 2>&1 + ( + cd "$work_dir" + git config user.email "test@test.com" + git config user.name "Test" + + # Setup course structure + mkdir -p .flow lectures labs assignments + cat > .flow/teach-config.yml <<'YAML' +course: + name: "STAT-101" +git: + draft_branch: draft + production_branch: main + auto_pr: true + require_clean: true +semester_info: + start_date: "2026-01-12" +YAML + + echo "status: active" > .STATUS + + cat > lectures/week-01.qmd <<'QMD' +--- +title: "Week 1: Introduction" +--- +Introduction to statistics. +QMD + + git add -A && git commit -q -m "init: course structure" + git push -q origin main + + # Create draft branch with additional content + git checkout -q -b draft + echo "# Week 2 lecture" > lectures/week-02.qmd + git add -A && git commit -q -m "add week-02 lecture" + git push -q -u origin draft + ) >/dev/null 2>&1 + + echo "$work_dir" +} + +echo "" +echo "╔════════════════════════════════════════════════════════════╗" +echo "║ teach deploy v2 - E2E Tests ║" +echo "╚════════════════════════════════════════════════════════════╝" +echo "" + +# ============================================================================ +# SECTION 1: Direct Merge Lifecycle +# ============================================================================ +echo "--- Direct Merge Lifecycle ---" + +# Test 1: Full direct deploy lifecycle +test_repo=$(setup_e2e_repo) +cd "$test_repo" +# Should be on draft branch with remote +branch=$(git branch --show-current) +if [[ "$branch" == "draft" ]]; then + # Call without $() capture so DEPLOY_* variables propagate + _deploy_direct_merge "draft" "main" "deploy: week-02 lecture" "true" >/dev/null 2>&1 + ret=$? + if [[ $ret -eq 0 ]]; then + _test_pass "full direct deploy lifecycle succeeds" + else + _test_fail "full direct deploy lifecycle succeeds" "ret=$ret" + fi +else + _test_fail "full direct deploy lifecycle" "not on draft (on $branch)" +fi + +# Test 2: Direct merge exports DEPLOY_* variables +if [[ -n "$DEPLOY_COMMIT_AFTER" && -n "$DEPLOY_COMMIT_BEFORE" ]]; then + _test_pass "direct merge exports DEPLOY_COMMIT_* variables" +else + _test_fail "direct merge exports DEPLOY_COMMIT_* variables" "after=$DEPLOY_COMMIT_AFTER before=$DEPLOY_COMMIT_BEFORE" +fi + +# Test 3: After direct merge, we're back on draft +branch=$(git branch --show-current) +if [[ "$branch" == "draft" ]]; then + _test_pass "after direct merge, back on draft branch" +else + _test_fail "after direct merge, back on draft branch" "on $branch" +fi + +# ============================================================================ +# SECTION 2: Deploy History Lifecycle +# ============================================================================ +echo "" +echo "--- Deploy History Lifecycle ---" + +# Test 4: history recording after deploy +test_repo=$(setup_e2e_repo) +cd "$test_repo" +_deploy_direct_merge "draft" "main" "deploy: test content" "true" >/dev/null 2>&1 +# Manually record history (as _teach_deploy_enhanced would) +_deploy_history_append "direct" "${DEPLOY_COMMIT_AFTER}" "${DEPLOY_COMMIT_BEFORE}" "draft" "main" "1" "deploy: test content" "null" "null" "${DEPLOY_DURATION}" >/dev/null 2>&1 +count=$(_deploy_history_count) +if [[ "$count" == "1" ]]; then + _test_pass "deploy records entry in history" +else + _test_fail "deploy records entry in history" "count=$count" +fi + +# Test 5: multiple deploys build sequential history +echo "# Week 3" > lectures/week-03.qmd +git add -A && git commit -q -m "add week-03" +git push -q origin draft 2>/dev/null +_deploy_direct_merge "draft" "main" "deploy: week-03" "true" >/dev/null 2>&1 +_deploy_history_append "direct" "${DEPLOY_COMMIT_AFTER}" "${DEPLOY_COMMIT_BEFORE}" "draft" "main" "1" "deploy: week-03" "null" "null" "${DEPLOY_DURATION}" >/dev/null 2>&1 +count=$(_deploy_history_count) +if [[ "$count" == "2" ]]; then + _test_pass "multiple deploys build sequential history" +else + _test_fail "multiple deploys build sequential history" "count=$count" +fi + +# Test 6: history list shows entries +output=$(_deploy_history_list 5 2>&1) +if echo "$output" | grep -q "deploy"; then + _test_pass "history list shows deploy entries" +else + _test_fail "history list shows deploy entries" "not found" +fi + +# ============================================================================ +# SECTION 3: Smart Commit Messages +# ============================================================================ +echo "" +echo "--- Smart Commit Messages ---" + +# Test 7: smart commit message generation +if typeset -f _generate_smart_commit_message >/dev/null 2>&1; then + test_repo=$(setup_e2e_repo) + cd "$test_repo" + msg=$(_generate_smart_commit_message "draft" "main") + if [[ -n "$msg" && "$msg" != "deploy: update" ]]; then + _test_pass "smart commit generates meaningful message: $msg" + else + # Fallback message is also acceptable + _test_pass "smart commit generates message (fallback): $msg" + fi +else + _test_skip "smart commit message generation (_generate_smart_commit_message not available)" +fi + +# ============================================================================ +# SECTION 4: Dry-run E2E +# ============================================================================ +echo "" +echo "--- Dry-run E2E ---" + +# Test 8: dry-run shows correct output +test_repo=$(setup_e2e_repo) +cd "$test_repo" +output=$(_deploy_dry_run_report "draft" "main" "STAT-101" "false" "" 2>&1) +ret=$? +if [[ $ret -eq 0 ]] && echo "$output" | grep -qi "DRY RUN"; then + _test_pass "dry-run shows correct output" +else + _test_fail "dry-run shows correct output" "ret=$ret" +fi + +# Test 9: dry-run shows files that would be deployed +if echo "$output" | grep -q "week-02"; then + _test_pass "dry-run shows files (week-02.qmd)" +else + _test_fail "dry-run shows files" "week-02 not found in output" +fi + +# ============================================================================ +# SECTION 5: .STATUS E2E +# ============================================================================ +echo "" +echo "--- .STATUS E2E ---" + +# Test 10: .STATUS gets updated after deploy +if command -v yq >/dev/null 2>&1; then + test_repo=$(setup_e2e_repo) + cd "$test_repo" + _deploy_history_append "direct" "abc12345" "def67890" "draft" "main" "1" "test" "null" "null" "5" >/dev/null 2>&1 + _deploy_update_status_file >/dev/null 2>&1 + ld=$(yq '.last_deploy // ""' .STATUS 2>/dev/null) + dc=$(yq '.deploy_count // 0' .STATUS 2>/dev/null) + today=$(date '+%Y-%m-%d') + if [[ "$ld" == "$today" && "$dc" == "1" ]]; then + _test_pass ".STATUS updated with deploy info" + else + _test_fail ".STATUS updated with deploy info" "ld=$ld dc=$dc" + fi +else + _test_skip ".STATUS update (yq not available)" +fi + +# Test 11: .STATUS teaching_week calculation +if command -v yq >/dev/null 2>&1; then + test_repo=$(setup_e2e_repo) + cd "$test_repo" + _deploy_update_status_file >/dev/null 2>&1 + tw=$(yq '.teaching_week // 0' .STATUS 2>/dev/null) + # Since start_date is 2026-01-12, on 2026-02-03 we should be week 4 + if [[ "$tw" -ge 1 && "$tw" -le 20 ]]; then + _test_pass ".STATUS teaching_week calculated (week $tw)" + else + # Week calculation may fail if date math is different; skip if 0 + if [[ "$tw" == "0" || -z "$tw" ]]; then + _test_skip ".STATUS teaching_week (date math issue)" + else + _test_fail ".STATUS teaching_week" "got: $tw" + fi + fi +else + _test_skip ".STATUS teaching_week (yq not available)" +fi + +# ============================================================================ +# SECTION 6: Help Output +# ============================================================================ +echo "" +echo "--- Help Output ---" + +# Test 12: help shows all options including rollback/history +output=$(_teach_deploy_enhanced_help 2>&1) +if echo "$output" | grep -q "\-\-rollback" && echo "$output" | grep -q "\-\-history"; then + _test_pass "help shows --rollback and --history options" +else + _test_fail "help shows --rollback and --history" "missing from output" +fi + +# Test 13: help shows rollback examples +if echo "$output" | grep -q "rollback 1"; then + _test_pass "help shows rollback examples" +else + _test_fail "help shows rollback examples" "not found" +fi + +# Test 14: help shows history examples +if echo "$output" | grep -q "history 20"; then + _test_pass "help shows history examples" +else + _test_fail "help shows history examples" "not found" +fi + +# Test 15: help shows .STATUS feature +if echo "$output" | grep -qi "STATUS"; then + _test_pass "help mentions .STATUS auto-update" +else + _test_fail "help mentions .STATUS auto-update" "not found" +fi + +# ============================================================================ +# SECTION 7: CI Mode E2E +# ============================================================================ +echo "" +echo "--- CI Mode E2E ---" + +# Test 16: CI mode direct merge +test_repo=$(setup_e2e_repo) +cd "$test_repo" +output=$(_deploy_direct_merge "draft" "main" "ci: auto deploy" "true" 2>&1) +ret=$? +if [[ $ret -eq 0 ]]; then + _test_pass "CI mode direct merge succeeds" +else + _test_fail "CI mode direct merge succeeds" "ret=$ret" +fi + +# Test 17: CI preflight on draft branch +cd "$test_repo" +output=$(_deploy_preflight_checks "true" 2>&1) +ret=$? +if [[ $ret -eq 0 ]]; then + _test_pass "CI preflight passes on draft branch" +else + _test_fail "CI preflight passes on draft branch" "ret=$ret" +fi + +# ============================================================================ +# SECTION 8: Auto-tag +# ============================================================================ +echo "" +echo "--- Auto-tag ---" + +# Test 18: auto-tag creates git tag +test_repo=$(setup_e2e_repo) +cd "$test_repo" +_deploy_direct_merge "draft" "main" "deploy: with tag" "true" >/dev/null 2>&1 +# Now simulate auto-tag (as the enhanced function would do) +tag="deploy-test-$(date +%Y%m%d%H%M%S)" +git tag "$tag" 2>/dev/null +if git tag | grep -q "$tag"; then + _test_pass "auto-tag creates git tag" +else + _test_fail "auto-tag creates git tag" "tag not found" +fi + +# ============================================================================ +# SECTION 9: Rollback E2E +# ============================================================================ +echo "" +echo "--- Rollback E2E ---" + +# Test 19: rollback with explicit index on valid history +test_repo=$(setup_e2e_repo) +cd "$test_repo" +# Do a deploy first +_deploy_direct_merge "draft" "main" "deploy: to be rolled back" "true" >/dev/null 2>&1 +_deploy_history_append "direct" "${DEPLOY_COMMIT_AFTER}" "${DEPLOY_COMMIT_BEFORE}" "draft" "main" "1" "deploy: to be rolled back" "null" "null" "5" >/dev/null 2>&1 +# Capture count BEFORE rollback for Test 20 +count_before=$(_deploy_history_count) +# Now rollback +output=$(_deploy_rollback 1 --ci 2>&1) +ret=$? +if [[ $ret -eq 0 ]]; then + _test_pass "rollback with explicit index succeeds" +else + _test_fail "rollback with explicit index" "ret=$ret output=$(echo "$output" | tail -3)" +fi + +# Test 20: rollback records in history with mode=rollback +# If rollback succeeded, check that history count increased +if [[ $ret -eq 0 ]]; then + count_after=$(_deploy_history_count) + if [[ "$count_after" -gt "$count_before" ]]; then + _deploy_history_get 1 + if [[ "$DEPLOY_HIST_MODE" == "rollback" ]]; then + _test_pass "rollback records in history with mode=rollback" + else + _test_fail "rollback records in history" "mode=$DEPLOY_HIST_MODE" + fi + else + _test_fail "rollback records in history" "count unchanged" + fi +else + _test_skip "rollback history recording (rollback did not complete)" +fi + +# ============================================================================ +# SECTION 10: Merge Commit Rollback (regression test for -m 1 fix) +# ============================================================================ +echo "" +echo "--- Merge Commit Rollback ---" + +# Test 21: deploy with diverged branches creates a merge commit +test_repo=$(setup_e2e_repo) +cd "$test_repo" +# Create divergence: add a commit directly to main that draft doesn't have +( + cd "$test_repo" + git checkout -q main 2>/dev/null + echo "# Hotfix content" > lectures/hotfix.qmd + git add -A && git commit -q -m "hotfix: main-only change" 2>/dev/null + git push -q origin main 2>/dev/null + git checkout -q draft 2>/dev/null +) >/dev/null 2>&1 + +# Deploy — draft and main have diverged, so merge creates 2-parent commit +_deploy_direct_merge "draft" "main" "deploy: merge commit test" "true" >/dev/null 2>&1 +ret_deploy=$? + +if [[ $ret_deploy -ne 0 ]]; then + _test_fail "merge commit deploy" "deploy failed ret=$ret_deploy" + _test_skip "merge commit rollback (deploy failed)" + _test_skip "merge commit rollback history (deploy failed)" +else + merge_hash="${DEPLOY_COMMIT_AFTER}" + parent_count=$(git cat-file -p "$merge_hash" 2>/dev/null | grep -c "^parent ") + + if [[ $parent_count -ge 2 ]]; then + _test_pass "diverged deploy creates merge commit ($parent_count parents)" + else + _test_fail "diverged deploy creates merge commit" "expected >=2 parents, got $parent_count" + fi + + # Test 22: rollback of merge commit succeeds (exercises -m 1 code path) + _deploy_history_append "direct" "$merge_hash" "${DEPLOY_COMMIT_BEFORE}" "draft" "main" "2" "deploy: merge commit test" "null" "null" "3" >/dev/null 2>&1 + mc_count_before=$(_deploy_history_count) + + mc_output=$(_deploy_rollback 1 --ci 2>&1) + mc_ret=$? + + if [[ $mc_ret -eq 0 ]]; then + _test_pass "merge commit rollback succeeds via -m 1" + else + _test_fail "merge commit rollback" "ret=$mc_ret output=$(echo "$mc_output" | tail -3)" + fi + + # Test 23: merge commit rollback recorded in history with mode=rollback + if [[ $mc_ret -eq 0 ]]; then + mc_count_after=$(_deploy_history_count) + if [[ "$mc_count_after" -gt "$mc_count_before" ]]; then + _deploy_history_get 1 + if [[ "$DEPLOY_HIST_MODE" == "rollback" ]]; then + _test_pass "merge commit rollback records with mode=rollback" + else + _test_fail "merge commit rollback history" "mode=$DEPLOY_HIST_MODE" + fi + else + _test_fail "merge commit rollback history" "count unchanged" + fi + else + _test_skip "merge commit rollback history (rollback failed)" + fi +fi + +# ============================================================================ +# SUMMARY +# ============================================================================ +echo "" +echo "═══════════════════════════════════════════" +echo " Results: $PASS passed, $FAIL failed, $SKIP skipped" +echo "═══════════════════════════════════════════" +[[ $FAIL -eq 0 ]] diff --git a/tests/fixtures/demo-course/.teach/concepts.json b/tests/fixtures/demo-course/.teach/concepts.json index fddaa559a..15a8457c3 100644 --- a/tests/fixtures/demo-course/.teach/concepts.json +++ b/tests/fixtures/demo-course/.teach/concepts.json @@ -2,8 +2,8 @@ "version": "1.0", "schema_version": "concept-graph-v1", "metadata": { - "last_updated": "2026-01-31T23:23:47Z", - "course_hash": "361ea9220e2ff2b6771468e18cddb45b5465b3a2", + "last_updated": "2026-02-04T05:03:50Z", + "course_hash": "e36a2bbd0443fc3840f3126e9917bc30afe2f373", "total_concepts": 12, "weeks": 5, "extraction_method": "frontmatter" @@ -32,7 +32,10 @@ "distributions": { "id": "distributions", "name": "Distributions", - "prerequisites": ["descriptive-stats", "data-types"], + "prerequisites": [ + "descriptive-stats", + "data-types" + ], "introduced_in": { "week": 1, "lecture": "lectures/week-01.qmd", @@ -42,7 +45,9 @@ "probability-basics": { "id": "probability-basics", "name": "Probability-Basics", - "prerequisites": ["data-types"], + "prerequisites": [ + "data-types" + ], "introduced_in": { "week": 2, "lecture": "lectures/week-02.qmd", @@ -52,7 +57,9 @@ "sampling": { "id": "sampling", "name": "Sampling", - "prerequisites": ["distributions"], + "prerequisites": [ + "distributions" + ], "introduced_in": { "week": 2, "lecture": "lectures/week-02.qmd", @@ -62,7 +69,11 @@ "inference": { "id": "inference", "name": "Inference", - "prerequisites": ["probability-basics", "sampling", "distributions"], + "prerequisites": [ + "probability-basics", + "sampling", + "distributions" + ], "introduced_in": { "week": 2, "lecture": "lectures/week-02.qmd", @@ -72,7 +83,10 @@ "linear-regression": { "id": "linear-regression", "name": "Linear-Regression", - "prerequisites": ["correlation", "inference"], + "prerequisites": [ + "correlation", + "inference" + ], "introduced_in": { "week": 3, "lecture": "lectures/week-03-broken.qmd", @@ -82,7 +96,9 @@ "correlation": { "id": "correlation", "name": "Correlation", - "prerequisites": ["linear-regression"], + "prerequisites": [ + "linear-regression" + ], "introduced_in": { "week": 3, "lecture": "lectures/week-03-broken.qmd", @@ -92,7 +108,10 @@ "multiple-regression": { "id": "multiple-regression", "name": "Multiple-Regression", - "prerequisites": ["linear-regression", "correlation"], + "prerequisites": [ + "linear-regression", + "correlation" + ], "introduced_in": { "week": 4, "lecture": "lectures/week-04.qmd", @@ -102,7 +121,10 @@ "model-selection": { "id": "model-selection", "name": "Model-Selection", - "prerequisites": ["multiple-regression", "inference"], + "prerequisites": [ + "multiple-regression", + "inference" + ], "introduced_in": { "week": 4, "lecture": "lectures/week-04.qmd", @@ -112,7 +134,10 @@ "assumptions-checking": { "id": "assumptions-checking", "name": "Assumptions-Checking", - "prerequisites": ["multiple-regression", "distributions"], + "prerequisites": [ + "multiple-regression", + "distributions" + ], "introduced_in": { "week": 4, "lecture": "lectures/week-04.qmd", @@ -122,7 +147,10 @@ "advanced-modeling": { "id": "advanced-modeling", "name": "Advanced-Modeling", - "prerequisites": ["nonexistent-concept", "multiple-regression"], + "prerequisites": [ + "nonexistent-concept", + "multiple-regression" + ], "introduced_in": { "week": 5, "lecture": "lectures/week-05-missing-prereq.qmd", diff --git a/tests/run-all.sh b/tests/run-all.sh index 6fdac9802..69e97537c 100755 --- a/tests/run-all.sh +++ b/tests/run-all.sh @@ -88,6 +88,9 @@ echo "Teach command tests:" run_test ./tests/test-teach-plan.zsh run_test ./tests/test-teach-plan-security.zsh run_test ./tests/automated-teach-style-dogfood.zsh +run_test ./tests/dogfood-teach-deploy-v2.zsh +run_test ./tests/test-teach-deploy-v2-unit.zsh +run_test ./tests/test-teach-deploy-v2-integration.zsh echo "" echo "Help compliance tests:" @@ -99,6 +102,7 @@ echo "E2E tests:" run_test ./tests/e2e-teach-plan.zsh run_test ./tests/e2e-teach-analyze.zsh run_test ./tests/e2e-dot-safety.zsh +run_test ./tests/e2e-teach-deploy-v2.zsh echo "" echo "=========================================" diff --git a/tests/test-teach-deploy-v2-integration.zsh b/tests/test-teach-deploy-v2-integration.zsh new file mode 100755 index 000000000..cbc35243d --- /dev/null +++ b/tests/test-teach-deploy-v2-integration.zsh @@ -0,0 +1,410 @@ +#!/usr/bin/env zsh +# test-teach-deploy-v2-integration.zsh - Integration tests for teach deploy v2 +# Tests flag combinations and multi-step workflows + +# Test framework setup +PASS=0 +FAIL=0 +SKIP=0 + +_test_pass() { ((PASS++)); echo " ✅ $1"; } +_test_fail() { ((FAIL++)); echo " ❌ $1: $2"; } +_test_skip() { ((SKIP++)); echo " ⏭️ $1 (skipped)"; } + +# ============================================================================ +# SETUP +# ============================================================================ + +SCRIPT_DIR="${0:A:h}" +PROJECT_ROOT="${SCRIPT_DIR:h}" +TEST_DIR=$(mktemp -d) +ORIGINAL_DIR=$(pwd) + +cleanup() { + cd "$ORIGINAL_DIR" + rm -rf "$TEST_DIR" +} +trap cleanup EXIT + +# Minimal FLOW_COLORS for non-interactive tests +typeset -gA FLOW_COLORS +FLOW_COLORS[info]="" +FLOW_COLORS[success]="" +FLOW_COLORS[error]="" +FLOW_COLORS[warn]="" +FLOW_COLORS[dim]="" +FLOW_COLORS[bold]="" +FLOW_COLORS[reset]="" +FLOW_COLORS[prompt]="" +FLOW_COLORS[muted]="" + +# Source libraries (suppress output) +source "$PROJECT_ROOT/lib/core.zsh" 2>/dev/null || true +source "$PROJECT_ROOT/lib/git-helpers.zsh" 2>/dev/null || true +source "$PROJECT_ROOT/lib/deploy-history-helpers.zsh" 2>/dev/null || true +source "$PROJECT_ROOT/lib/deploy-rollback-helpers.zsh" 2>/dev/null || true +source "$PROJECT_ROOT/lib/dispatchers/teach-deploy-enhanced.zsh" 2>/dev/null || true + +# Stub/override functions for test isolation +# These MUST be set AFTER sourcing libs to override real implementations +_teach_error() { echo "ERROR: $1" >&2; } +_git_in_repo() { git rev-parse --git-dir >/dev/null 2>&1; } +_git_current_branch() { git branch --show-current 2>/dev/null; } +_git_is_clean() { [[ -z "$(git status --porcelain 2>/dev/null)" ]]; } +# Override conflict detection for test repos (no remote tracking) +_git_detect_production_conflicts() { return 0; } +_git_has_unpushed_commits() { return 1; } + +# Helper: create a sandboxed git repo with draft/main branches and a remote +setup_full_repo() { + local bare_dir=$(mktemp -d "$TEST_DIR/bare-XXXXXX") + local work_dir="$TEST_DIR/work-$(basename $bare_dir)" + rm -rf "$work_dir" # ensure clean + + # Create bare remote + git init -q --bare "$bare_dir" 2>/dev/null + + # Clone working dir + git clone -q "$bare_dir" "$work_dir" 2>/dev/null + cd "$work_dir" + git config user.email "test@test.com" + git config user.name "Test" + + # Setup config + mkdir -p .flow + cat > .flow/teach-config.yml <<'YAML' +course: + name: "STAT-101" +git: + draft_branch: draft + production_branch: main + auto_pr: true + require_clean: true +YAML + git add -A >/dev/null 2>&1 && git commit -q -m "init" >/dev/null 2>&1 + git push -q origin main 2>/dev/null + + # Create draft branch + git checkout -q -b draft 2>/dev/null + git push -q -u origin draft 2>/dev/null + + echo "$work_dir" +} + +# Helper: create simple git repo (no remote) +setup_simple_repo() { + local dir=$(mktemp -d "$TEST_DIR/simple-XXXXXX") + mkdir -p "$dir/.flow" + ( + cd "$dir" + git init -q 2>/dev/null + git config user.email "test@test.com" + git config user.name "Test" + cat > .flow/teach-config.yml <<'YAML' +course: + name: "STAT-101" +git: + draft_branch: draft + production_branch: main + auto_pr: true + require_clean: true +YAML + git add -A >/dev/null 2>&1 + git commit -q -m "init" >/dev/null 2>&1 + ) + echo "$dir" +} + +echo "" +echo "╔════════════════════════════════════════════════════════════╗" +echo "║ teach deploy v2 - Integration Tests ║" +echo "╚════════════════════════════════════════════════════════════╝" +echo "" + +# ============================================================================ +# SECTION 1: Flag Combinations +# ============================================================================ +echo "--- Flag Combinations ---" + +# Test 1: --history exits early (no preflight) +test_repo=$(setup_simple_repo) +cd "$test_repo" +_deploy_history_append "direct" "aaa11111" "bbb22222" "draft" "main" "2" "test" "null" "null" "5" >/dev/null 2>&1 +output=$(_teach_deploy_enhanced --history 2>&1) +ret=$? +if [[ $ret -eq 0 ]] && ! echo "$output" | grep -q "Pre-flight"; then + _test_pass "--history exits early without preflight" +else + _test_fail "--history exits early without preflight" "ret=$ret" +fi + +# Test 2: --history with custom count +_deploy_history_append "pr" "bbb22222" "aaa11111" "draft" "main" "3" "second" "null" "null" "8" >/dev/null 2>&1 +output=$(_teach_deploy_enhanced --history 1 2>&1) +if [[ $ret -eq 0 ]]; then + _test_pass "--history with custom count" +else + _test_fail "--history with custom count" "ret=$ret" +fi + +# Test 3: --rollback exits early (no preflight) +output=$(_teach_deploy_enhanced --rollback 1 --ci 2>&1) +# Rollback will fail on simple repo (no remote), but should NOT run preflight +if ! echo "$output" | grep -q "Pre-flight Checks"; then + _test_pass "--rollback exits early without preflight" +else + _test_fail "--rollback exits early without preflight" "preflight ran" +fi + +# Test 4: --ci + --rollback passes ci flag through +output=$(_teach_deploy_enhanced --ci --rollback --ci 2>&1) +# CI mode without index should produce "CI mode requires explicit" error +if echo "$output" | grep -qi "CI mode requires explicit\|index"; then + _test_pass "--ci + --rollback passes ci flag (requires index)" +else + # May also succeed if history is empty + if echo "$output" | grep -qi "No deployment history"; then + _test_pass "--ci + --rollback passes ci flag (no history)" + else + _test_fail "--ci + --rollback passes ci flag" "unexpected output" + fi +fi + +# Test 5: unknown flags return error +output=$(_teach_deploy_enhanced --unknown-flag 2>&1) +ret=$? +if [[ $ret -ne 0 ]]; then + _test_pass "unknown flags return error" +else + _test_fail "unknown flags return error" "expected non-zero exit" +fi + +# Test 6: --help shows help (no error) +output=$(_teach_deploy_enhanced --help 2>&1) +ret=$? +if [[ $ret -eq 0 ]] && echo "$output" | grep -q "teach deploy"; then + _test_pass "--help shows help text" +else + _test_fail "--help shows help text" "ret=$ret" +fi + +# Test 7: --help includes rollback option +if echo "$output" | grep -q "\-\-rollback"; then + _test_pass "--help includes --rollback option" +else + _test_fail "--help includes --rollback option" "not found" +fi + +# Test 8: --help includes history option +if echo "$output" | grep -q "\-\-history"; then + _test_pass "--help includes --history option" +else + _test_fail "--help includes --history option" "not found" +fi + +# Test 9: --dry-run includes history log hint +test_repo=$(setup_simple_repo) +cd "$test_repo" +git checkout -q -b draft 2>/dev/null +echo "new" > test.qmd +git add -A >/dev/null 2>&1 && git commit -q -m "content" >/dev/null 2>&1 +output=$(_teach_deploy_enhanced --dry-run 2>&1) +if echo "$output" | grep -q "deploy-history"; then + _test_pass "--dry-run shows history log hint" +else + _test_fail "--dry-run shows history log hint" "not found" +fi + +# ============================================================================ +# SECTION 2: Preflight with CI mode +# ============================================================================ +echo "" +echo "--- Preflight CI Mode ---" + +# Test 10: CI mode fails on wrong branch +test_repo=$(setup_simple_repo) +cd "$test_repo" +# Stay on main (not draft) +output=$(_deploy_preflight_checks "true" 2>&1) +ret=$? +if [[ $ret -ne 0 ]]; then + _test_pass "CI mode preflight fails when not on draft branch" +else + _test_fail "CI mode preflight fails when not on draft branch" "expected failure" +fi + +# Test 11: CI mode succeeds on draft branch +# Note: requires a repo where draft and main don't conflict +# Override conflict detection to isolate preflight logic +_git_detect_production_conflicts() { return 0; } +git checkout -q -b draft 2>/dev/null +output=$(_deploy_preflight_checks "true" 2>&1) +ret=$? +if [[ $ret -eq 0 ]]; then + _test_pass "CI mode preflight succeeds on draft branch" +else + _test_fail "CI mode preflight succeeds on draft branch" "ret=$ret" +fi + +# ============================================================================ +# SECTION 3: Deploy History Integration +# ============================================================================ +echo "" +echo "--- Deploy History Integration ---" + +# Test 12: multiple deploys build sequential history +test_repo=$(setup_simple_repo) +cd "$test_repo" +for i in {1..5}; do + _deploy_history_append "direct" "hash${i}aaa" "prev${i}aaa" "draft" "main" "$i" "deploy $i" "null" "null" "$i" >/dev/null 2>&1 +done +count=$(_deploy_history_count) +if [[ "$count" == "5" ]]; then + _test_pass "multiple deploys build sequential history" +else + _test_fail "multiple deploys build sequential history" "count=$count" +fi + +# Test 13: history_get returns entries in reverse order +_deploy_history_get 1 +msg1="$DEPLOY_HIST_MESSAGE" +_deploy_history_get 5 +msg5="$DEPLOY_HIST_MESSAGE" +if [[ "$msg1" == "deploy 5" && "$msg5" == "deploy 1" ]]; then + _test_pass "history entries in reverse order (newest=1)" +else + _test_fail "history entries in reverse order" "1=$msg1, 5=$msg5" +fi + +# Test 14: history list with limited count +output=$(_deploy_history_list 2 2>&1) +# Should show 2 entries, not 5 +lines_with_deploy=$(echo "$output" | grep -c "deploy [0-9]" || echo 0) +if [[ "$lines_with_deploy" -le 3 ]]; then + _test_pass "history list respects count limit" +else + _test_fail "history list respects count limit" "showed $lines_with_deploy entries" +fi + +# ============================================================================ +# SECTION 4: .STATUS Integration +# ============================================================================ +echo "" +echo "--- .STATUS Integration ---" + +# Test 15: .STATUS gets updated after deploy (if yq available) +if command -v yq >/dev/null 2>&1; then + test_repo=$(setup_simple_repo) + cd "$test_repo" + echo "status: active" > .STATUS + _deploy_history_append "direct" "aaa11111" "bbb22222" "draft" "main" "1" "test" "null" "null" "1" >/dev/null 2>&1 + _deploy_update_status_file >/dev/null 2>&1 + ld=$(yq '.last_deploy // ""' .STATUS 2>/dev/null) + dc=$(yq '.deploy_count // 0' .STATUS 2>/dev/null) + today=$(date '+%Y-%m-%d') + if [[ "$ld" == "$today" && "$dc" == "1" ]]; then + _test_pass ".STATUS updated with last_deploy and deploy_count" + else + _test_fail ".STATUS updated with last_deploy and deploy_count" "ld=$ld dc=$dc" + fi +else + _test_skip ".STATUS update test (yq not available)" +fi + +# Test 16: .STATUS not created if absent +test_repo=$(setup_simple_repo) +cd "$test_repo" +rm -f .STATUS +_deploy_update_status_file >/dev/null 2>&1 +if [[ ! -f .STATUS ]]; then + _test_pass ".STATUS not created when absent" +else + _test_fail ".STATUS not created when absent" "file was created" +fi + +# ============================================================================ +# SECTION 5: Mixed mode history entries +# ============================================================================ +echo "" +echo "--- Mixed Mode History ---" + +# Test 17: direct + pr + rollback modes in same history +test_repo=$(setup_simple_repo) +cd "$test_repo" +_deploy_history_append "direct" "aaa11111" "bbb22222" "draft" "main" "2" "direct deploy" "null" "null" "5" >/dev/null 2>&1 +_deploy_history_append "pr" "ccc33333" "aaa11111" "draft" "main" "3" "pr deploy" "42" "null" "15" >/dev/null 2>&1 +_deploy_history_append "rollback" "ddd44444" "ccc33333" "draft" "main" "3" "revert: rollback" "null" "null" "8" >/dev/null 2>&1 +count=$(_deploy_history_count) +if [[ "$count" == "3" ]]; then + _test_pass "mixed mode history (direct + pr + rollback)" +else + _test_fail "mixed mode history" "count=$count" +fi + +# Test 18: verify each mode stored correctly +_deploy_history_get 1 +mode3="$DEPLOY_HIST_MODE" +_deploy_history_get 2 +mode2="$DEPLOY_HIST_MODE" +_deploy_history_get 3 +mode1="$DEPLOY_HIST_MODE" +if [[ "$mode1" == "direct" && "$mode2" == "pr" && "$mode3" == "rollback" ]]; then + _test_pass "each deploy mode stored correctly" +else + _test_fail "each deploy mode stored correctly" "1=$mode1 2=$mode2 3=$mode3" +fi + +# ============================================================================ +# SECTION 6: Flag Parser Edge Cases +# ============================================================================ +echo "" +echo "--- Flag Parser Edge Cases ---" + +# Test 19: --direct-push backward compat (alias for --direct) +test_repo=$(setup_simple_repo) +cd "$test_repo" +git checkout -q -b draft 2>/dev/null +echo "new file" > content.qmd +git add -A >/dev/null 2>&1 && git commit -q -m "add content" >/dev/null 2>&1 +# The enhanced function should accept --direct-push without error +output=$(_teach_deploy_enhanced --direct-push --dry-run 2>&1) +ret=$? +if [[ $ret -eq 0 ]]; then + _test_pass "--direct-push backward compat works" +else + _test_fail "--direct-push backward compat" "ret=$ret" +fi + +# Test 20: -d short flag works +output=$(_teach_deploy_enhanced -d --dry-run 2>&1) +ret=$? +if [[ $ret -eq 0 ]]; then + _test_pass "-d short flag works" +else + _test_fail "-d short flag works" "ret=$ret" +fi + +# Test 21: --direct + --dry-run shows direct mode +output=$(_teach_deploy_enhanced --direct --dry-run 2>&1) +if echo "$output" | grep -qi "direct"; then + _test_pass "--direct + --dry-run shows direct mode preview" +else + _test_fail "--direct + --dry-run shows direct mode preview" "not found" +fi + +# Test 22: -m flag passes custom message to dry-run +output=$(_teach_deploy_enhanced --dry-run -m "Custom Week 5" 2>&1) +if echo "$output" | grep -q "Custom Week 5"; then + _test_pass "-m flag passes custom message to preview" +else + _test_fail "-m flag passes custom message to preview" "not found" +fi + +# ============================================================================ +# SUMMARY +# ============================================================================ +echo "" +echo "═══════════════════════════════════════════" +echo " Results: $PASS passed, $FAIL failed, $SKIP skipped" +echo "═══════════════════════════════════════════" +[[ $FAIL -eq 0 ]] diff --git a/tests/test-teach-deploy-v2-unit.zsh b/tests/test-teach-deploy-v2-unit.zsh new file mode 100755 index 000000000..55392b935 --- /dev/null +++ b/tests/test-teach-deploy-v2-unit.zsh @@ -0,0 +1,580 @@ +#!/usr/bin/env zsh +# test-teach-deploy-v2-unit.zsh - Unit tests for teach deploy v2 features +# Tests each function in isolation within a sandboxed git repo + +# Test framework setup +PASS=0 +FAIL=0 +SKIP=0 + +_test_pass() { ((PASS++)); echo " ✅ $1"; } +_test_fail() { ((FAIL++)); echo " ❌ $1: $2"; } +_test_skip() { ((SKIP++)); echo " ⏭️ $1 (skipped)"; } + +# ============================================================================ +# SETUP +# ============================================================================ + +SCRIPT_DIR="${0:A:h}" +PROJECT_ROOT="${SCRIPT_DIR:h}" +TEST_DIR=$(mktemp -d) +ORIGINAL_DIR=$(pwd) + +cleanup() { + cd "$ORIGINAL_DIR" + rm -rf "$TEST_DIR" +} +trap cleanup EXIT + +# Minimal FLOW_COLORS for non-interactive tests +typeset -gA FLOW_COLORS +FLOW_COLORS[info]="" +FLOW_COLORS[success]="" +FLOW_COLORS[error]="" +FLOW_COLORS[warn]="" +FLOW_COLORS[dim]="" +FLOW_COLORS[bold]="" +FLOW_COLORS[reset]="" +FLOW_COLORS[prompt]="" +FLOW_COLORS[muted]="" + +# Source core + helpers (suppress output) +source "$PROJECT_ROOT/lib/core.zsh" 2>/dev/null || true +source "$PROJECT_ROOT/lib/git-helpers.zsh" 2>/dev/null || true +source "$PROJECT_ROOT/lib/deploy-history-helpers.zsh" 2>/dev/null || true +source "$PROJECT_ROOT/lib/deploy-rollback-helpers.zsh" 2>/dev/null || true +source "$PROJECT_ROOT/lib/dispatchers/teach-deploy-enhanced.zsh" 2>/dev/null || true + +# Stub functions that may not be available +if ! typeset -f _teach_error >/dev/null 2>&1; then + _teach_error() { echo "ERROR: $1" >&2; } +fi +if ! typeset -f _git_in_repo >/dev/null 2>&1; then + _git_in_repo() { git rev-parse --git-dir >/dev/null 2>&1; } +fi +if ! typeset -f _git_current_branch >/dev/null 2>&1; then + _git_current_branch() { git branch --show-current 2>/dev/null; } +fi +if ! typeset -f _git_is_clean >/dev/null 2>&1; then + _git_is_clean() { [[ -z "$(git status --porcelain 2>/dev/null)" ]]; } +fi + +# Helper: create a sandboxed git repo with .flow/teach-config.yml +# IMPORTANT: All git output is redirected to /dev/null to keep echo clean +# Uses mktemp for unique directories (avoids subshell counter issues) +setup_git_repo() { + local dir=$(mktemp -d "$TEST_DIR/repo-XXXXXX") + mkdir -p "$dir/.flow" + ( + cd "$dir" + git init -q 2>/dev/null + git config user.email "test@test.com" + git config user.name "Test" + cat > .flow/teach-config.yml <<'YAML' +course: + name: "STAT-101" +git: + draft_branch: draft + production_branch: main + auto_pr: true + require_clean: true +YAML + git add -A >/dev/null 2>&1 + git commit -q -m "init" >/dev/null 2>&1 + ) + echo "$dir" +} + +echo "" +echo "╔════════════════════════════════════════════════════════════╗" +echo "║ teach deploy v2 - Unit Tests ║" +echo "╚════════════════════════════════════════════════════════════╝" +echo "" + +# ============================================================================ +# SECTION 1: Deploy History Helpers +# ============================================================================ +echo "--- Deploy History ---" + +# Test 1: history_append creates file if not exists +test_repo=$(setup_git_repo) +cd "$test_repo" +rm -f .flow/deploy-history.yml +_deploy_history_append "direct" "abc12345" "def67890" "draft" "main" "5" "test deploy" "null" "null" "10" >/dev/null 2>&1 +if [[ -f .flow/deploy-history.yml ]]; then + _test_pass "history_append creates file if not exists" +else + _test_fail "history_append creates file if not exists" "file not created" +fi + +# Test 2: history_append adds entry to existing file +_deploy_history_append "pr" "xyz12345" "abc12345" "draft" "main" "3" "second deploy" "null" "null" "5" >/dev/null 2>&1 +count=$(_deploy_history_count) +if [[ "$count" == "2" ]]; then + _test_pass "history_append adds entry to existing file" +else + _test_fail "history_append adds entry to existing file" "expected 2, got $count" +fi + +# Test 3: history_count returns correct number +test_repo=$(setup_git_repo) +cd "$test_repo" +count=$(_deploy_history_count) +if [[ "$count" == "0" ]]; then + _test_pass "history_count returns 0 for no history" +else + _test_fail "history_count returns 0 for no history" "got $count" +fi + +# Test 4: history_count after appends +_deploy_history_append "direct" "aaa11111" "bbb22222" "draft" "main" "1" "first" "null" "null" "1" >/dev/null 2>&1 +_deploy_history_append "direct" "ccc33333" "aaa11111" "draft" "main" "2" "second" "null" "null" "2" >/dev/null 2>&1 +_deploy_history_append "pr" "ddd44444" "ccc33333" "draft" "main" "3" "third" "null" "null" "3" >/dev/null 2>&1 +count=$(_deploy_history_count) +if [[ "$count" == "3" ]]; then + _test_pass "history_count returns correct count after 3 appends" +else + _test_fail "history_count returns correct count after 3 appends" "expected 3, got $count" +fi + +# Test 5: history_get retrieves correct entry (1 = most recent) +_deploy_history_get 1 +if [[ "$DEPLOY_HIST_MESSAGE" == "third" ]]; then + _test_pass "history_get retrieves most recent entry (idx=1)" +else + _test_fail "history_get retrieves most recent entry (idx=1)" "got: $DEPLOY_HIST_MESSAGE" +fi + +# Test 6: history_get retrieves correct entry (3 = oldest) +_deploy_history_get 3 +if [[ "$DEPLOY_HIST_MESSAGE" == "first" ]]; then + _test_pass "history_get retrieves oldest entry (idx=3)" +else + _test_fail "history_get retrieves oldest entry (idx=3)" "got: $DEPLOY_HIST_MESSAGE" +fi + +# Test 7: history_get fails for out-of-range index +if _deploy_history_get 99 2>/dev/null; then + _test_fail "history_get rejects out-of-range index" "should have returned non-zero" +else + _test_pass "history_get rejects out-of-range index" +fi + +# Test 8: history_list shows formatted table +test_repo=$(setup_git_repo) +cd "$test_repo" +_deploy_history_append "direct" "aaa11111" "bbb22222" "draft" "main" "2" "test message" "null" "null" "5" >/dev/null 2>&1 +output=$(_deploy_history_list 5 2>&1) +if echo "$output" | grep -q "test message"; then + _test_pass "history_list shows deploy messages" +else + _test_fail "history_list shows deploy messages" "message not found in output" +fi + +# Test 9: history_list returns error when no history file +test_repo=$(setup_git_repo) +cd "$test_repo" +rm -f .flow/deploy-history.yml +output=$(_deploy_history_list 5 2>&1) +ret=$? +if [[ $ret -ne 0 ]]; then + _test_pass "history_list returns error when no history" +else + _test_fail "history_list returns error when no history" "expected non-zero exit" +fi + +# Test 10: Single quotes in messages are escaped +test_repo=$(setup_git_repo) +cd "$test_repo" +_deploy_history_append "direct" "aaa11111" "bbb22222" "draft" "main" "1" "week 5 update" "null" "null" "1" >/dev/null 2>&1 +# Verify yq can read the file after append +count=$(_deploy_history_count) +if [[ "$count" == "1" ]]; then + _test_pass "history file is valid YAML after append" +else + _test_fail "history file is valid YAML after append" "yq failed to parse, count=$count" +fi + +# Test 11: history_append stores correct mode +_deploy_history_get 1 +if [[ "$DEPLOY_HIST_MODE" == "direct" ]]; then + _test_pass "history_append stores correct mode field" +else + _test_fail "history_append stores correct mode field" "got: $DEPLOY_HIST_MODE" +fi + +# Test 12: history_append stores commit hash (truncated to 8) +_deploy_history_get 1 +if [[ "$DEPLOY_HIST_COMMIT" == "aaa11111" ]]; then + _test_pass "history_append stores 8-char commit hash" +else + _test_fail "history_append stores 8-char commit hash" "got: $DEPLOY_HIST_COMMIT" +fi + +# ============================================================================ +# SECTION 2: Smart Commit Messages +# ============================================================================ +echo "" +echo "--- Smart Commit Messages ---" + +# Test 13: categorizes lecture files correctly +if typeset -f _generate_smart_commit_message >/dev/null 2>&1; then + test_repo=$(setup_git_repo) + cd "$test_repo" + git checkout -q -b draft 2>/dev/null + mkdir -p lectures + echo "---\ntitle: Week 5\n---\nHello" > lectures/week-05.qmd + git add -A >/dev/null 2>&1 && git commit -q -m "add lecture" >/dev/null 2>&1 + msg=$(_generate_smart_commit_message "draft" "main") + if echo "$msg" | grep -qi "lecture\|content\|week-05"; then + _test_pass "smart commit categorizes lecture files" + else + _test_fail "smart commit categorizes lecture files" "got: $msg" + fi +else + _test_skip "smart commit categorizes lecture files (_generate_smart_commit_message not loaded)" +fi + +# Test 14: categorizes assignment files correctly +if typeset -f _generate_smart_commit_message >/dev/null 2>&1; then + test_repo=$(setup_git_repo) + cd "$test_repo" + git checkout -q -b draft 2>/dev/null + mkdir -p assignments + echo "---\ntitle: HW3\n---" > assignments/hw-03.qmd + git add -A >/dev/null 2>&1 && git commit -q -m "add assignment" >/dev/null 2>&1 + msg=$(_generate_smart_commit_message "draft" "main") + if echo "$msg" | grep -qi "assignment\|content\|hw"; then + _test_pass "smart commit categorizes assignment files" + else + _test_fail "smart commit categorizes assignment files" "got: $msg" + fi +else + _test_skip "smart commit categorizes assignment files" +fi + +# Test 15: categorizes config files correctly +if typeset -f _generate_smart_commit_message >/dev/null 2>&1; then + test_repo=$(setup_git_repo) + cd "$test_repo" + git checkout -q -b draft 2>/dev/null + echo "title: Test" > _quarto.yml + git add -A >/dev/null 2>&1 && git commit -q -m "add config" >/dev/null 2>&1 + msg=$(_generate_smart_commit_message "draft" "main") + if echo "$msg" | grep -qi "config\|quarto\|_quarto"; then + _test_pass "smart commit categorizes config files" + else + _test_fail "smart commit categorizes config files" "got: $msg" + fi +else + _test_skip "smart commit categorizes config files" +fi + +# Test 16: empty diff produces fallback message +if typeset -f _generate_smart_commit_message >/dev/null 2>&1; then + test_repo=$(setup_git_repo) + cd "$test_repo" + msg=$(_generate_smart_commit_message "main" "main") + if [[ -n "$msg" ]]; then + _test_pass "empty diff produces fallback message" + else + _test_fail "empty diff produces fallback message" "got empty string" + fi +else + _test_skip "empty diff produces fallback message" +fi + +# Test 17: message truncates at 72 chars +if typeset -f _generate_smart_commit_message >/dev/null 2>&1; then + test_repo=$(setup_git_repo) + cd "$test_repo" + git checkout -q -b draft 2>/dev/null + mkdir -p lectures labs assignments exams projects + for i in {01..20}; do + echo "content $i" > "lectures/week-${i}.qmd" + done + git add -A >/dev/null 2>&1 && git commit -q -m "add many files" >/dev/null 2>&1 + msg=$(_generate_smart_commit_message "draft" "main") + if [[ ${#msg} -le 72 ]]; then + _test_pass "message truncates at 72 chars (len=${#msg})" + else + _test_fail "message truncates at 72 chars" "length=${#msg}" + fi +else + _test_skip "message truncates at 72 chars" +fi + +# ============================================================================ +# SECTION 3: Preflight Checks +# ============================================================================ +echo "" +echo "--- Preflight Checks ---" + +# Test 18: preflight returns error outside git repo +test_nongit=$(mktemp -d "$TEST_DIR/nongit-XXXXXX") + +cd "$test_nongit" +output=$(_deploy_preflight_checks "true" 2>&1) +ret=$? +if [[ $ret -ne 0 ]]; then + _test_pass "preflight returns error outside git repo" +else + _test_fail "preflight returns error outside git repo" "expected non-zero exit" +fi + +# Test 19: preflight returns error without config file +test_repo_noconfig=$(mktemp -d "$TEST_DIR/noconfig-XXXXXX") + +cd "$test_repo_noconfig" +git init -q 2>/dev/null +git config user.email "test@test.com" +git config user.name "Test" +echo "test" > file.txt +git add -A >/dev/null 2>&1 && git commit -q -m "init" >/dev/null 2>&1 +output=$(_deploy_preflight_checks "true" 2>&1) +ret=$? +if [[ $ret -ne 0 ]]; then + _test_pass "preflight returns error without config file" +else + _test_fail "preflight returns error without config file" "expected non-zero exit" +fi + +# Test 20: preflight sets DEPLOY_* variables correctly +test_repo=$(setup_git_repo) +cd "$test_repo" +_deploy_preflight_checks "true" >/dev/null 2>&1 +if [[ "$DEPLOY_DRAFT_BRANCH" == "draft" && "$DEPLOY_PROD_BRANCH" == "main" ]]; then + _test_pass "preflight sets DEPLOY_* variables correctly" +else + _test_fail "preflight sets DEPLOY_* variables correctly" "draft=$DEPLOY_DRAFT_BRANCH prod=$DEPLOY_PROD_BRANCH" +fi + +# Test 21: preflight sets course name +if [[ "$DEPLOY_COURSE_NAME" == "STAT-101" ]]; then + _test_pass "preflight sets DEPLOY_COURSE_NAME" +else + _test_fail "preflight sets DEPLOY_COURSE_NAME" "got: $DEPLOY_COURSE_NAME" +fi + +# ============================================================================ +# SECTION 4: Dry-run Report +# ============================================================================ +echo "" +echo "--- Dry-run Report ---" + +# Test 22: dry-run shows preview without mutations +test_repo=$(setup_git_repo) +cd "$test_repo" +git checkout -q -b draft 2>/dev/null +echo "new content" > lectures.qmd +git add -A >/dev/null 2>&1 && git commit -q -m "add content" >/dev/null 2>&1 +output=$(_deploy_dry_run_report "draft" "main" "STAT-101" "false" "" 2>&1) +if echo "$output" | grep -qi "DRY RUN"; then + _test_pass "dry-run shows DRY RUN header" +else + _test_fail "dry-run shows DRY RUN header" "not found in output" +fi + +# Test 23: dry-run with direct mode +output=$(_deploy_dry_run_report "draft" "main" "STAT-101" "true" "" 2>&1) +if echo "$output" | grep -qi "direct"; then + _test_pass "dry-run shows direct mode" +else + _test_fail "dry-run shows direct mode" "not found in output" +fi + +# Test 24: dry-run with custom message +output=$(_deploy_dry_run_report "draft" "main" "STAT-101" "false" "custom message" 2>&1) +if echo "$output" | grep -q "custom message"; then + _test_pass "dry-run shows custom message" +else + _test_fail "dry-run shows custom message" "not found in output" +fi + +# Test 25: dry-run shows history log hint +output=$(_deploy_dry_run_report "draft" "main" "STAT-101" "false" "" 2>&1) +if echo "$output" | grep -q "deploy-history"; then + _test_pass "dry-run shows history log hint" +else + _test_fail "dry-run shows history log hint" "not found in output" +fi + +# ============================================================================ +# SECTION 5: .STATUS Update Function +# ============================================================================ +echo "" +echo "--- .STATUS Update ---" + +# Test 26: skips if no .STATUS file +test_repo=$(setup_git_repo) +cd "$test_repo" +rm -f .STATUS +output=$(_deploy_update_status_file 2>&1) +ret=$? +if [[ $ret -eq 0 ]]; then + _test_pass "status update skips if no .STATUS file" +else + _test_fail "status update skips if no .STATUS file" "returned $ret" +fi + +# Test 27: updates last_deploy date (if yq available) +if command -v yq >/dev/null 2>&1; then + test_repo=$(setup_git_repo) + cd "$test_repo" + echo "status: active" > .STATUS + _deploy_update_status_file >/dev/null 2>&1 + last_deploy=$(yq '.last_deploy // ""' .STATUS 2>/dev/null) + today=$(date '+%Y-%m-%d') + if [[ "$last_deploy" == "$today" ]]; then + _test_pass "status update sets last_deploy to today" + else + _test_fail "status update sets last_deploy to today" "got: $last_deploy" + fi +else + _test_skip "status update sets last_deploy (yq not available)" +fi + +# Test 28: updates deploy_count +if command -v yq >/dev/null 2>&1; then + test_repo=$(setup_git_repo) + cd "$test_repo" + echo "status: active" > .STATUS + _deploy_history_append "direct" "aaa11111" "bbb22222" "draft" "main" "1" "test" "null" "null" "1" >/dev/null 2>&1 + _deploy_update_status_file >/dev/null 2>&1 + dc=$(yq '.deploy_count // 0' .STATUS 2>/dev/null) + if [[ "$dc" == "1" ]]; then + _test_pass "status update sets deploy_count" + else + _test_fail "status update sets deploy_count" "got: $dc" + fi +else + _test_skip "status update sets deploy_count (yq not available)" +fi + +# ============================================================================ +# SECTION 6: Rollback (unit-level) +# ============================================================================ +echo "" +echo "--- Rollback ---" + +# Test 29: CI mode requires index +test_repo=$(setup_git_repo) +cd "$test_repo" +_deploy_history_append "direct" "aaa11111" "bbb22222" "draft" "main" "1" "test" "null" "null" "1" >/dev/null 2>&1 +output=$(_deploy_rollback --ci 2>&1) +ret=$? +if [[ $ret -ne 0 ]]; then + _test_pass "rollback CI mode requires explicit index" +else + _test_fail "rollback CI mode requires explicit index" "expected failure" +fi + +# Test 30: rollback validates index against history count +output=$(_deploy_rollback 99 --ci 2>&1) +ret=$? +if [[ $ret -ne 0 ]]; then + _test_pass "rollback rejects out-of-range index" +else + _test_fail "rollback rejects out-of-range index" "expected failure" +fi + +# Test 31: rollback returns error on empty history +test_repo=$(setup_git_repo) +cd "$test_repo" +rm -f .flow/deploy-history.yml +output=$(_deploy_rollback 1 --ci 2>&1) +ret=$? +if [[ $ret -ne 0 ]]; then + _test_pass "rollback returns error on empty history" +else + _test_fail "rollback returns error on empty history" "expected failure" +fi + +# ============================================================================ +# SECTION 7: History Append Edge Cases +# ============================================================================ +echo "" +echo "--- History Edge Cases ---" + +# Test 32: history_append with all null optional fields +test_repo=$(setup_git_repo) +cd "$test_repo" +_deploy_history_append "direct" "abc12345" "def67890" "draft" "main" "0" "empty deploy" "null" "null" "0" >/dev/null 2>&1 +count=$(_deploy_history_count) +if [[ "$count" == "1" ]]; then + _test_pass "history_append handles null optional fields" +else + _test_fail "history_append handles null optional fields" "count=$count" +fi + +# Test 33: history_append with pr_number +_deploy_history_append "pr" "ghi12345" "abc12345" "draft" "main" "5" "pr deploy" "42" "null" "20" >/dev/null 2>&1 +_deploy_history_get 1 +if [[ "$DEPLOY_HIST_PR" == "42" ]]; then + _test_pass "history_append stores pr_number correctly" +else + _test_fail "history_append stores pr_number correctly" "got: $DEPLOY_HIST_PR" +fi + +# Test 34: history file is valid YAML after multiple appends +test_repo=$(setup_git_repo) +cd "$test_repo" +for i in {1..5}; do + _deploy_history_append "direct" "hash${i}000" "prev${i}000" "draft" "main" "$i" "deploy $i" "null" "null" "$i" >/dev/null 2>&1 +done +count=$(_deploy_history_count) +if [[ "$count" == "5" ]]; then + _test_pass "history file valid YAML after 5 appends" +else + _test_fail "history file valid YAML after 5 appends" "count=$count" +fi + +# ============================================================================ +# SECTION 8: Merge Commit Detection (regression for -m 1 rollback fix) +# ============================================================================ +echo "" +echo "--- Merge Commit Detection ---" + +# Test 35: merge commit has 2 parents (validates detection logic) +test_repo=$(setup_git_repo) +cd "$test_repo" +# Create diverging branches +( + cd "$test_repo" + git checkout -q -b feature 2>/dev/null + echo "feature work" > feature.txt + git add -A && git commit -q -m "feat: feature work" 2>/dev/null + git checkout -q main 2>/dev/null + echo "main work" > main.txt + git add -A && git commit -q -m "fix: main work" 2>/dev/null + git merge feature --no-ff -m "merge: feature into main" 2>/dev/null +) >/dev/null 2>&1 +merge_hash=$(git rev-parse HEAD) +parent_count=$(git cat-file -p "$merge_hash" 2>/dev/null | grep -c "^parent ") +if [[ $parent_count -eq 2 ]]; then + _test_pass "merge commit detected with 2 parents" +else + _test_fail "merge commit detected with 2 parents" "got $parent_count" +fi + +# Test 36: regular commit has 1 parent +test_repo=$(setup_git_repo) +cd "$test_repo" +echo "extra" > extra.txt +git add -A && git commit -q -m "add extra" >/dev/null 2>&1 +regular_hash=$(git rev-parse HEAD) +parent_count=$(git cat-file -p "$regular_hash" 2>/dev/null | grep -c "^parent ") +if [[ $parent_count -eq 1 ]]; then + _test_pass "regular commit detected with 1 parent" +else + _test_fail "regular commit detected with 1 parent" "got $parent_count" +fi + +# ============================================================================ +# SUMMARY +# ============================================================================ +echo "" +echo "═══════════════════════════════════════════" +echo " Results: $PASS passed, $FAIL failed, $SKIP skipped" +echo "═══════════════════════════════════════════" +[[ $FAIL -eq 0 ]]