AGENTS.md · diff
git:20260824.0549249 to git:20260828.e54d45d
136 added, 117 removed. Audit A to A.
- # Senpi Repository Guide
+ # Development Rules
- Generated: 2026-08-24
- Commit: `baf15a54d`
- Branch: `initdeep-refresh-20260824`
+ ## Conversational Style
- Senpi is an extension-first coding-agent monorepo. Keep changes scoped, preserve upstream mergeability, and read the nearest `AGENTS.md` plus every applicable `changes.md` before editing.
+ - Keep answers short and concise
+ - No emojis in commits, issues, PR comments, or code
+ - No fluff or cheerful filler text (e.g., "Thanks @user" not "Thanks so much @user!")
+ - Technical prose only, be direct
+ - Use concise, clear, simple language. Define unavoidable jargon before using it.
+ - Explain non-trivial designs and problems as: problem, concrete example or short trace, then solution. State why the solution is necessary and distinguish it from optional complexity.
+ - Prefer concrete behavior and small illustrations over abstract summaries, dense terminology, or unexplained lists of changes.
+ - When the user asks a question, answer it first before making edits or running implementation commands.
+ - When responding to user feedback or an analysis, explicitly say whether you agree or disagree before saying what you changed.
- ## MANDATORY EXECUTION PROTOCOLS — NON-NEGOTIABLE
+ ## Code Quality
- ### PROTOCOL 1 — EXPLICITLY REQUESTED MODIFICATIONS
+ - Read files in full before wide-ranging changes, before editing files you have not fully inspected, and when asked to investigate or audit. Do not rely on search snippets for broad changes.
+ - No `any` unless absolutely necessary.
+ - Inline single-line helpers that have only one call site.
+ - Check node_modules for external API types; don't guess.
+ - **No inline imports** (`await import()`, `import("pkg").Type`, dynamic type imports). Top-level imports only.
+ - Never remove or downgrade code to fix type errors from outdated deps; upgrade the dep instead.
+ - Use only erasable TypeScript syntax (Node strip-only mode) in code checked by the root config (`packages/*/src`, `packages/*/test`, `packages/coding-agent/examples`): no parameter properties, `enum`, `namespace`/`module`, `import =`, `export =`, or other constructs needing JS emit. Use explicit fields with constructor assignments.
+ - Always ask before removing functionality or code that appears intentional.
+ - Do not preserve backward compatibility unless the user asks for it.
+ - Never hardcode key checks (e.g. `matchesKey(keyData, "ctrl+x")`). Add defaults to `DEFAULT_EDITOR_KEYBINDINGS` or `DEFAULT_APP_KEYBINDINGS` so they stay configurable.
+ - Never modify `packages/ai/src/models.generated.ts` directly; update `packages/ai/scripts/generate-models.ts` instead, then regenerate. Including the resulting `models.generated.ts` diff is always OK, even if regeneration includes unrelated upstream model metadata changes.
- **WHEN A PATCH MUST BE MADE TO THIS REPOSITORY AND THE USER HAS EXPLICITLY INSTRUCTED THE MODIFICATION, THE FOLLOWING SEQUENCE IS ABSOLUTE AND BINDING. EXECUTE EVERY STEP, IN THIS EXACT ORDER. SKIPPING, REORDERING, OR ABBREVIATING ANY STEP IS A DEFECT:**
+ ## Commands
- 1. **EXPLORE** — READ EVERY FILE, SYMBOL, AND TEST THE CHANGE TOUCHES BEFORE WRITING A SINGLE LINE.
- 2. **MAKE A PLAN** — A DECISION-COMPLETE PLAN EXISTS BEFORE ANY CODE.
- 3. **ADD TODOS IN ULTRA-DETAIL** — MIRROR EVERY ATOMIC PLAN STEP INTO THE TODO LIST.
- 4. **MAKE A NEW WORKTREE** — NEVER IMPLEMENT IN THE SHARED WORKTREE.
- 5. **MAKE A PR AND WORK UNTIL IT GETS MERGED** — AN UNMERGED PR IS UNFINISHED WORK.
- 6. **SET A GOAL AND RUN THE ULW LOOP** — EVERY SUCCESS CRITERION PASSES WITH CAPTURED EVIDENCE.
- 7. **MANAGE TODOS OBSESSIVELY** — UPDATE ON EVERY STATE TRANSITION. A STALE TODO LIST IS A DEFECT.
+ - After code changes (not docs): `npm run check` (full output, no tail). Fix all errors, warnings, and infos before committing. Does not run tests.
+ - Never run `npm run build` or `npm test` unless requested by the user.
+ - Never run the full vitest suite directly: it includes e2e tests that activate when endpoint/auth env vars are present. For all non-e2e tests, run `./test.sh` from the repo root. Otherwise run specific tests from the package root:
+ - Vitest: `node "$(git rev-parse --show-toplevel)/node_modules/vitest/dist/cli.js" --run test/specific.test.ts`
+ - `packages/tui` (`node:test`): `node --test test/specific.test.ts`
+ - If you create or modify a test file, run it and iterate on test or implementation until it passes.
+ - For `packages/coding-agent/test/suite/`, use `test/suite/harness.ts` + the faux provider. No real provider APIs, keys, or paid tokens.
+ - When regressions tests for fixing a github issue, add a comment with the github issue number next to the test.
+ - For ad-hoc scripts, `write` them to a temp file (e.g. `/tmp`), run, edit if needed, remove when done. Don't embed multi-line scripts in `bash` commands.
+ - Never commit unless the user asks.
- **DELIVERY STOP INVARIANT:** UNDER PROTOCOL 1, “PR OPENED” IS NEVER A VALID STOP CONDITION, GOAL SUCCESS CRITERION, OR FINAL TODO. DELIVERY ENDS ONLY WHEN GITHUB REPORTS `MERGED` AND THE TASK WORKTREE IS REMOVED. WHILE GATES ARE PENDING, KEEP MERGE/CLEANUP TODOS OPEN, MONITOR TO COMPLETION, THEN MERGE-COMMIT AND CLEAN UP BEFORE THE FINAL RESPONSE.
+ ## Dependency and Install Security
- ### PROTOCOL 2 — USER-REQUESTED PR REVIEWS
+ - Treat npm dep and lockfile changes as reviewed code. Direct external deps stay pinned to exact versions.
+ - When updating `undici`, you MUST read its changelog/release notes for the target version and evaluate whether any changes may affect functionality before applying the update.
+ - Hydrate/update locally with `npm install --ignore-scripts`; clean/CI-style with `npm ci --ignore-scripts`. Don't run lifecycle scripts unless the user asks.
+ - If dep metadata changes, refresh `package-lock.json` with `npm install --package-lock-only --ignore-scripts`.
+ - If `packages/coding-agent/npm-shrinkwrap.json` needs regen, run `node scripts/generate-coding-agent-shrinkwrap.mjs` (verify with `--check` or `npm run check`). New deps with lifecycle scripts require review and an explicit allowlist entry in that script; never add one silently.
+ - Pre-commit blocks lockfile commits unless `PI_ALLOW_LOCKFILE_CHANGE=1`. Don't bypass unless the user wants the lockfile change committed.
- **WHEN THE USER REQUESTS A PR REVIEW, YOU MUST:**
+ ## Git
- 1. **MAKE A NEW WORKTREE** — CREATE A DEDICATED GIT WORKTREE AND PULL THE PR BRANCH INTO IT. NEVER CHECK THE PR OUT IN THE SHARED WORKTREE.
- 2. **REVIEW INSIDE THAT WORKTREE** — RUN THE FULL REVIEW (READ, BUILD, TEST, QA) THERE.
- 3. **CLEAN UP WHEN THE REVIEW IS DONE** — THE MOMENT THE REVIEW IS FINISHED, REMOVE THE WORKTREE (`git worktree remove` THEN `git worktree prune`). A LEFTOVER REVIEW WORKTREE IS A DEFECT.
+ Multiple pi sessions may be running in this cwd at the same time, each modifying different files. Git operations that touch unstaged, staged, or untracked files outside your own changes will stomp on other sessions' work. Follow these rules:
- ## STRUCTURE
+ Committing:
- | Area | Purpose |
- |---|---|
- | `packages/ai/` | Provider-neutral streaming, models, auth, API implementations |
- | `packages/agent/` | Browser-safe agent loop plus optional Node harness |
- | `packages/coding-agent/` | `senpi` CLI, sessions, extensions, RPC, interactive mode |
- | `packages/tui/` | Differential terminal renderer and editor primitives |
- | `packages/protocol/`, `packages/server/`, `packages/client/` | Framed-CBOR wire protocol plus its server and client for remote sessions |
- | `packages/telemetry/` | Vendor-neutral telemetry contracts and typed schema utilities |
- | `packages/session-backends/` | Session backend adapters; `sqlite-node/` Node sqlite session store |
- | `packages/evals/` | Model-backed eval suites over real `AgentSession`; spends tokens by design |
- | `packages/pty/` | TypeScript PTY loader, sessions, registry, vendored prebuilds, pipe fallback |
- | `packages/senpi-codemode/` | Source-only persistent-kernel `eval` extension (js/py/rb/jl kernels) |
- | `crates/senpi-pty/` | Rust/N-API native PTY implementation and ABI owner |
- | `scripts/` | Build, validation, release, lock and environment tooling |
- | `bench/` | Benchmark baselines and improvement ledger (data only; run via `scripts/run-pr530-benchmarks.mjs`) |
- | `.github/` | CI/release/issue automation plus committed merge and release agent drivers |
- | `.agents/skills/senpi-qa/` | Required real-CLI QA harness; private dependency island outside the workspace |
- | `local-ignore/` | QA evidence archive; gitignored except deliberately tracked historical receipts |
+ - Only commit files YOU changed in THIS session.
+ - Stage explicit paths (`git add <path1> <path2>`); never `git add -A` / `git add .`.
+ - Before committing, run `git status` and verify you are only staging your files.
+ - `packages/ai/src/models.generated.ts` may always be included alongside your files.
+ - Message format: `{feat,fix,docs}[(ai,tui,agent,coding-agent)]: <commit message> (optionally multiple lines)`. Message is informative and concise.
- ## WHERE TO LOOK
+ Never run (destroys other agents' work or bypasses checks):
- | Task | Start here |
- |---|---|
- | Add a feature to the CLI | `packages/coding-agent/src/core/extensions/builtin/` |
- | Change provider/API behavior | `packages/ai/src/api/` then `packages/ai/src/providers/` |
- | Change Cursor transport or exec bridging | `packages/ai/src/api/cursor-agent/`, `packages/coding-agent/src/core/cursor-exec-bridge.ts` |
- | Change agent-loop semantics or the harness | `packages/agent/src/agent-loop.ts`, `packages/agent/src/harness/` |
- | Change interactive rendering | `packages/coding-agent/src/modes/interactive/` and `packages/tui/src/` |
- | Change app-server/RPC | `packages/coding-agent/src/modes/app-server/` or `.../modes/rpc/` |
- | Add or change coding-agent tests or examples | `packages/coding-agent/test/`, `packages/coding-agent/examples/` |
- | Change PTY behavior | `packages/pty/` and, for native behavior, `crates/senpi-pty/` |
- | Change model/provider runtime or docs | `packages/ai/src/{models.ts,auth,providers}`, `packages/coding-agent/docs/providers.md` |
- | Change compaction | mechanics in `packages/coding-agent/src/core/compaction/`; policy in `.../extensions/builtin/compaction/` |
- | Change wire protocol or remote sessions | `packages/protocol/`, then consumers `packages/server/` and `packages/client/` |
- | Change eval prompt/rendering | `packages/senpi-codemode/src/{prompt,tool,kernels}/` |
- | Audit changelogs or prepare a release | `.github/agent/commands/cl.md`, `scripts/release.mjs`, `scripts/release-packages.mjs` |
+ - `git reset --hard`, `git checkout .`, `git clean -fd`, `git stash`, `git add -A`, `git add .`, `git commit --no-verify`.
- ## CODE MAP
+ If rebase conflicts occur:
- Runtime flow: `ai` (models/auth -> providers -> api) feeds `agent/src/agent-loop.ts`, driven by `coding-agent/src/core` into interactive | print | RPC | app-server; `tui` renders, `pty` -> `crates/senpi-pty` runs terminals, `protocol` (framed CBOR) links `server` and `client`.
+ - Resolve conflicts only in files you modified.
+ - If a conflict is in a file you did not modify, abort and ask the user.
+ - Never force push.
- | Symbol / file | Role | Notes |
- |---|---|---|
- | `coding-agent/src/core/agent-session.ts` | Session runtime core | 8k LOC; highest-risk file in the repo |
- | `coding-agent/src/modes/interactive/interactive-mode.ts` | Interactive loop | 8.5k LOC; components under `interactive/components/` |
- | `agent/src/agent-loop.ts` | Browser-safe agent loop | Reached via `coding-agent/src/core/sdk.ts` |
- | `coding-agent/src/core/extensions/builtin/index.ts` | `builtinExtensions` order | 39 entries, `mcp` last; the only authority on numbering |
- | `ai/src/api/cursor-agent/gen/agent_pb.ts` | Generated protobuf-es | 19.6k LOC; regenerate, never hand-edit |
- | `tui/src/index.ts` | Renderer barrel | Consumer coupling point for coding-agent and senpi-codemode |
+ ## Issues and PRs
- ## COMMANDS
+ See `CONTRIBUTING.md` for the contributor gate (auto-close workflows, `lgtm`/`lgtmi`, quality bar).
- - Install dependencies: `npm install --ignore-scripts`. After an approved dependency change, `npm run refresh-lock` (lockfile + registry metadata + shrinkwrap + install-lock).
- - Full static validation after code changes: `npm run check` (biome, pinned-deps/ts-imports/shrinkwrap/install-lock checks, `check:claude-sdk-platform-lock`, `tsc --noEmit`, browser-smoke). It runs no tests; CI runs the same commands, so keep them in sync. Broad validation: `npm test`.
- - Narrow tests run from the package root using that package's test command. Runners differ: Vitest for `ai`, `coding-agent`, `senpi-codemode`, `server`, `session-backends`, `telemetry`; `node --test --import tsx` for `tui`; `node --test` for `scripts/` (`npm run test:scripts`) and `.agents/skills/senpi-qa/scripts/lib/`.
- - App-server transport QA is its own channel: `npm run qa:app-server` (`packages/coding-agent/scripts/qa-app-server/`), not part of `npm test`. Model catalog data: `npm run hydrate:model-data`, verified by `check:model-data`, from the repository root.
- - Never run `npm run dev` in this repository.
+ When reviewing PRs:
- ## CONVENTIONS
+ - Do not run `gh pr checkout`, `git switch`, or otherwise move the worktree to the PR branch unless the user explicitly asks.
+ - Use `gh pr view`, `gh pr diff`, `gh api`, and local `git show`/`git diff` against fetched refs to inspect PR metadata, commits, and patches without changing branches.
+ - If you need PR file contents, fetch/read them into temporary files or use `git show <ref>:<path>` without switching branches.
- - Read files in full before broad edits; prefer existing patterns and public extension APIs over new core behavior.
- - TypeScript under `packages/*/src`, `packages/*/test`, and `packages/coding-agent/examples` must use erasable syntax. Avoid `any` and verify external types in `node_modules`.
- - Imports are top-level by default. Inline or dynamic imports are forbidden except existing documented lazy/browser-safe boundaries such as `packages/ai/src/api/*.lazy.ts` and credential probes.
- - Do not hardcode TUI keys; add defaults to `packages/tui/src/keybindings.ts` or `packages/coding-agent/src/core/keybindings.ts`.
- - Never hand-edit generated sources: `packages/ai/src/{models,image-models}.generated.ts` and `src/providers/data/*.json` (regenerate via `packages/ai/scripts/generate-models.ts`), `packages/ai/src/api/cursor-agent/gen/agent_pb.ts` (`buf generate` + `scripts/transform-cursor-agent-proto.mjs`), `crates/senpi-pty/index.{js,d.ts}` (napi-rs), `packages/coding-agent/install-lock/*`, and `packages/coding-agent/src/modes/app-server/protocol/generated/`. Builtin extension registration order is authoritative only in `builtin/index.ts` — never quote a registration number from prose.
- - Ask before removing intentional functionality; backward compatibility is opt-in, not automatic.
- - Changing fork-specific source behavior means reading the nearest `changes.md` first and updating it in the same verified increment, not in a follow-up. Merges resolve tracker files to `ours`, so a stale entry misleads the next upstream sync.
- - Changelog edits are release/audit work only: follow `.github/agent/commands/cl.md`, never edit released sections, and satisfy the changelog gate (`.github/workflows/changelog-gate.yml`) for both CHANGELOG.md and changes.md — see below.
+ When creating issues:
- ## CHANGES.MD TRACKER POLICY
+ - Add `pkg:*` labels for affected packages (`pkg:agent`, `pkg:ai`, `pkg:coding-agent`, `pkg:tui`); use all that apply.
- - Upstream ownership is pinned by `.github/upstream.json` (`badlogic/pi-mono` tag + sha): a production path in that pinned tree is upstream-owned; a path absent from it (and not a rename destination) is fork-only and exempt.
- - Production scope is every changed path except: `changes.md` trackers and `.github/upstream.json`; lockfiles (`package-lock.json`, `npm-shrinkwrap.json`, `pnpm-lock.yaml`, `yarn.lock`, `bun.lock`, `Cargo.lock`, `*.lock`, `*.lock.json`); non-production metadata (`.gitignore`, `LICENSE`, `test.sh`); test/fixture/example/doc trees (`__tests__`, `tests`, `fixtures`, `examples`, `docs`) plus `*.test.*`/`*.spec.*`; `.md`/`.mdx`; and `*.generated.{ts,mts,cts,js}` sources.
- - Every upstream-owned production path must be covered in its exact nearest ancestor `changes.md` — never a farther tracker — by an entry naming that exact repo-relative path under all four canonical headings: `What changed`, `Why`, `Why an extension could not handle it`, `Expected merge conflict zones`.
- - Tracker coverage is independent of the release changelog: `no-changelog` waives only the CHANGELOG.md entry, never changes.md, and coverage never substitutes for a required CHANGELOG.md entry.
- - A pin-sync PR — one that edits `.github/upstream.json` — exempts upstream-owned paths that exactly match the new pinned tree, but paths still divergent from the new pin are integration repairs and must gain coverage from tracker entries in the same PR.
- - Enforcement: `scripts/check-pr-changelog.mjs` gates every PR through `.github/workflows/changelog-gate.yml` (counting only tracker entries the PR itself touches), and `scripts/audit-changes-md.mjs` audits the whole tree against the pin (`--format json|markdown`).
+ When posting issue/PR comments:
- ## QUALITY GATES
+ - Write the comment to a temp file and post with `gh issue/pr comment --body-file` (never multi-line markdown via `--body`).
+ - Keep comments concise, technical, in the user's tone.
+ - End every AI-posted comment with the AI-generated disclaimer line specified by the originating prompt (e.g. `This comment is AI-generated by `/wr``).
- - Any runtime change under `packages/{ai,agent,coding-agent,tui,pty,senpi-codemode}` (the release-managed set) plus `crates/senpi-pty` requires scoped tests, `npm run check`, and real CLI QA through `.agents/skills/senpi-qa/`.
- - Save QA receipts under `local-ignore/qa-evidence/<YYYYMMDD>-<slug>/`; no evidence means no commit or push. Evidence, logs, comments, and PR bodies must never contain tokens, credentials, auth headers, cookies, or raw environment dumps.
- - Default/unit tests must not spend tokens or require real credentials; coding-agent tests use the faux provider and `packages/coding-agent/test/suite/harness.ts` (the legacy `test/test-harness.ts` must not be extended).
- - Tests added or changed run directly until green. New coding-agent lifecycle tests go in `test/suite/`; issue regressions in `test/suite/regressions/<issue>-<slug>.test.ts`; the flat `test/*.test.ts` root cluster is legacy placement and must not grow.
- - Test quarantine is a safety boundary: `test/setup.ts` forces `SENPI_CODING_AGENT_DIR` into a temp dir and always wins over an inherited value. Never reintroduce an `if (!process.env.SENPI_CODING_AGENT_DIR)` short-circuit — that once deleted a real user agent dir.
- - Live/credentialed surfaces are opt-in only: `packages/ai/test/live-api-gates.ts` (`PI_ENABLE_*`), `packages/coding-agent/test/integration/` (`PI_RUN_INTEGRATION=1`), `packages/evals` (`npm run eval -- --provider X --model Y`). `packages/evals/.eval/` artifacts hold prompts and responses — treat as sensitive.
- - Async tests subscribe before triggering, with bounded deadlines or fake timers; fixed sleeps survive only at genuine OS boundaries and must not be copied from legacy tests.
- - Documentation-only changes use focused validators and `git diff --check`, not runtime QA — but `packages/coding-agent/docs/` ships in the tarball and is test-asserted, so doc edits there can fail CI.
+ When closing issues via commit:
- ## DEPENDENCIES AND INFRA
+ - Include `fixes #<number>` or `closes #<number>` in the message so merging auto-closes the issue. For multiple issues, repeat the keyword per issue (`closes #1, closes #2`); a shared keyword (`closes #1, #2`) only closes the first.
- - Treat dependency and lockfile diffs as code: pin direct external dependencies exactly, use `--ignore-scripts` for install/lock refreshes. The pre-commit hook allows workspace-metadata-only refreshes; other lockfile changes require explicit `PI_ALLOW_LOCKFILE_CHANGE=1` approval.
- - Keep shared environment surfaces synchronized: dependency, Node, provider/env, QA-channel, build-command, and forwarded-port changes must update `scripts/devenv-setup.mjs`, `.devcontainer/devcontainer.json`, and related references together, keeping root `package.json` workspaces and `pnpm-workspace.yaml` aligned with any workspace-package move or rename.
- - Regenerate `packages/coding-agent/publish-deps.lock.json` with `node scripts/generate-coding-agent-shrinkwrap.mjs`; never replace it with `npm-shrinkwrap.json`. Regenerate `packages/coding-agent/install-lock/` with `npm run install-lock:coding-agent`.
- - External registry entries in root, publish, and installer locks must preserve both npm tarball `resolved` URLs and `integrity` hashes; incomplete merge results are invalid even when dependency topology still resolves.
- - `@earendil-works/pi-telemetry` is a runtime dependency and must stay in Senpi's owned CalVer alias, publish, and bundle sets. `@earendil-works/pi-storage-sqlite-node` remains private and independently versioned because it is not reachable from the shipped coding-agent runtime.
- - Dependencies with lifecycle scripts require package/version review and an explicit justified generator allowlist entry; never add one silently to pass the gate.
+ ## Testing pi Interactive Mode with tmux
- ## GIT AND DELIVERY
+ Run the TUI in a controlled terminal (from the repo root):
- - Multiple agents share this worktree. Stage only files changed in the current session with explicit `git add <path>`; do not commit speculatively — commit only when the user asks or a delegated workflow already ends in commit/push.
- - Never use `git reset --hard`, `git checkout .`, `git clean -fd`, `git stash`, `git add -A`, `git add .`, `git commit --no-verify`, or force-push. Review incoming PRs per PROTOCOL 2, never by switching this shared worktree.
- - Commit format `{feat,fix,docs}[(scope)]: concise message` with `fixes #N` / `closes #N` when applicable. Normal work ships through a feature branch and reviewer-readable PR with evidence; merge with a merge commit, never squash or rebase merge.
- - Resolve rebase conflicts only in files owned by the current session; otherwise abort and ask.
+ ```bash
+ tmux new-session -d -s pi-test -x 80 -y 24
+ tmux send-keys -t pi-test "./pi-test.sh" Enter
+ sleep 3 && tmux capture-pane -t pi-test -p # capture after startup
+ tmux send-keys -t pi-test "your prompt here" Enter
+ tmux send-keys -t pi-test Escape # special keys (also C-o for ctrl+o, etc.)
+ tmux kill-session -t pi-test
+ ```
- ## RELEASE NOTES
+ ## Changelog
- - Releases use CalVer and lockstep-version the packages in `scripts/release-packages.mjs`; the pipeline runs `.github/agent/` drivers -> `scripts/release.mjs` -> `publish-npm.yml` -> `build-binaries.yml` / `native-prebuilds.yml`.
- - Release only from clean `main` after changelog audit and release smoke tests; `scripts/release.mjs` owns versioning, generated artifacts, checks, commits, tag, and push.
- - Never rerun the release script after its tag is pushed; failed publishing is retried from the existing tag workflow. Publishing is fork-scoped: `scripts/publish.mjs` rewrites private `@earendil-works/pi-*` packages into public `@code-yeongyu/senpi-*` manifests, and upstream names never appear on npm.
+ Location: `packages/*/CHANGELOG.md` (one per package).
- ## NOTES
+ Sections under `## [Unreleased]`: `### Breaking Changes` (API changes requiring migration), `### Added`, `### Changed`, `### Fixed`, `### Removed`.
- - Deep guidance lives in ~60 nested `AGENTS.md` files holding the file-level maps this root omits; read the nearest one before editing. `packages/coding-agent` is by far the largest package (~105k LOC with tests).
- - `packages/ai` tests alias `@earendil-works/pi-telemetry` to telemetry source, so telemetry breakage fails AI tests. Node floors differ: packages require >=22.19.0, root and CI use Node 24.
- - `packages/tui` uses tabs in source and its own `node --test` runner — do not apply coding-agent test habits there. `packages/coding-agent/bin/senpi` is only a symlink to built output; launcher logic lives in `src/cli.ts` / `src/bun-runtime.ts`.
+ Rules:
+
+ - All new entries go under `## [Unreleased]`. Read the full section first and append to existing subsections; never duplicate them.
+ - Released version sections (e.g. `## [0.12.2]`) are immutable; never modify them.
+ - Do not create changelog entries when working on a branch other than `main` or pull request
+
+ Attribution:
+
+ - Internal (from issues): `Fixed foo bar ([#123](https://github.com/earendil-works/pi-mono/issues/123))`
+ - External contributions: `Added feature X ([#456](https://github.com/earendil-works/pi-mono/pull/456) by [@username](https://github.com/username))`
+
+ ## Releasing
+
+ **Lockstep versioning**: all packages share one version; every release updates all together. `patch` = fixes + additions, `minor` = breaking changes. No major releases.
+
+ 1. **Update CHANGELOGs**: ask the user whether they ran the `/cl` prompt on the latest commit on `main`. If not, they must run `/cl` first to audit and update each package's `[Unreleased]` section before releasing.
+
+ 2. **Local smoke test**: build an unpublished release and smoke test from outside the repo (so it can't resolve workspace files):
+ ```bash
+ npm run release:local -- --out /tmp/pi-local-release --force
+ cd /tmp
+
+ # Node package install smoke tests
+ /tmp/pi-local-release/node/pi --help
+ /tmp/pi-local-release/node/pi --version
+ /tmp/pi-local-release/node/pi --list-models
+ /tmp/pi-local-release/node/pi -p "Say exactly: ok"
+ /tmp/pi-local-release/node/pi
+
+ # Bun binary smoke tests
+ /tmp/pi-local-release/bun/pi --help
+ /tmp/pi-local-release/bun/pi --version
+ /tmp/pi-local-release/bun/pi --list-models
+ /tmp/pi-local-release/bun/pi -p "Say exactly: ok"
+ /tmp/pi-local-release/bun/pi
+ ```
+ Verify both Node and Bun startup, model/account listing, interactive startup, and at least one real prompt with the intended default provider. The bare commands `/tmp/pi-local-release/node/pi` and `/tmp/pi-local-release/bun/pi` start interactive mode; run each in tmux, submit a prompt, and wait for the model reply before considering the interactive smoke test passed. Failures are release blockers unless the user explicitly accepts the risk.
+
+ 3. **Run the release script**:
+ ```bash
+ PI_ALLOW_LOCKFILE_CHANGE=1 npm_config_min_release_age=0 npm run release:patch # fixes + additions
+ PI_ALLOW_LOCKFILE_CHANGE=1 npm_config_min_release_age=0 npm run release:minor # breaking changes
+ ```
+ Use `npm_config_min_release_age=0` only for the release command. The repo's normal npm age gate can otherwise block the release lockfile refresh when the current workspace package version was published recently. Review any lockfile or shrinkwrap diffs the release creates before push.
+
+ The release script bumps all package versions, updates changelogs, regenerates release artifacts, runs `npm run check`, commits `Release vX.Y.Z`, tags `vX.Y.Z`, adds fresh `## [Unreleased]` changelog sections, commits `Add [Unreleased] section for next cycle`, then pushes `main` and the tag. Do not rerun the release script after a tag was pushed.
+
+ 4. **CI verifies and announces the npm release**: pushing the `vX.Y.Z` tag triggers `.github/workflows/build-binaries.yml`. The `publish-npm` job uses npm trusted publishing through GitHub Actions OIDC with environment `npm-publish`; no local `npm publish`, `npm whoami`, OTP, or WebAuthn flow is required. After publishing, `announce-pi-dev-release` verifies every public workspace package resolves at the exact release version and that its npm tarball is available, then writes the verified release marker to R2. `pi.dev/api/latest-version` reads that marker; it must never announce a release from npm before this job succeeds.
+
+ 5. **If CI publish or announcement fails**: inspect the failed job. The publish helper is idempotent and skips package versions already present on npm; the announcement job rechecks availability before updating the R2 marker. Rerun the failed job or workflow after fixing CI or transient npm issues. Do not rerun `npm run release:patch` or `npm run release:minor` for the same version.
+
+ ## User Override
+
+ If the user's instructions conflict with any rule in this document, ask for explicit confirmation before overriding. Only then execute their instructions.