Repository navigation
Conversation
…eased Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…495) Implements the protocol proposed in docs/planning/PROPOSAL-claude-chat-to-code-handoff.md. - lib/handoff-helpers.zsh: _flow_handoff() scaffolds docs/planning/HANDOFF-<slug>.md from a fixed schema (Summary, Key Decisions, Traps to Avoid, Working Agreements, Relevant Files, Open Work, Verification Note, Origin) - Relevant Files pre-filled from 'git diff --name-only' against base branch (default dev, falls back to main) - Refuses to overwrite an existing handoff for the same slug - --issue flag files a GitHub issue via gh CLI using the handoff as the body - Wired into commands/flow.zsh dispatcher and completions/_flow - docs/commands/handoff.md command reference + mkdocs.yml nav entry - CHANGELOG.md entry under Unreleased Tests: - tests/e2e-handoff.zsh: 7 tests against an isolated temp repo (help, fresh slug, overwrite refusal, real-diff pre-fill, empty-diff placeholder, --issue-without-gh failure, and a zsh -f regression test for a stray-stdout bug hit during prototyping) - all passing - tests/dogfood-handoff.zsh: real-repo smoke test, self-cleaning artifact - passing Fixes a zsh special-variable collision found during prototyping (local var named 'fpath' collides with zsh's function-autoload array) by using git diff --name-only instead of parsing --stat output. Co-authored-by: Test User <test@example.com>
…496) - mcprestart: one-command quit+relaunch of Claude Desktop to reset a stalled (4-min timeout) or dead ('tool not found') MCP bridge - mcprestart-log: shows recent restarts + this-month count, turning the '5+/month' flicker estimate into a measured number Context: docs-standards ADR-001 scoped a full McpDoctorUseCase in atlas for automated detection — parked pending queue priority. This is the zero-engineering interim fix for the manual relaunch step, not a replacement for the eventual automated audit. Co-authored-by: Test User <test@example.com>
PR #495 shipped the command but missed the usual doc surfaces (QUICK- REFERENCE.md, flow.1, MASTER-API-REFERENCE.md), and its own docs/commands/handoff.md linked to docs/specs/ which mkdocs excludes from the built site. - man/man1/flow.1: add handoff to Setup & Diagnostics + an example - docs/help/QUICK-REFERENCE.md: add Session Handoff section - docs/reference/MASTER-API-REFERENCE.md: add Handoff Helpers section - mkdocs.yml: exclude the planning proposal doc from nav (working doc, like the ADHD templates) - docs/commands/handoff.md: point the spec link at GitHub instead of the excluded specs/ path Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JnVugoVjTHRK41krTTMi8L
…dd em undo + recent-folders (Phase 3) (#497) em move <ID> <folder> was broken in production: _em_move and its adapter _em_hml_move were each defined twice in email-dispatcher.zsh/em-himalaya.zsh, and zsh's last-definition-wins semantics meant the two LIVE definitions were incompatible with each other — the live _em_move called _em_hml_move with the numeric message ID in the source-folder position. Every move would attempt to move a message from a folder named after its own ID. Same failure class as the flag/star case-duplicate bug fixed earlier this session (PR #493), this time a duplicate function definition rather than a duplicate case label. Caught by tests/test-em-move-restore.zsh, which was never wired into tests/run-all.sh (orphaned, like test-em-ai-switch.zsh was) — it now fails 3/8 -> passes 8/8 against the fixed source and is registered. Kept the multi-ID interface (em move <FOLDER> <ID>...) since it matches em move --help, MASTER-DISPATCHER-GUIDE.md, and the orphaned test file. Corrected the same stale <ID>-first interface across QUICK-REFERENCE.md, MASTER-DISPATCHER-GUIDE.md, REFCARD-EMAIL-DISPATCHER.md (which had both a stale and a correct em move row), EMAIL-DISPATCHER-GUIDE.md, EMAIL-COOKBOOK.md, and man/man1/em.1 — plus a leftover "em flag = alias for em star" line the Phase 1+2 docs sweep missed. Phase 3 (docs/specs/SPEC-em-ux-refactor-2026-07-07.md): - em undo: single-step undo of the last star/flag/unflag/move (1hr window, no stack), built on lib/em-cache.zsh's existing get/set API - em move --recent <ID>...: quick-pick target folder from last 3 destinations, no fzf required - Centralized ALIASES section in em help (single source of truth instead of scattered per-command mentions) Tests: tests/test-em-undo.zsh (16 new tests), tests/test-em-move-restore.zsh (8/8, newly registered), tests/test-em-help-guards.zsh (undo help coverage). Full suite: 80 passed, 0 failed, 1 skipped. mkdocs build --strict clean. Man-page guard: 12/12. Claude-Session: https://claude.ai/code/session_01JnVugoVjTHRK41krTTMi8L Co-authored-by: Test User <test@example.com> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JnVugoVjTHRK41krTTMi8L
Mirrors the settings.json change (fallback for env-block being silently ignored, issue #63186) — keeps the two in sync. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Points at the uv-managed venv (~/.venvs/savant-render) created to run savant's xlsx-render dogfood test with a real openpyxl instead of the degrade path, without touching the externally-managed system Python. Not added to PATH by default.
…lk to a free port Fixes the recurring OSError 48 (Address already in use) when port 8000 is held by another process: an explicit -a/--dev-addr still passes through untouched; otherwise a busy port already serving the savant site is opened in the browser instead of crashing, and any other conflict walks up 8000-8020 to the first free port. E2E: with 8000-8002 busy, auto-picked 8003 and served (curl-verified), then cleaned up. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…no-ff (#505) * fix(teach-deploy): keep --dry-run read-only and unblocked in CI mode ci_mode auto-detects whenever stdin is not a TTY, so every automation or agent caller gets it. In that mode two preflight conditions were fatal even under --dry-run: - "production has new commits" aborted before the plan could render - "uncommitted changes" aborted before the plan could render Neither can affect a dry run, which mutates nothing. Both teaching repos tested hit the first one, so a dry run could not render a plan on the normal state of a working course repo. The uncommitted-changes handler had a second, worse edge: interactively it PROMPTS to commit. A pty wrapper answers the default Y and silently creates a real commit. Observed once against a live course repo. Fix: skip both mutation-oriented gates when dry_run is true. Real deploys are untouched - they still abort on both conditions, which the two controls in the new suite assert. tests/test-teach-deploy-dryrun-readonly.zsh: 10 assertions over sandboxed fixtures, each checking repository state before and after so any write is caught. 10 passed, 0 failed. * docs(changelog): record the teach deploy --dry-run read-only fix Added to [Unreleased] in both the root and docs/ changelogs. Note the two files already differ by ~20KB of pre-existing drift; this entry is mirrored into both rather than reconciling that separately. * fix(teach-deploy): merge with --no-ff on the direct path A deploy of N draft commits used to fast-forward production to draft's own tip, leaving no single commit representing the deploy. Since teach deploy --rollback reverts the deploy's commit, a fast-forwarded deploy could only be rolled back one commit at a time - it silently lost rollback granularity rather than failing. The codebase already assumed merge commits exist: git-helpers.zsh:693 filters "merge commits from --no-ff deploys" when detecting production conflicts. Verified in a sandboxed fixture. Before: linear history, production tip was draft's own content commit. After: production tip is a merge commit with parents [base, draft-tip]. tests/test-teach-deploy-merge-topology.zsh asserts both properties. Positive control: reverting only the --no-ff fails both assertions, and restoring it passes both. One trap documented in the test header - after a successful deploy, draft and production legitimately point at the SAME commit, because the deploy syncs draft from production afterwards. Ref equality is therefore not evidence of a fast-forwarded deploy; parent count is. An earlier check of mine got this wrong. All 8 deploy suites: 246 passed, 1 failed, 1 skipped. The failure is Test 24 in test-teach-deploy-unit.zsh, which reproduces identically on unmodified dev. * fix(teach-deploy): dry run now names the files it is NOT showing Self-review of this PR: skipping the uncommitted-changes handler on a dry run has a side effect the first commit did not account for. The handler previously committed dirty work before the plan rendered, so the "Would deploy N files" list included it. Skipping the handler is the right call - a dry run should not assume you would commit - but it means the list is committed work only, and can silently understate what a real deploy would ship. The report now names the count of excluded uncommitted files and says a real deploy would offer to commit them first. The preflight already showed "[--] Working tree has uncommitted changes"; this connects that line to the file count below it. Covered by a new assertion in test-teach-deploy-dryrun-readonly.zsh (11 passed). All 8 deploy suites re-run: 247 passed, 1 failed, 1 skipped - the failure is the same pre-existing Test 24 that reproduces on dev. --------- Co-authored-by: Test User <test@example.com>
…gating) (#506) * ci: close four coverage gaps found in a gap analysis 1. TWO SUITES MERGED WITHOUT CI EVER RUNNING THEM (my regression, from #505). run-all.sh takes an explicit run_test list, not a glob, so test-teach-deploy-dryrun-readonly and test-teach-deploy-merge-topology went in green while CI never executed their 12 assertions. Both wired; both run in under 2s, well inside the 30s default. 2. 74 SUITES THAT HAD NEVER RUN ON A RUNNER. Audited all 118 test-*.zsh files unreferenced by run-all.sh: 74 pass, 37 fail, 6 exceed 20s, 1 is TTY-only. The 74 are now in tests/run-extended.sh behind a NON-BLOCKING soak job. Deliberately not promoted straight into the required check - "passes on a Mac" is not evidence about ubuntu-latest, and one environment-dependent suite would break the gate for everyone. This mirrors how full-suite itself was introduced, per test.yml's own comment. Verified locally: 74 passed, 0 failed. The 37 broken ones are left out and are NOT silently dropped - the count is recorded in run-extended.sh's header. They fail in the main checkout too, so this is rot, not worktree sensitivity. 3. CHANGELOG PARITY, GATED WHERE IT CAN ACTUALLY BE GATED. CHANGELOG.md and docs/CHANGELOG.md are meant to mirror and nothing checked it. They have drifted BIDIRECTIONALLY: 23 sections only in root, 41 only in docs, and of 33 shared versions only 12 have identical bodies. Neither file is a superset, so this cannot be fixed by copying one over the other - 21 shared versions need a human to pick the correct body. Reconciling that is explicitly NOT attempted here; picking a winner 21 times would destroy release history. Instead test-changelog-parity.zsh gates the [Unreleased] section, which is identical today, so new entries can no longer land in one file only. Historical drift is reported as advisory. Positive control: planting an entry in one file's [Unreleased] fails the test and names the offending line. 4. DOCS QUALITY WAS UNGATED. mkdocs.yml had no strict setting while docs.yml runs `mkdocs gh-deploy --force` on push, so broken links deployed silently. strict: true enabled, verified safe first (current tree builds with 0 warnings). scripts/lint-docs.sh existed but was wired into nothing - now runs in CI as ADVISORY, because 394 errors would fail every run and 138 of those are MD040, which is real work rather than a config toggle. Also fixes a real bug: .markdownlintignore is a markdownlint-cli **v1** file and this script runs cli2, which never reads it - so the repo's intent to skip .archive/ was inert and 49 errors came from archived docs. Excluded via a negated glob cli2 honours. 443 -> 394. NOT DONE, deliberately: no `lint-docs.sh --fix`. It resolves 302 of 443 but renumbers ordered lists (MD029) and rewrote `#| label:` to `# | label:` in docs/plans/2026-01-31-teach-validate-lint.md, breaking a Quarto chunk directive. ~20 of 48 touched files had genuine content edits including a security spec whose items 3-9 were renumbered to 1-7. Measured, reverted, and documented in the CI step so the next person does not repeat it. Full blocking suite locally: 2 failures (e2e-em-dispatcher, test-atlas-contract), both reproducing on unmodified dev and both green in CI - local environment, not this branch. * ci: narrow the soak list to the 60 that pass on the runner First CI run of the soak job did its job: 60 of the 74 passed on ubuntu-latest, 14 failed. They need a tool or service a fresh runner does not have - brew, atlas, the macOS keychain, a real mail account - which is exactly the 'passes on a Mac is not evidence about CI' case the job exists to surface. The 14 are excluded and named in the file with the reason, not silently dropped. A soak job that is permanently red gets ignored, and then it protects nothing; green means a later failure is real signal. The right fix for each is to exit 77 (this repo's clean-skip code) when its dependency is absent, at which point it can rejoin the list. Until then they sit where they already were - outside CI. Local: 60 passed, 0 failed. --------- Co-authored-by: Test User <test@example.com>
Minor bump: 2 feat, 3 fix, no breaking changes since v7.16.0.
Highlights - teach deploy safety:
- --dry-run is genuinely read-only. It no longer aborts in CI mode (which
every non-TTY caller gets), no longer prompts to commit (a pty wrapper
auto-accepted that and created a real commit in a live course repo), and
now names the uncommitted files its plan excludes.
- --direct merges with --no-ff, so a deploy is one revertable commit again
and --rollback can undo a multi-commit deploy as a unit.
Plus em undo / em move --recent, and the CI coverage work from #506.
Release mechanics:
- scripts/release.sh 7.17.0 updated package.json, CLAUDE.md,
flow.plugin.zsh, README badge and 20 man pages.
- CHANGELOG.md and docs/CHANGELOG.md both cut [7.17.0] with identical
bodies; tests/test-changelog-parity.zsh confirms [Unreleased] parity.
- docs/index.md updated by hand: release.sh does NOT touch it, so its
"What's New" banner and footer still advertised v7.16.0. Worth folding
into release.sh so the next release does not repeat it.
Verified: no 7.16.0 references remain outside the changelogs and release
archives; man-page version guard 12/12; mkdocs --strict exit 0; full suite
82 passed / 2 failed, both (e2e-em-dispatcher, test-atlas-contract)
reproducing on unmodified dev and green in CI.
Co-authored-by: Test User <test@example.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release v7.17.0 — 12 commits since the last
dev → main.Minor bump: 2
feat, 3fix, no breaking changes since v7.16.0.Headline:
teach deploysafety--dry-runis genuinely read-only. It aborted in CI mode (auto-detected whenever stdin isnot a TTY, so every automation caller got it) on conditions a dry run cannot be affected by, and
interactively it prompted to commit — which a pty wrapper auto-accepted, creating a real commit
in a live course repo. Both gates now skip on a dry run; real deploys still abort on both.
--directmerges with--no-ff. Without it a deploy of N commits fast-forwarded productionto draft's tip, leaving no single commit representing the deploy — so
--rollback, which revertsthe deploy's commit, could only undo the last of the N.
Also
em undo,em move --recent, anem helpALIASES section, and anem moveproduction fix.CI and docs coverage (#506)
Extended Suitesoakjob. 118 unreferenced
test-*.zshfiles were audited: 74 pass locally, 60 of those also pass onubuntu-latest. The 14 needing brew/atlas/keychain are excluded and named.[Unreleased], so an entry can no longer land in one file only.mkdocs strict: true— broken links now fail the build instead of deploying, sincedocs.ymlruns
gh-deploy --forceon push..markdownlintignoreis a markdownlint-cli v1 file whilelint-docs.shruns cli2, which never read it — the intent to skip
.archive/was inert (49 of 443 errors).Verification
All three checks green on #507. Full suite 82 passed / 2 failed locally; both failures
(
e2e-em-dispatcher,test-atlas-contract) reproduce on unmodifieddevand are green in CI.Man-page version guard 12/12.
mkdocs build --strictexit 0. No7.16.0references remain outsidethe changelogs and release archives.
Post-merge
Tag
v7.17.0, GitHub release, then the Homebrew tap formula bump.