Contributing
This page is the public contributor guide for AICodeReviewer. It covers the
repository layout, local development setup, the test and validation matrix,
and the common contribution workflows. The repository’s AGENTS.md holds the
always-on rules, guardrails, environment notes, and the list of known
codebase pitfalls to avoid reintroducing — read it before larger changes.
Repository layout
Section titled “Repository layout”| Path | Purpose |
|---|---|
packages/* |
Runtime TypeScript packages (CLI, core, server, agents, sandbox, outputs, mcp-output, llm, vcs, store, eval). Managed with pnpm workspaces and TypeScript project references. |
docs/site |
This documentation site (Astro Starlight, English + 简体中文). An isolated workspace package; not part of the runtime. |
docs/ (other) |
Topical reference modules (e.g. output channels) consulted while writing these pages. |
example/ |
Deployment sample: config.yaml, .env.sample, Compose stack, trigger scripts. |
deploy/ |
Dockerfile, deploy.sh, and related deployment assets. |
eval/ |
Permanent eval CLI test fixtures. |
AGENTS.md |
Always-on contributor guidance, guardrails, and known codebase pitfalls. |
.agents/skills/ |
Repeatable workflow skills (audit, deployment, maintenance, etc.). |
Development setup
Section titled “Development setup”Requirements:
- Node.js
>= 22for the runtime (better-sqlite313 requires it; the deployment image uses Node 24 LTS userspace). - Node.js
>= 23.6fordocs:build/docs:check; the source-backed config validator relies on native TypeScript stripping. The docs CI job uses Node 24. - pnpm.
# From the repository rootpnpm installpnpm buildTest and validation matrix
Section titled “Test and validation matrix”After the final edit, run every applicable gate before proposing a change and
confirm it discovers the expected files or tests. On Linux/CI, pnpm run ci is the
final runtime gate; on Windows PowerShell invoke the Node binaries directly.
| Step | Linux/CI | Windows PowerShell |
|---|---|---|
| ESLint | pnpm lint |
node node_modules/eslint/bin/eslint.js . --max-warnings=0 |
| Typecheck | pnpm typecheck |
node node_modules/typescript/bin/tsc -b tsconfig.json --pretty false |
| Unit tests | pnpm test |
node node_modules/vitest/vitest.mjs run --coverage |
| Markdown lint | pnpm markdownlint |
node node_modules/markdownlint-cli2/markdownlint-cli2-bin.mjs |
| Build | pnpm build |
cmd /c "pnpm build" |
| Eval fixture validation | pnpm eval:validate (after build) |
node packages/cli/dist/index.js eval --validate-only |
| Docs build | pnpm docs:build |
pnpm docs:build |
pnpm eval:validate runs aicr eval --validate-only, which checks eval/*.json
shape and expected-problem contracts only — no LLM, no config secrets. A full
aicr eval run loads config and calls the LLM, so keep it as a separate
environment-specific benchmark job.
Changes that affect config shape, agent adapters, MCP tool contracts, output
rendering, deployment behavior, or public workflow must update the matching
docs, example/config.yaml, and example/README.md in the same change.
Local service integration tests
Section titled “Local service integration tests”These tests can use disposable local services without production credentials:
| Variable | Requirement and coverage |
|---|---|
AICR_SVN_TEST_EXECUTABLE |
Absolute path to svn, with svnadmin and svnserve in the same directory. Enables real repository metadata tests and a post-commit hook → authenticated HTTP → SQLite scheduling test. |
AICR_REDIS_TEST_URL |
URL of a local test Redis. Enables automatic-commit storage and model-catalog persistence tests; the catalog test uses its own random key prefix. |
AICR_GITEA_TEST_URL / AICR_GITEA_TEST_TOKEN |
Disposable loopback HTTP Gitea and its temporary admin token. Tests create private repositories and users, publish issues, then read persisted assignees. Both unset means skipped; incomplete configuration fails. |
Set the variables before running the test command above. Without them, the
corresponding integration tests are skipped. SVN fixtures live under build/tmp/
and the hook test stops its own daemon. These tests do not call an LLM or publish
reviews to remote systems. Deployment-specific authentication and networking
still need separate acceptance.
For Gitea, run this from the checkout root in Linux or WSL Debian with rootless Podman, curl, node and timeout available:
bash tests/services/with-gitea.sh \ pnpm exec vitest run packages/outputs/test/gitea-assignment-live.test.ts --maxWorkers=1The wrapper pins Gitea 1.25.4 with SQLite, limits it to 1 CPU / 512 MiB and
binds a random loopback port. It uses a fresh directory beneath
~/workspace/github/atframework (override with AICR_ACCEPTANCE_ROOT), supplies
the two fixture variables, and removes its container, data and newly downloaded
image on exit. The service also has a 900-second lifetime limit. Logs stay under
build/logs/gitea-*. After interruption, verify owned resources were removed;
SIGKILL cannot run directory cleanup. These results cover the pinned Gitea version.
packages/core/test/config-examples.test.ts always validates the deployment
config and configuration YAML blocks across repository documentation, including
both locales. Malformed configuration blocks fail instead of being skipped.
The SVN wrapper uses a pinned Debian image and Subversion 1.14.5-3, with
1 CPU / 256 MiB, a random loopback port and the same 900-second cleanup policy.
It provides AICR_SVN_TEST_URL; the test process needs svn on PATH.
The tests check auxiliary repository content, diff and failure cleanup, then
run analysis and publication through the orchestrator with a deterministic model fixture.
bash tests/services/with-svn.sh \ pnpm exec vitest run packages/vcs/test/svn-context-live.test.ts \ packages/server/test/svn-analysis-live.test.ts --maxWorkers=1For PostgreSQL SCRAM/TLS/roles, Redis TLS/ACL/AOF and SVN HTTPS/authz/hooks,
run the deployment fixture. It checks persisted data after a container restart,
uses random loopback ports and shares 1 CPU / 512 MiB across the services.
It removes its container, volume and temporary directory on exit. Debian package
versions are recorded in build/logs/deployment-versions.log.
bash tests/services/with-deployment-services.shAn optional child command receives AICR_PG_TEST_URL, AICR_REDIS_TEST_URL,
AICR_REDIS_OOM_TEST_URL and NODE_EXTRA_CA_CERTS. Certificate verification stays
enabled; OOM injection uses a separate Redis process. Serialize suites sharing PG:
bash tests/services/with-deployment-services.sh \ pnpm exec vitest run --coverage --maxWorkers=1Opt-in real accounts
Section titled “Opt-in real accounts”Real-account tests skip when their variable group is absent; incomplete groups fail. They never read a credential file automatically. Use a test group/account.
| Required variables | Test and optional settings |
|---|---|
AICR_FEISHU_TEST_APP_ID, AICR_FEISHU_TEST_APP_SECRET, AICR_FEISHU_TEST_RECEIVE_ID |
packages/outputs/test/feishu-app-live.test.ts: read members/profiles, send one card and recall it. AICR_FEISHU_TEST_DIRECTORY_CHAT_ID selects another source group; AICR_FEISHU_TEST_MENTION_OPEN_ID opts into notifying one approved member. |
AICR_ZHIPU_TEST_BASE_URL, AICR_ZHIPU_TEST_API_KEY |
packages/llm/test/providers-live.test.ts: glm-5.3-flash; AICR_ZHIPU_TEST_KIND selects openai_compatible (default) or anthropic. |
AICR_KIMI_TEST_BASE_URL, AICR_KIMI_TEST_API_KEY |
Same test, kimi-for-coding; AICR_KIMI_TEST_KIND selects the protocol. |
For anthropic, the base URL is the protocol root without /v1.
LLM cases make one request per enabled provider, with a 60-second deadline,
256 output tokens, thinking disabled and no automatic retries. They check an
answer and usage, not review quality or billing. Logs contain counts and usage,
never credentials, profiles or raw provider errors. Feishu recall failures fail
the run; after a forced interruption, check the test group for a remaining card.
If you maintain development/secret/secret.yaml, the explicit local helper
requires yq v4 and reads only the selected fields into the test child’s environment:
node tests/services/with-local-secrets.mjs feishunode tests/services/with-local-secrets.mjs zhipunode tests/services/with-local-secrets.mjs kiminode tests/services/with-local-secrets.mjs zhipu anthropicnode tests/services/with-local-secrets.mjs kimi anthropicThe field groups are .channel.feishu_app.{app_id,app_secret,receive_id},
.llm.provider.zhipu.{baseURL,token} and
.llm.provider.kimi_coding_backup.{baseURL,token}. CI supplies environment
variables directly. Keep these calls separate from routine coverage runs.
Adding a package
Section titled “Adding a package”- Create the package directory under
packages/<name>/with its ownpackage.json,tsconfig.json,src/, andtest/. - Add the package to
pnpm-workspace.yaml(the workspace already globspackages/*, so this is usually automatic). - Add a TypeScript project reference from the root
tsconfig.jsonand from any package that consumes it; add a reference back from the new package’stsconfig.jsonto its dependencies. - Add at least a
test/index.test.tsso the package has a test surface, even if it only exports a constant. - Add the new
onlyBuiltDependenciesentry topnpm-workspace.yamlif the package introduces a native module (pnpm 10 gates native builds behindonlyBuiltDependencies).
Adding or changing a config field
Section titled “Adding or changing a config field”The Zod schema in packages/core/src/config.ts is the source of truth.
- Update the schema (and any
superRefinecross-field validation). - Add or update a test in
packages/core/test/config.test.ts. - Update
example/config.yamlwith a commented example. - Update the relevant narrative page under
docs/site/src/content/docs/.../configuration/and the field table in Configuration fields — in both locales.pnpm docs:buildfails if the table misses a schema field, documents an invented field, or the two locales drift apart, so the gate tells you what is missing. - If the field changes runtime behavior, update
example/README.mdand the matching topical doc.
Workspace config files cannot write system-level fields; respect the
cache / defaults / instances three-part shape and the
global → workspace-default → workspace-instance override order.
Adding an output channel
Section titled “Adding an output channel”- Implement the dispatcher in
packages/outputs/src/and register it in the output registry. The channelkindis a free-form string constrained by the registry (not a closed enum). - Add built-in Handlebars templates for the problem and summary variants under the template engine.
- Add tests, including the IM-markdown transformer if the channel is an IM
bot (table regexes must not use the
gflag with.test()). - Document the channel in Output channels and add its fields to Configuration fields.
- Update
example/config.yamlwith a commented example.
See Output channels for the problem schema, summary schema, channel mapping, and the no-problems policy that every channel must respect.
Maintaining the docs site
Section titled “Maintaining the docs site”The docs site is bilingual (English under .../en/, 简体中文 under
.../zh-cn/). Every user-facing page exists in both locales; keep config
keys, commands, paths, field names, and enum values identical across locales.
- Build and validate locally with
pnpm docs:build. The build runs six validation gates: the public/internal boundary (pages undersrc/content/docs/must not reference the internal AI/roadmap documentation tree or carry migration-source maintenance notes), config-field coverage of the Zod schema, CLI command/flag consistency withpackages/cli, internal link/anchor resolution including sidebar coverage, SEO metadata (every page needs a non-emptytitle/descriptionwithin length bounds, plus the committedrobots.txtand 1200x630og-image.png), and bilingual consistency (same page set in both locales, identical code-fence counts and machine tokens — config keys, env vars, flags, paths — and no banned filler words). - Sidebar slugs omit the
indexsegment (e.g.troubleshooting/index.mdhas slugtroubleshooting). Frontmattertemplateonly acceptsdocorsplash; Starlightsocialis an array of link items. - Content pages use
.md. The two landing pages (en/index.mdx,zh-cn/index.mdx) use.mdxso they can render Starlight components (hero frontmatter plusCard,CardGrid,LinkCard,Steps,Aside). MDX is provided by Starlight with no extra integration; components never render in plain.md. The public-content validator scans both.mdand.mdx. - Cross-links use locale-prefixed paths (
/en/...,/zh-cn/...).
When you change a config shape, output contract, or runtime behavior, update both locales’ relevant pages in the same change.
Workflow rules
Section titled “Workflow rules”- Keep edits minimal and surgical; do not weaken lint, typecheck, test, or markdown gates to land a change.
- All temporary task artifacts (scratch scripts, debug logs, one-off reports,
benchmark output) go under
build/, never in the repository root,eval/, or a package directory. - Public/shared modules (
packages/cli/src,ReviewEvent, template context) must stay platform-neutral — import canonical schemas from@aicr/coreand keep provider/channel-specific names inside config contracts, docs, tests, and platform-specific adapters.
For the full, always-on contributor rules — including the numbered list of
known codebase pitfalls to avoid reintroducing — read AGENTS.md at the
repository root.