AGENTS.md · git:20260725.9c1b406 · 2026-07-25 · sha256 8b2c3431d2e99f00
AGENTS.md git:20260725.9c1b406A
Immutable. This exact content is served forever at /api/v1/blob/8b2c3431d2e99f00.
# Agent Instructions
This project uses **bd** (beads) for issue tracking. Run `bd onboard` to get started.
In this repo, beads is **local-only**: use it for structured issue tracking on your machine, but do not expect a shared Dolt remote.
## Quick Reference
```bash
bd ready # Find available work
bd show <id> # View issue details
bd update <id> --claim # Claim work atomically
bd close <id> # Complete work
```
## GitHub Discussions
This repo has GitHub Discussions enabled (`silverstein/minutes`). Issues are for bugs and feature requests. Discussions are for usage questions, setup help, and community show-and-tell.
**Agent guidelines:**
- When triaging an issue that's really a "how do I...?" question, suggest converting it to a Discussion rather than closing it
- When a user's bug report turns out to be a config/setup issue, answer it and note that Discussions is the better venue for follow-ups
- After shipping a feature or fix, check if any open Q&A discussions are resolved by the change — post a reply pointing to the release
- When writing user-facing error messages or help text, link to Discussions (not Issues) for support: `https://github.com/silverstein/minutes/discussions`
- Don't file Discussions as work items — they're community conversations, not tracked tasks
## Portable Agent Skills (`.agents/skills/minutes/`, `.opencode/skills/`)
This repo maintains skill outputs in **three locations**:
- `.claude/plugins/minutes/` — Claude Code plugin (uses `${CLAUDE_PLUGIN_ROOT}`)
- `.agents/skills/minutes/` — Agent-agnostic mirror for Codex, Gemini, Pi, and other agents (uses `$MINUTES_SKILLS_ROOT`)
- `.opencode/skills/` — OpenCode-native mirror (one-level discovery path + matching `.opencode/commands/`)
**What lives where:**
- `SKILL.md` files are mirrored 1:1. Content is identical except for path variables and platform-specific references (e.g., "open in desktop app" in the plugin version becomes a CLI command in the agents version).
- `_runtime/hooks/lib/` contains `minutes-learn.mjs` and `minutes-learn-cli.mjs` — the behavioral learning system. These must stay byte-identical across `.agents/skills/minutes/_runtime/hooks/lib/` and `.opencode/skills/_runtime/hooks/lib/`.
- Bundled scripts (for example `scripts/tag_apply.py`) are mirrored into both portable trees.
- `.opencode/commands/*.md` provides native `/minutes-*` slash commands for OpenCode and is generated from the same canonical skill sources.
**When you modify a skill or runtime hook:**
```bash
# Preferred workflow: edit the canonical source under tooling/skills/sources/<name>/skill.md
# then regenerate every host surface from one place.
cd tooling/skills
npm run build
npm run compile
# Verify generated outputs are current:
npm run compile:dry
npm run check
```
**Why multiple trees?** Claude Code plugins use `${CLAUDE_PLUGIN_ROOT}` and plugin metadata. Codex/Gemini/Pi consume the `.agents/skills/minutes/` mirror. OpenCode only auto-discovers `skills/*/SKILL.md` one directory deep and has its own `.opencode/commands/` surface, so it needs a flattened generated tree.
## Non-Interactive Shell Commands
**ALWAYS use non-interactive flags** with file operations to avoid hanging on confirmation prompts.
Shell commands like `cp`, `mv`, and `rm` may be aliased to include `-i` (interactive) mode on some systems, causing the agent to hang indefinitely waiting for y/n input.
**Use these forms instead:**
```bash
# Force overwrite without prompting
cp -f source dest # NOT: cp source dest
mv -f source dest # NOT: mv source dest
rm -f file # NOT: rm file
# For recursive operations
rm -rf directory # NOT: rm -r directory
cp -rf source dest # NOT: cp -r source dest
```
**Other commands that may prompt:**
- `scp` - use `-o BatchMode=yes` for non-interactive
- `ssh` - use `-o BatchMode=yes` to fail instead of prompting
- `apt-get` - use `-y` flag
- `brew` - use `HOMEBREW_NO_AUTO_UPDATE=1` env var
## macOS Desktop Identity Rule
For any local desktop work that touches macOS privacy / TCC-sensitive features
(Microphone, Screen Recording, Input Monitoring, Accessibility, call capture,
global hotkeys), do **not** dogfood by repeatedly replacing
`/Applications/Minutes.app` with ad-hoc local rebuilds.
Use the dedicated development app identity instead:
```bash
export MINUTES_DEV_SIGNING_IDENTITY="Developer ID Application: Your Name (TEAMID)"
./scripts/install-dev-app.sh
```
Canonical dogfood target:
- `~/Applications/Minutes Dev.app`
Why:
- macOS TCC permissions attach to the effective app identity and signature
- ad-hoc local rebuilds of `/Applications/Minutes.app` can trigger repeated or misleading permission prompts
- the signed dev app is the stable local identity for permission-sensitive testing
## Pre-Commit Discipline
The full pre-commit checklist lives in [`docs/checklists/pre-commit.md`](docs/checklists/pre-commit.md). It covers MCP manifest sync, MCPB bundle guards, cargo fmt/clippy/test, Unix-only-API gating, feature-stub parity, site release constants, skill compiler outputs, and the toolchain + UI items below. **Read it before any commit that touches Rust, MCP server, frontend, or release surfaces** — the table is the single source of truth and is kept up to date as failure modes get caught.
Two items added after PR #206 that bear repeating here because they bit hard:
1. **Rust toolchain pin (`rust-toolchain.toml`).** The repo pins rustc/clippy/rustfmt to a specific version. CI uses `dtolnay/rust-toolchain@stable` for the system default, but rustup's cargo proxy reads the pin file when invoked from inside the repo and routes through the pinned toolchain. **The pin is honored locally only when cargo runs through the rustup proxy** — verify with `command -v cargo` matching `rustup which cargo` (typically `~/.cargo/bin/cargo`, but `CARGO_HOME` overrides relocate it). If `which cargo` resolves to `/opt/homebrew/bin/cargo` or another non-rustup path, the pin is silently ignored and your local clippy/rustfmt drift from CI's. Two prior commits (`4954de2`, `21cd699`) are clippy-fix-only commits that landed because of exactly this drift. Fix once: prepend rustup's bin dir to your shell PATH (`export PATH="$(dirname "$(rustup which cargo)"):$PATH"` in your shell rc, or just `~/.cargo/bin` if you haven't overridden `CARGO_HOME`), or `brew uninstall rust`. The build scripts (`scripts/build.sh`, `scripts/install-dev-app.sh`) detect it via `rustup which cargo` themselves so script-driven builds are immune; only interactive `cargo` invocations need the shell PATH fix.
2. **UI render verification.** Any change to `tauri/src/index.html`, any new Tauri `cmd_*` exposed to the frontend, or any modal/overlay/panel layout shift requires building the dev app via `./scripts/install-dev-app.sh` and click-testing the affected surface in `~/Applications/Minutes Dev.app` before commit. Type checks and Rust unit tests do not catch UI render bugs. PR #206 surfaced four such bugs (path candidate dedup, build-artifact bundles polluting the picker, default selection, ad-hoc detection logic) only via click-testing — none would have failed any test or CI job.
## Independent-cadence crate: `whisper-guard`
`crates/whisper-guard/` is published to crates.io on its own cadence — separate from the main Minutes release.
It is NOT in the main Release Checklist's "all 6 versions must match" list.
**When you change anything under `crates/whisper-guard/src/`:**
1. Bump `crates/whisper-guard/Cargo.toml` `version` (semver).
2. Publish independently:
```bash
cd crates/whisper-guard
cargo publish --dry-run && cargo publish
```
3. Do NOT bump the main Minutes version just because whisper-guard changed.
**Before cutting a Minutes release, verify whisper-guard is in sync:**
```bash
PUBLISHED=$(curl -s https://crates.io/api/v1/crates/whisper-guard | jq -r '.crate.max_stable_version')
LAST_PUBLISH_COMMIT=$(git log --grep="whisper-guard $PUBLISHED" --format="%H" | head -1)
git log "$LAST_PUBLISH_COMMIT"..HEAD -- crates/whisper-guard/
```
If that diff is non-empty, publish whisper-guard first. The full procedure lives in [`docs/release/procedure.md`](docs/release/procedure.md) Step 11.5.
**Why this matters:** whisper-guard has external consumers (~277 downloads at last check). Repo state drifting ahead of crates.io means downstream users silently miss anti-hallucination fixes shipped here.
<!-- BEGIN BEADS INTEGRATION profile:full hash:d4f96305 -->
## Issue Tracking with bd (beads)
**IMPORTANT**: This project uses **bd (beads)** for ALL issue tracking. Do NOT use markdown TODOs, task lists, or other tracking methods.
### Why bd?
- Dependency-aware: Track blockers and relationships between issues
- Git-friendly: Dolt-backed local history for issue state
- Agent-optimized: JSON output, ready work detection, discovered-from links
- Prevents duplicate tracking systems and confusion
### Quick Start
**Check for ready work:**
```bash
bd ready --json
```
**Create new issues:**
```bash
bd create "Issue title" --description="Detailed context" -t bug|feature|task -p 0-4 --json
bd create "Issue title" --description="What this issue is about" -p 1 --deps discovered-from:bd-123 --json
```
**Claim and update:**
```bash
bd update <id> --claim --json
bd update bd-42 --priority 1 --json
```
**Complete work:**
```bash
bd close bd-42 --reason "Completed" --json
```
### Issue Types
- `bug` - Something broken
- `feature` - New functionality
- `task` - Work item (tests, docs, refactoring)
- `epic` - Large feature with subtasks
- `chore` - Maintenance (dependencies, tooling)
### Priorities
- `0` - Critical (security, data loss, broken builds)
- `1` - High (major features, important bugs)
- `2` - Medium (default, nice-to-have)
- `3` - Low (polish, optimization)
- `4` - Backlog (future ideas)
### Workflow for AI Agents
1. **Check ready work**: `bd ready` shows unblocked issues
2. **Claim your task atomically**: `bd update <id> --claim`
3. **Work on it**: Implement, test, document
4. **Discover new work?** Create linked issue:
- `bd create "Found bug" --description="Details about what was found" -p 1 --deps discovered-from:<parent-id>`
5. **Complete**: `bd close <id> --reason "Done"`
### Local-Only Storage
bd writes issue state into the local beads/Dolt store for this repo.
- Each write auto-commits to local Dolt history
- This repo does **not** use a configured Dolt remote
- Do not require `bd dolt push`/`bd dolt pull` in landing workflows unless the repo is explicitly reconfigured later
### Important Rules
- ✅ Use bd for ALL task tracking
- ✅ Always use `--json` flag for programmatic use
- ✅ Link discovered work with `discovered-from` dependencies
- ✅ Check `bd ready` before asking "what should I work on?"
- ❌ Do NOT create markdown TODO lists
- ❌ Do NOT use external issue trackers
- ❌ Do NOT duplicate tracking systems
For more details, see README.md and docs/QUICKSTART.md.
## Landing the Plane (Session Completion)
**When ending a work session**, you MUST complete ALL steps below. Work is NOT complete until `git push` succeeds.
**MANDATORY WORKFLOW:**
1. **File issues for remaining work** - Create issues for anything that needs follow-up
2. **Run quality gates** (if code changed) - Tests, linters, builds
3. **Update issue status** - Close finished work, update in-progress items
4. **PUSH TO REMOTE** - This is MANDATORY:
```bash
git pull --rebase
git push
git status # MUST show "up to date with origin"
```
5. **Clean up** - Clear stashes, prune remote branches
6. **Verify** - All changes committed AND pushed
7. **Hand off** - Provide context for next session
**CRITICAL RULES:**
- Work is NOT complete until `git push` succeeds
- NEVER stop before pushing - that leaves work stranded locally
- NEVER say "ready to push when you are" - YOU must push
- If push fails, resolve and retry until it succeeds
<!-- END BEADS INTEGRATION -->
## Proven Decisions (do not re-litigate)
Three decisions with incident history behind them. Each has a primary control
in code/tests — these lines are pointers, not the enforcement.
- **A wrong rewrite or merge is worse than none.** Applies to speaker names,
entity resolution, and any automated identity/text rewriting: only
high-confidence results may rewrite user-visible data; uncertain results stay
anonymous/unmerged. Proven by the "Every Name, Right" epic
(docs/plans/every-name-right-2026-06-11.md, the #385 fix series) and enforced
by the proper-name regression evals.
- **Recording must never be degraded by an optional consumer.** Copilot
streams, live sidecars, coaching surfaces, and any other listener are
failure-isolated from capture: if they wedge or die, recording and WAV
preservation continue. Defined in RFC 0004 (copilot-realtime-stream,
failure-isolation boundary); enforced by the capture-reliability invariant
suites in minutes-core (consumer isolation, stop-deadline WAV preservation).
- **Auto-update stays off** until a hosted release manifest and rollback UX
exist. Decision record: docs/investigations/auto-update-evaluation.md. Do not
wire updaters into the desktop app before those preconditions are met.