Operational guidance for coding agents working in doubt.nvim.
This file is intentionally practical: run commands exactly as listed, follow module ownership, and preserve existing behavior unless explicitly changing it.
- Project:
doubt.nvim(Neovim plugin for skeptical code review and handoff export). - Language/runtime: Lua on Neovim APIs (
vim.api,vim.fn,vim.ui,vim.json,vim.uv). - Required dependency:
MunifTanjim/nui.nvim. - Entrypoints:
plugin/doubt.luabootstrap andrequire("doubt").setup(...). - Core workflow: create claims -> persist sessions -> refresh/reanchor freshness -> export XML/templates.
plugin/doubt.lua: plugin bootstrap, command registration on load.lua/doubt/init.lua: top-level orchestration and public API.lua/doubt/config.lua: defaults, setup merge, highlights.lua/doubt/claims.lua: claim normalization, sorting, anchor validation.lua/doubt/state.lua: persistence and workspace-scoped session mutation.lua/doubt/render.lua: extmarks/signs/inline note rendering.lua/doubt/panel.lua: side panel UI and help modal.lua/doubt/export.lua: XML and template rendering.lua/doubt/input.lua: wrappers aroundnui.inputand checklist input.lua/doubt/commands.lua::Doubt*command wiring.lua/doubt/keymaps.lua: default keymaps and claim-kind mappings.tests/*_spec.lua: Plenary/Busted-style headless Neovim specs.tests/minimal_init.lua: minimal runtime setup for Plenary tests.tests/helpers/bootstrap.lua: test bootstrap and minimal assertions.
There is no separate build step and no configured linter/formatter in-repo (stylua, luacheck, selene, make, and CI config are absent).
Use the Plenary headless runner for tests. scripts/run_tests.sh downloads plenary.nvim to .deps/plenary.nvim if PLENARY_DIR is not set.
# Run full test suite (all spec files)
./scripts/run_tests.sh
# Run one specific test file (single-test workflow)
./scripts/run_tests.sh tests/plugin_bootstrap_spec.lua
# Example: run another specific spec file
./scripts/run_tests.sh tests/claim_anchor_model_spec.luaRecommended verification flow for code changes:
- Run the specific changed spec file(s) first.
- Run the full suite before finalizing.
- If behavior touches UI flows, do a quick manual Neovim smoke test (
:DoubtPanel, claim add/edit/delete, export).
- Tests are file-scoped Plenary/Busted specs.
- "Run a single test" means pass one
*_spec.luafile to./scripts/run_tests.sh. - Keep temporary state isolated using
vim.fn.tempname()patterns (as existing tests do). - When adding a new spec, use naming
tests/<feature>_spec.lua.
- Use tabs for indentation in Lua files.
- Keep lines readable; prefer small helper functions over deep nesting.
- Preserve existing table literal and function-call formatting style in touched files.
- No auto-formatter is enforced; match local file style manually.
- Place
local mod = require("...")near file top. - Prefer explicit module paths (
require("doubt.state"), not dynamic require patterns). - Typical ordering:
- internal
doubt.*modules, - third-party modules (
nui.*), local M = {}and module-local state.
- internal
- Avoid circular dependencies; push shared pure logic into owning module instead.
- No static type system is configured; enforce shapes by normalization and guards.
- Normalize claims through
claims.normalize_claim(...)before persistence/use. - Keep claim fields stable (
id,kind,start_line,start_col,end_line,end_col,note,freshness,anchor). - Treat path keys as normalized via
vim.fs.normalizebefore storing in state. - Session names must be trimmed and non-empty.
- Use
snake_casefor locals/functions. - Keep module API on
local M = {}with explicit exported function names. - Use descriptive names for derived values (
session_name,workspace_key,normalized_claims). - Keep command/action names aligned with established
Doubt*vocabulary.
- Prefer early returns for invalid states.
- Validate user inputs at boundaries (commands, input callbacks, persistence load).
- Route user-facing messages through
ctx.notifywhere context exists. - Use
vim.log.levels.INFOfor benign/empty states. - Use
vim.log.levels.WARNfor invalid actions or unknown session/claim targets. - Use
vim.log.levels.ERRORfor hard failures (save/load/export failures). - Do not throw errors for expected UX paths when notify + return is sufficient.
- Keep persistence logic in
state.lua; do not leak file I/O into UI modules. - Workspace scoping lives under
state.workspaces[workspace_key]; preserve this shape. - Maintain deterministic claim order with
claims.sort_claims(...). - Drop empty file claim buckets during normalization.
- Preserve freshness semantics:
fresh: anchor/range still valid,reanchored: anchor found at updated location,stale: ambiguous/missing/changed anchor.
- Keep
commands.luaandkeymaps.luaas thin wiring layers. - Put claim math/normalization in
claims.lua, not panel/render modules. - Put panel-specific interaction in
panel.lua; rendering primitives inrender.lua. - Preserve existing keymap defaults and opt-out behavior (
keymaps = falseor per-key false).
- Follow existing test style:
local t = dofile("tests/helpers/bootstrap.lua"). - Use
t.assert_eqandt.assert_matchhelpers for assertions. - Keep specs deterministic and isolated (explicit setup, no hidden shared globals).
- Validate both state mutation and user-visible behavior (notify text, mappings, buffer options).
- Canonical payload is raw XML grouped by file.
:DoubtExportapplies template wrappers; default template is configurable (currentlyreview).- Trusted export includes only
fresh+reanchoredclaims. - Stale claims are skipped and should be reported in notify output.
- Supported template variables:
{{xml}},{{session}}/{{session_name}},{{file_count}},{{claim_count}}.
- Make focused changes in owning module first; avoid cross-module refactors unless required.
- Do not silently alter command names, key defaults, or persistence schema.
- Do not commit
.planningartifacts unless explicitly requested. - If adding behavior, add or update matching spec coverage in
tests/.
At time of writing, no repo-local rule files were found:
- No
.cursor/rules/directory. - No
.cursorrulesfile. - No
.github/copilot-instructions.mdfile.
If any of these are added later, treat them as higher-priority agent instructions and update this document accordingly.