docs/nanorepo-architecture.md
NanoClaw's core is intentionally minimal. Skills are how users extend it: adding channels, integrations, cross-platform support, or replacing internals entirely. Examples: add Telegram alongside WhatsApp, switch from Apple Container to Docker, add Gmail integration, add voice message transcription. Each skill modifies the actual codebase, adding channel handlers, updating the message router, changing container configuration, and adding dependencies, rather than working through a plugin API or runtime hooks.
The problem: users need to combine multiple modifications to a shared codebase, keep those modifications working across core updates, and do all of this without becoming git experts or losing their custom changes. A plugin system would be simpler but constrains what skills can do. Giving skills full codebase access means they can change anything, but that creates merge conflicts, update breakage, and state tracking challenges.
This architecture solves that by making skill application fully programmatic using standard git mechanics, with AI as a fallback for conflicts git can't resolve, and a shared resolution cache so most users never hit those conflicts at all. The result: users compose exactly the features they want, customizations survive core updates automatically, and the system is always recoverable.
Skills are self-contained, auditable packages applied via standard git merge mechanics. Claude Code orchestrates the process — running git commands, reading skill manifests, and stepping in only when git can't resolve a conflict. The system uses existing git features (merge-file, rerere, apply) rather than custom merge infrastructure.
Every operation follows this escalation:
git merge-file merges, git rerere replays cached resolutions, structured operations apply without merging. No AI. Handles the vast majority of cases.SKILL.md, .intent.md, and state.yaml to resolve conflicts git can't handle. Caches resolutions via git rerere so the same conflict never needs resolving twice.Important: A clean merge doesn't guarantee working code. Semantic conflicts can produce clean text merges that break at runtime. Tests run after every operation.
Before any operation, all affected files are copied to .nanoclaw/backup/. On success, backup is deleted. On failure, backup is restored. Works safely for users who don't use git.
.nanoclaw/base/ holds a clean copy of the core codebase. This is the single common ancestor for all three-way merges, only updated during core updates.
Source code where skills weave in logic. Merged via git merge-file against the shared base. Skills carry full modified files.
Files like package.json, docker-compose.yml, .env.example. Skills declare requirements in the manifest; the system applies them programmatically. Multiple skills' declarations are batched — dependencies merged, package.json written once, npm install run once.
structured:
npm_dependencies:
whatsapp-web.js: "^2.1.0"
env_additions:
- WHATSAPP_TOKEN
docker_compose_services:
whatsapp-redis:
image: redis:alpine
ports: ["6380:6379"]
Structured conflicts (version incompatibilities, port collisions) follow the same three-level resolution model.
A skill contains only the files it adds or modifies. Modified code files carry the full file (clean core + skill's changes), making git merge-file straightforward and auditable.
skills/add-whatsapp/
SKILL.md # What this skill does and why
manifest.yaml # Metadata, dependencies, structured ops
tests/whatsapp.test.ts # Integration tests
add/src/channels/whatsapp.ts # New files
modify/src/server.ts # Full modified file for merge
modify/src/server.ts.intent.md # Structured intent for conflict resolution
Each modified file has a .intent.md with structured headings: What this skill adds, Key sections, Invariants, and Must-keep sections. These give Claude Code specific guidance during conflict resolution.
Declares: skill metadata, core version compatibility, files added/modified, file operations, structured operations, skill relationships (conflicts, depends, tested_with), post-apply commands, and test command.
One skill, one happy path — a skill implements the reasonable default for 80% of users.
Customization is more patching. Apply the skill, then modify via tracked patches, direct editing, or additional layered skills. Custom modifications are recorded in state.yaml and replayable.
Skills layer via depends. Extension skills build on base skills (e.g., telegram-reactions depends on add-telegram).
Renames, deletes, and moves are declared in the manifest and run before code merges. When core renames a file, a path remap resolves skill references at apply time — skill packages are never mutated.
git merge-file)git rerere → Claude Code → Claude Code + user input)state.yaml.nanoclaw/resolutions/ ships pre-computed, verified conflict resolutions with hash enforcement — a cached resolution only applies if base, current, and skill input hashes match exactly. This means most users never encounter unresolved conflicts for common skill combinations.
git rerere requires unmerged index entries that git merge-file doesn't create. An adapter sets up the required index state after merge-file produces a conflict, enabling rerere caching. This requires the project to be a git repository; users without .git/ lose caching but not functionality.
.nanoclaw/state.yaml records: core version, all applied skills (with per-file hashes for base/skill/merged), structured operation outcomes, custom patches, and path remaps. This makes drift detection instant and replay deterministic.
Direct edits are detected via hash comparison before any operation. Users can record them as tracked patches, continue untracked, or abort. The three-level model can always recover coherent state from any starting point.
Most changes propagate automatically through three-way merge. Breaking changes require a migration skill — a regular skill that preserves the old behavior, authored against the new core. Migrations are declared in migrations.yaml and applied automatically during updates.
git apply --3way)The user sees no prompts during updates. To accept a new default later, they remove the migration skill.
Uninstall is replay without the skill: read state.yaml, remove the target skill, replay all remaining skills from clean base using the resolution cache. Backup for safety.
Flatten accumulated layers into a clean starting point. Updates base, regenerates diffs, clears old patches and stale cache entries. Trades individual skill history for simpler future merges.
Given state.yaml, reproduce the exact installation on a fresh machine with no AI (assuming cached resolutions). Apply skills in order, merge, apply custom patches, batch structured operations, run tests.
Each skill includes integration tests. Tests run always — after apply, after update, after uninstall, during replay, in CI. CI tests all official skills individually and pairwise combinations for skills sharing modified files or structured operations.