Acolyte is a terminal-first AI coding assistant: local-first, observable, extensible. Read docs/architecture.md before working on unfamiliar subsystems.
Extension points:
- New post-generation behavior →
Evaluatorinlifecycle-evaluators.ts, add toEVALUATORS - New tool guard →
ToolGuardintool-guards.ts, add toGUARDS - New tool → appropriate
*-toolkit.ts; all tools flow throughrunTool
These must always hold. Break them and the system breaks.
- All tools go through
runToolintool-execution.ts— never call a tool function directly. - Every RPC payload, model response, and config value is validated through Zod before entering the type system.
@signalis a suffix — model output must end with exactly one@signalline. Strip the signal line and everything after it.- TUI state updaters must use functional form (
setState(prev => ...)) when reading current state — stale closure reads cause race conditions. - Run
bun run verifybefore every commit.
- Start from latest
main. - Read relevant files before editing. Use errors, logs, tests, and source as evidence — never guess.
- Keep changes scoped to the task. Defer out-of-scope work to issues.
- Clean up code you touch — but don't chase cleanup into unrelated files.
- Before creating a new file: check whether an existing one is the right place.
- When behavior and tests diverge: fix the implementation. Update expectations only if explicitly requested.
- Default to autonomous execution. Pause only when a decision is ambiguous, risky, or irreversible.
- When unexpected diffs or artifacts appear: stop and confirm before continuing.
- Ask when intent is unclear. Lead with outcomes, changed files, and next steps — not process.
- Commit only when explicitly requested.
- Format:
type(scope): description— types:feat,fix,refactor,docs,test,chore. - Single-line subject, no body, under 72 characters. ASCII only — no arrows, symbols, or emoji.
- Never amend commits already pushed to remote — create a new commit instead.
- Cut releases only for user-facing features or meaningful bug fixes — not for internal refactors or tooling cleanup alone.
- Branch names use hyphens, no slashes (e.g.
di-pattern, notrefactor/di-pattern).
- Before adding anything new: ask "will this be used right now?" If not, don't add it.
- Before abstracting: find at least three similar cases first.
- When a function mixes concerns (e.g. persistence + display): split it.
- Before finalizing a fix: ask "root cause or symptom?" If symptom, keep digging.
- Before committing non-trivial changes: ask "is there a more elegant solution?"
- No transitional architecture at shared seams: if the proper fix belongs in a different boundary or contract, move it there. Do not stop at a "good enough for this slice" shape; land the canonical owner, normalized contract, and single source of truth before committing.
- If you must leave tech debt: get explicit agreement and mark it
TODO(username):. - When choosing between a prompt/tool-contract fix and host-side classification logic: prefer the contract fix.
- Before committing chat-feature changes: UX intent is clear, regression test exists, smoke run passes.
- When adding a subsystem boundary: define the interface first, implement second.
- When defining a string union or shared type: define it as a Zod schema first and infer the TS type from it.
- No banner or separator comments. Import from the canonical source module directly — no re-export layers.
- No direct
useEffectin chat-layer code. UseuseMountEffect(mount-only),useSyncEffect(state sync on dep change),useInterval(timers), oruseAsyncEffect(async deps) fromsrc/tui/effects.ts. Prefer derived state and event handlers over effects.
- Never call a task done without proving it works. Run tests, check output, demonstrate correctness.
- While iterating, run the narrowest check:
bun run typecheck,bun run lint, orbun test <file>. - Run
bun run verifyas the final gate before committing. - Never commit on red. If the baseline is already red: restore green first, then continue.
- Manual testing only at milestone checkpoints. Document any validation that couldn't run and why.
- Before closing a fix: ask "could this regress silently?" If yes, add a test.
- Before adding a test: ask "does this cover behavior that could realistically break?" If not, skip it.
- For unit tests, keep a module's unit coverage in its canonical
<module>.test.tsfile. Split into separate test files only for distinct subsystems or intentionally separate surfaces. - When a test and runtime disagree: fix the test unless a real runtime bug is proven.
- Never add test-only branches, flags, mocks, or behavior changes to runtime code.
- Never run destructive git or file operations unless explicitly requested.
- Never amend commits already pushed to remote — create a new commit instead.
- Use
--force-with-leaseover--force. - Do not discard unrelated changes without approval.
- Never revert commits — drop with
git resetif not pushed; revert only as a last resort.