Generated: 2026-08-24
Commit: baf15a54d
Branch: initdeep-refresh-20260824
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.
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:
- EXPLORE — READ EVERY FILE, SYMBOL, AND TEST THE CHANGE TOUCHES BEFORE WRITING A SINGLE LINE.
- MAKE A PLAN — A DECISION-COMPLETE PLAN EXISTS BEFORE ANY CODE.
- ADD TODOS IN ULTRA-DETAIL — MIRROR EVERY ATOMIC PLAN STEP INTO THE TODO LIST.
- MAKE A NEW WORKTREE — NEVER IMPLEMENT IN THE SHARED WORKTREE.
- MAKE A PR AND WORK UNTIL IT GETS MERGED — AN UNMERGED PR IS UNFINISHED WORK.
- SET A GOAL AND RUN THE ULW LOOP — EVERY SUCCESS CRITERION PASSES WITH CAPTURED EVIDENCE.
- MANAGE TODOS OBSESSIVELY — UPDATE ON EVERY STATE TRANSITION. A STALE TODO LIST IS A DEFECT.
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.
WHEN THE USER REQUESTS A PR REVIEW, YOU MUST:
- 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.
- REVIEW INSIDE THAT WORKTREE — RUN THE FULL REVIEW (READ, BUILD, TEST, QA) THERE.
- CLEAN UP WHEN THE REVIEW IS DONE — THE MOMENT THE REVIEW IS FINISHED, REMOVE THE WORKTREE (
git worktree removeTHENgit worktree prune). A LEFTOVER REVIEW WORKTREE IS A DEFECT.
| 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 and never tracked (the PR body carries the QA summary) |
| 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 RPC | packages/coding-agent/src/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 |
Runtime flow: ai (models/auth -> providers -> api) feeds agent/src/agent-loop.ts, driven by coding-agent/src/core into interactive | print | RPC; tui renders, pty -> crates/senpi-pty runs terminals, protocol (framed CBOR) links server and client.
| 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 |
- Install dependencies:
bun install --ignore-scripts. After an approved dependency change,bun run refresh-lock(lockfile + registry metadata + install-lock). - Full static validation after code changes:
bun run check(oxlint, oxfmt, the lint ratchet, pinned-deps/ts-imports/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:bun run testrunstest:scripts, thenscripts/run-workspaces.mjsruns every workspacetestscript sequentially in path order with the package manager you invoked, sobun run test,npm run test, andpnpm run testrun the same suites the same way. - Narrow tests run from the package root using that package's test command (
bun run --cwd packages/<pkg> test -- <args>), or from the repository root through the runner (bun run test --workspace packages/<pkg> -- <args>, which runs the scripts tests first). Runners differ: Vitest forai,coding-agent,senpi-codemode,server,session-backends,telemetry;node --test --import tsxfortui;node --testforscripts/(bun run test:scripts) and.agents/skills/senpi-qa/scripts/lib/. - Model catalog data:
bun run hydrate:model-data, verified bycheck:model-data, from the repository root. - Never run
bun run devin this repository.
- Read files in full before broad edits; prefer existing patterns and public extension APIs over new core behavior.
- TypeScript under
packages/*/src,packages/*/test, andpackages/coding-agent/examplesmust use erasable syntax. Avoidanyand verify external types innode_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.tsand credential probes. - Do not hardcode TUI keys; add defaults to
packages/tui/src/keybindings.tsorpackages/coding-agent/src/core/keybindings.ts. - Never hand-edit generated sources:
packages/ai/src/{models,image-models}.generated.tsandsrc/providers/data/*.json(regenerate viapackages/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), andpackages/coding-agent/install-lock/*. Builtin extension registration order is authoritative only inbuiltin/index.ts— never quote a registration number from prose. - Root
package.jsonscripts reach workspaces only throughnode scripts/run-workspaces.mjs; nevernpm run --workspaces,npm --workspace=<name> run,npm --prefix <dir> run, orcd <dir> && npm run(scripts/root-workspace-scripts.test.mjsfails the manifest). Those shapes hardcode npm, and under bun the flag-after-name form re-entered the root script forever. - Ask before removing intentional functionality; backward compatibility is opt-in, not automatic.
- Changing fork-specific source behavior means reading the nearest
changes.mdfirst and writing its entry in the same verified increment, not in a follow-up. Merges resolve tracker files toours, so a stale entry misleads the next upstream sync. - Change fragments (senpi#3610): a PR never edits an existing
changes.mdor a packageCHANGELOG.md[Unreleased]section (every PR editing the same top lines made all of them conflict). It adds ONE file,changes/unreleased/<pr-number>.md, with one<!-- changes-log: <path> -->block per log: a tracker block holds the dated##entry with its four canonical sections, a changelog block holds### <Section>plus bullets. Onlyscripts/release.mjscompiles and deletes fragments at the release cut, before stamping. Format:changes/unreleased/README.md; convert an old-style branch withnode scripts/sync-changes-fragments.mjs from-diff --base origin/main --pr <n>. - 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.
- Upstream ownership is pinned by
.github/upstream.json(badlogic/pi-monotag + 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.mdtrackers 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(a fragment block for that tracker counts) — 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-changelogwaives 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.mjsgates every PR through.github/workflows/changelog-gate.yml(counting only tracker entries the PR itself touches), andscripts/audit-changes-md.mjsaudits the whole tree against the pin (--format json|markdown).
- Any runtime change under
packages/{ai,agent,coding-agent,tui,pty,senpi-codemode}(the release-managed set) pluscrates/senpi-ptyrequires scoped tests,bun 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. The receipts stay local: summarize them in the PR body (decisive excerpt plus asha256sumline per file) and nevergit add -fthem;scripts/tracked-harness-artifacts-audit.test.mjsfails when any path under.omo/,local-ignore/,.qa-evidence/orqa-evidence/is tracked. 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 legacytest/test-harness.tsmust not be extended). - Tests added or changed run directly until green. New coding-agent lifecycle tests go in
test/suite/; when a regression test fixes a GitHub issue, add a comment with the issue number next to the test; the flattest/*.test.tsroot cluster is legacy placement and must not grow. - Test quarantine is a safety boundary:
test/setup.tsforcesSENPI_CODING_AGENT_DIRinto a temp dir and always wins over an inherited value. Never reintroduce anif (!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(bun 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 — butpackages/coding-agent/docs/ships in the tarball and is test-asserted, so doc edits there can fail CI. - For manual checks of the interactive TUI in a controlled tmux terminal, load and follow
.senpi/skills/interactive-testing.md.
Before adding or changing a test, answer all four; a missing answer means do not add it yet:
- What observable behavior, invariant, or independent contract does it protect?
- What credible regression makes it fail?
- Why does existing coverage not already catch that failure? Each contract has one primary test owner at the strongest boundary; extend a table case or shared fixture instead of adding a near-duplicate.
- Does it need a production seam (export, flag, wrapper, injection hook) no production caller needs? If yes, test at the real boundary instead. Junk patterns (reject unless the retention bar below applies): assertion-free probes; self-comparisons and identity copiers; copied fixtures, inventories, manifests or export lists; exact source, import or string greps; private call-shape tests duplicated at a real boundary; duplicate invocations of one contract; replays of a shared helper through a wrapper; tests that exist to keep a test-only export or wrapper alive; production code whose only callers are tests; expected values produced by the code under test; mocks that implement the asserted behavior; fixture-supplied receipts or postconditions the code under test should produce; assertions against a store nothing writes; capability or flag restatements without a delivery proof; negative controls that pass for an unrelated reason; names that promise more than the input exercises. Retention bar: keep a pattern match only when it independently guards a public API, protocol, config, migration, storage, security, platform, default, prompt-byte, generated or cross-language, package, release, or architecture contract, and say which one in the test. Static or slow is never a deletion reason. A retained test that fails on the base is a product bug to fix at its owner, never a test to delete. A bug regression test must fail on the pre-fix code for the intended reason; an existing owner test that already fails may serve as that proof. Method source: openclaw test-audit skill.
- Treat dependency and lockfile diffs as code: pin direct external dependencies exactly, use
--ignore-scriptsfor install/lock refreshes. The pre-commit hook allows workspace-metadata-only refreshes; other lockfile changes require explicitPI_ALLOW_LOCKFILE_CHANGE=1approval. - 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 rootpackage.jsonworkspaces andpnpm-workspace.yamlaligned with any workspace-package move or rename. - Never publish an
npm-shrinkwrap.json. Regeneratepackages/coding-agent/install-lock/withbun run install-lock:coding-agent. - External registry entries in root and installer locks must preserve both npm tarball
resolvedURLs andintegrityhashes; incomplete merge results are invalid even when dependency topology still resolves. @earendil-works/pi-telemetryis a runtime dependency and must stay in Senpi's owned CalVer alias and publish sets.@earendil-works/pi-storage-sqlite-noderemains 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.
- 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 messagewithfixes #N/closes #Nwhen 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.
- 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
mainafter changelog audit and release smoke tests;scripts/release.mjsowns 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.mjsrewrites private@earendil-works/pi-*packages into public@code-yeongyu/senpi-*manifests, and upstream names never appear on npm. - For release preparation, local release smoke tests, publishing, and recovery, load and follow
.senpi/skills/release.md; it points at.github/agent/release-driver.mdand the two-phasepublish-npm.ymlflow.
- Deep guidance lives in ~60 nested
AGENTS.mdfiles holding the file-level maps this root omits; read the nearest one before editing.packages/coding-agentis by far the largest package (~105k LOC with tests). packages/aitests alias@earendil-works/pi-telemetryto telemetry source, so telemetry breakage fails AI tests. Node floors differ: packages require >=22.19.0, root and CI use Node 24.packages/tuiuses tabs in source and its ownnode --testrunner — do not apply coding-agent test habits there.packages/coding-agent/bin/senpiis only a symlink to built output; launcher logic lives insrc/cli.ts/src/bun-runtime.ts.
Three PR labels drive the review workflow; automation lives in .github/workflows/review-claims.yml:
will-review— a reviewer claims the PR ("I will review this"). Applying it auto-requests the labeler as reviewer and BLOCKS merge via the requiredReview claim gatecheck.in-review— the claimer is actively reviewing. Same merge-blocking + auto-reviewer-request effects.stale-review— a claim sat 3+ days without the claimer's review; the sweep removes the claim labels and applies this one. A fresh claim clears it.
Rules:
- Apply
will-reviewwhen you plan to review a PR; switch toin-reviewwhen you start. - NEVER merge a PR carrying
will-revieworin-review; the gate check enforces this. - Claim labels are removed automatically ONLY when the claimer (the person who applied the label) submits an approve or request-changes review. Do not remove someone else's claim label by hand.
- If a PR shows
stale-review, it needs a (new) reviewer: claim it.