Thank you for your interest in contributing to JoinCode! This document describes how to set up your development environment and the conventions we follow.
By participating, you agree to uphold our Code of Conduct.
| Requirement | Version |
|---|---|
| .NET SDK | 10.0.301+ |
| OS | Windows / Linux / macOS |
| Git | 2.30+ |
# Clone the repository
git clone https://github.com/JoinCode/JoinCode.git
cd JoinCode
# Restore and build (Debug, incremental)
dotnet build Generators.slnx -c Debug
dotnet build Foundation.slnx -c Debug
dotnet build Infrastructure.slnx -c Debug
dotnet build Core.slnx -c Debug
dotnet build Services.slnx -c Debug
dotnet build Composition.slnx -c Debug
dotnet build App.slnx -c Debug
# Or use the build script
.\build.ps1 -Mode Fast -SkipTests -Configuration DebugThe build output is placed in artifacts/bin/. The main executable is
artifacts/bin/JoinCode/Debug/net10.0/jcc.exe.
JoinCode uses a seven-layer solution isolation architecture. Each layer is
an independent .slnx that depends only on lower layers:
① Generators → Source generators (netstandard2.0)
② Foundation → Abstractions, Structura, Transport.Contracts
③ Infrastructure → Infrastructure, Transport.Impl
④ Core → ai/ + execution/ + safety/ + search/
⑤ Services → Mcp, Dream, Eyes, Bridge
⑥ Composition → Composition, Clock
⑦ App → JoinCode.exe, Sdk, tests
Build order matters. Always build lower layers before upper layers. See ADR 0001 for the rationale.
git checkout main
git pull --rebase origin main
git checkout -b w1 # or w2, w3, ... (task branches)Follow the progressive development approach (ADR 0007):
- Complete one feature at a time
- Compile after each change
- Run relevant unit tests
- Commit after each green test
# Build only the affected .csproj (fastest)
dotnet build core/ai/Llm/src/Llm.csproj -c Debug
# Run unit tests for a specific component
dotnet test tests/Unit/Hands.Tests/Hands.Tests.csproj -c Debug --filter "Category!=Integration"
# Full build + test (before creating a PR)
.\build.ps1 -Mode FastWe follow Conventional Commits:
<type>: <description> | 决策: [optional decision note]
Types: feat, fix, refactor, docs, test, chore
git add -A
git commit -m "feat: add tool search capability" -m "决策: query MCP memory first, then internet"Commit rules:
- Do not include branch names (w1/w2/feature-xxx) in the message
- Do not include PR/Issue numbers (GitHub auto-links them)
- Unit tests must pass before committing
- No
$, backticks, or triple quotes in commit messages
# Sync with latest main first
git fetch origin main
git merge origin/main
# Push and create PR
git push -u origin w1
gh pr create --base main --head w1 --title "feat: add tool search capability" --body "..."
gh pr merge <number> --auto --squashPRs trigger full CI (build + unit tests + integration tests + E2E + AOT). CI must pass before auto-merge.
GlobalUsings.cs: Allusingdirectives go inGlobalUsings.cs, never in individual.csfiles- Nullable reference types: Enabled globally
TreatWarningsAsErrors: Enabled — zero warnings allowed- XML doc comments: Required on all public members; never delete them
- No comments in code unless explicitly requested
- PascalCase for public members, types, and namespaces
- camelCase for local variables and parameters
_camelCasefor private fields
| Rule | Reference |
|---|---|
| Seven-layer slnx isolation | ADR 0001 |
| NativeAOT, no Microsoft AI packages | ADR 0002 |
| Rebase over merge | ADR 0003 |
| TDD double-layer | ADR 0006 |
Archive to .xxx/, never delete |
ADR 0008 |
| Pass interfaces, not properties | ADR 0016 |
| Treat warnings as errors | ADR 0027 |
| Scenario | Use |
|---|---|
| Lookup (unordered) | Dictionary<K,V> / HashSet<T> |
| Hardcoded ordered (e.g., enum map) | SortedList<K,V> |
| High-frequency insert + ordered | SortedDictionary<K,V> |
| AOT immutable lookup | FrozenSet<T> / FrozenDictionary<K,V> |
| Never for lookup | List<T> / T[] (.Contains() is O(n)) |
Finite string constants must be enums with [EnumValue] attributes. Source
generators produce XxxConstants and XxxExtensions automatically. See
ADR 0019.
🔴 E2E red → 🔴 unit red → 🟢 unit green → 🔵 refactor → 🟢 E2E green
- Write a failing E2E test first (when external interface changes)
- Then write failing unit tests to localize the root cause
- Implement until unit tests pass
- Refactor
- Verify E2E passes
# All tests except integration
dotnet test App.slnx -c Release /p:SkipLocalPack=true --filter "Category!=Integration"
# Single test
dotnet test tests/Unit/Hands.Tests/Hands.Tests.csproj -c Debug --filter "FullyQualifiedName~YourTestClass"Each test should have a 10-second timeout. If the full test suite hangs, stop and fix it — never leave the suite in a broken state.
- Add the tool to an existing
ToolCategoryenum value (or add a new one with justification) - Use the
[McpTool]attribute + source generator pattern - Write tool description in Chinese (align with
ErrorRecoveryToolHandlers) - Update
ToolCategory[EnumValue]and rebuild with--no-incremental - See ADR 0014
When making a cross-module or architecturally significant decision:
- Create
docs/adr/NNNN-kebab-case-title.md(NNNN = next four-digit number) - Set status to
proposed - Implement the decision
- Change status to
acceptedafter verification - ADRs are immutable — only the status field changes after acceptance
See docs/README.md for the template and conventions.
- See SUPPORT.md for support channels
- Read AGENTS.md for detailed engineering rules (Chinese)
- Check existing issues and discussions
Contributors are recognized in release notes and the GitHub Contributors page. Significant contributions may be acknowledged in the README.