diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..35daed2 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,13 @@ +# Worked adapter examples + +Concrete, sanitized `.claude/gates.json` + `CLAUDE.md` pairs for real stacks, to copy from when you +[fill in your own adapter](../docs/GETTING_STARTED.md#step-3--fill-in-claudegatesjson-the-adapter--the-important-one). +Each subdirectory is one stack; the `gates.json` there is what you'd drop into your repo's `.claude/gates.json`. + +| Example | Stack | Highlights | +|---|---|---| +| [`ts-solidity-foundry/`](ts-solidity-foundry/) | TypeScript (pnpm workspace) **+** Solidity (Foundry) monorepo | Gates that span two toolchains; a module map mixing `packages/*` (TS) and `contracts/` (Foundry); mixed-stack `test_affected`; which artifacts to gitignore vs track. | + +> These are **references, not runnable projects** — they show the adapter shape and the decisions a mixed +> stack forces, not a buildable tree. Adapt the paths and commands to your repo, then verify each gate runs +> (`bash .claude/scripts/gate.sh build`, etc.). diff --git a/examples/ts-solidity-foundry/CLAUDE.md b/examples/ts-solidity-foundry/CLAUDE.md new file mode 100644 index 0000000..8952c02 --- /dev/null +++ b/examples/ts-solidity-foundry/CLAUDE.md @@ -0,0 +1,43 @@ +# acme-protocol + +> Worked example `CLAUDE.md` for the [`ts-solidity-foundry`](./) adapter. Sanitized; adapt to your repo. +> Keep this file lean — project-WIDE context only. Task detail belongs in the task prompt. + +## What this project is +On-chain lending protocol. Solidity contracts hold the logic; a TypeScript SDK wraps them and a web app +consumes the SDK. Users interact through the web app; integrators use the SDK. + +## Stack & layout +- Language / runtime: TypeScript (Node 20) + Solidity 0.8.x +- Package manager: pnpm (workspace) for TS; Foundry (`forge`) for contracts +- Key directories (mirror `.claude/gates.json` → `modules`): + - `packages/sdk/` — TS client SDK wrapping the contracts (consumes the generated ABI) + - `packages/web/` — front-end app, consumes `sdk` + - `contracts/` — Foundry project: `.sol` sources, `test/`, deploy scripts, `foundry.toml` + +## Conventions +- Code style / lint: eslint + prettier for TS; `forge fmt` for Solidity (checked in CI via `forge fmt --check`). +- Testing: vitest per TS package (`test` script); `forge test` for contracts. Tests live beside sources + (`*.test.ts`) and in `contracts/test/*.t.sol`. +- Definition of done: `build`, `lint`, `typecheck`, `test` all green; contract changes keep `forge coverage` + ≥ threshold; reviewers approve (security lens required for `contracts`). + +## Multi-agent orchestration +This repo is set up for orchestrated multi-agent development. See `docs/USAGE.md`. +- **Adapter:** `.claude/gates.json` — module map, gate commands, model routing. Keep it current. +- **Gates run via** `.claude/scripts/gate.sh ` and the hooks in `.claude/settings.json`. + +### Module boundaries (hard rule) +A worker assigned to a module MUST NOT edit files outside that module's `path`. +- The **ABI** that `sdk` consumes is a *build artifact* of `contracts` — regenerate it via `build`, never by + hand-editing across the boundary. If an SDK change needs a contract change, the orchestrator re-scopes it as + two coordinated sub-tasks (contracts first, then sdk), not one worker reaching across. +- `web` depends on `sdk`'s published types; same rule — cross-package changes are re-scoped, never reached. + +### Merge policy +`pr-per-agent` — base branch `main`. + +## Don'ts +- Don't put secrets (RPC URLs, deployer keys, mnemonics) in the repo — use `.env` (gitignored). +- Don't bypass the gates. +- Don't commit build artifacts — see the example README for the gitignore split. diff --git a/examples/ts-solidity-foundry/README.md b/examples/ts-solidity-foundry/README.md new file mode 100644 index 0000000..4efc1ab --- /dev/null +++ b/examples/ts-solidity-foundry/README.md @@ -0,0 +1,81 @@ +# TypeScript + Solidity (Foundry) monorepo — worked adapter + +A reference [`gates.json`](gates.json) + [`CLAUDE.md`](CLAUDE.md) for a **pnpm workspace + Foundry** monorepo: +several TS packages plus a `contracts/` Foundry project. It exists because the first real adaptation of this +template to a mixed stack hit a handful of stack-specific decisions a JS-only example doesn't surface. Those +decisions, and how this example resolves them: + +## 1. Gates that span two toolchains +Each gate runs the TS side, then the Foundry side, and fails if **either** fails: + +```jsonc +"build": "pnpm -r build && (cd contracts && forge build)", +"test": "pnpm -r test && (cd contracts && forge test)", +"lint": "pnpm -r lint && (cd contracts && forge fmt --check)", +``` + +- `&&` (not `;`) so a red TS build short-circuits before forge runs and the gate's exit code is honest. +- `(cd contracts && …)` in a **subshell** — forge must run inside its own project dir (where `foundry.toml` + lives), but the subshell keeps `gate.sh`'s working dir at the repo root for the next gate. +- `typecheck` is TS-only (Solidity's compile *is* its typecheck, already covered by `forge build`). +- `coverage` combines `pnpm -r coverage` with `forge coverage --report summary`; keep one + `coverage_threshold` that both must clear, or split into per-stack tickets if they diverge. + +## 2. A module map mixing TS packages and a Foundry dir +```jsonc +"modules": [ + { "name": "sdk", "path": "packages/sdk" }, + { "name": "web", "path": "packages/web" }, + { "name": "contracts", "path": "contracts" } +] +``` +Each `path` is a **non-overlapping worker boundary**. The subtlety in a mixed stack is the **generated ABI**: +`sdk` consumes an ABI produced from `contracts`. That ABI is a *build artifact*, not a shared source file — so: + +- A worker in `contracts` edits `.sol` and regenerates the ABI via `build`. +- A worker in `sdk` consumes the committed/generated ABI but **never edits `contracts`**. +- If a feature needs both (new contract method + SDK wrapper), the orchestrator splits it into two coordinated + sub-tasks (contracts → then sdk), rather than letting one worker cross the boundary. This keeps the isolation + guarantee that makes parallel workers safe. + +## 3. `test_affected` for this stack +`test_affected` runs on the `Stop` hook after every change, so it should be fast: + +```jsonc +"test_affected": "pnpm --filter \"...[origin/main]\" test && (cd contracts && forge test)" +``` + +- **TS side:** `pnpm --filter "...[origin/main]"` runs only packages changed since `origin/main` (plus their + dependents). This needs `origin/main` present in the worktree — a per-worktree setup hook should + `git fetch origin main` first, or the filter silently matches nothing. (See the `test_affected` guidance in + [`docs/GETTING_STARTED.md`](../../docs/GETTING_STARTED.md#choosing-test_affected-per-stack).) +- **Foundry side:** `forge` has **no** native since-base filter, so run the full `forge test`. Contract suites + are usually fast enough to run whole; if not, split by directory with `forge test --match-path`. +- If in doubt, `test_affected` = your full `test` is always a correct (if slower) default. + +## 4. Artifacts: gitignore vs track +```gitignore +# TS +node_modules/ +packages/*/dist/ + +# Foundry +contracts/out/ # compiled artifacts — regenerated by `forge build` +contracts/cache/ +contracts/broadcast/ # deploy tx logs — noise; keep only if you need an on-chain audit trail + +# Secrets +.env +``` +- **Ignore** compiled output (`out/`, `dist/`, `cache/`) — it's reproducible from `build`. +- **Track** the generated **ABI/types** that `sdk` imports *if* you want SDK builds to work without first + compiling contracts (common for CI speed and for downstream consumers). Otherwise generate it in `build` and + ignore it. Pick one and state it in `CLAUDE.md` so workers don't thrash. +- **Track** `foundry.toml`, `remappings.txt`, and the lockfiles (`pnpm-lock.yaml`); **never** track `.env`. + +## Using it +1. Copy [`gates.json`](gates.json) → your repo's `.claude/gates.json`; fix module `path`s and gate commands to + match your layout. +2. Copy [`CLAUDE.md`](CLAUDE.md) → your repo root; trim to your project. +3. Verify each gate actually runs: `bash .claude/scripts/gate.sh build` (and `lint`, `test`, …). +4. Follow [`docs/GETTING_STARTED.md`](../../docs/GETTING_STARTED.md) from Step 4 on. diff --git a/examples/ts-solidity-foundry/gates.json b/examples/ts-solidity-foundry/gates.json new file mode 100644 index 0000000..33b812c --- /dev/null +++ b/examples/ts-solidity-foundry/gates.json @@ -0,0 +1,66 @@ +{ + "_README": "WORKED EXAMPLE (not a live adapter) — a TypeScript (pnpm workspace) + Solidity (Foundry) monorepo. Copy this into YOUR repo's .claude/gates.json and adapt paths/commands. See examples/ts-solidity-foundry/README.md for the reasoning behind each choice. Empty string = gate skipped, not failed.", + + "project": { + "name": "acme-protocol", + "language": "typescript+solidity", + "packageManager": "pnpm" + }, + + "modules": [ + { + "name": "sdk", + "path": "packages/sdk", + "description": "TypeScript client SDK that wraps the contracts. Depends on the ABI produced by `contracts`, but a worker here edits only TS — never the .sol sources.", + "owner": "" + }, + { + "name": "web", + "path": "packages/web", + "description": "Front-end app (consumes `sdk`). TS/TSX only.", + "owner": "" + }, + { + "name": "contracts", + "path": "contracts", + "description": "Foundry project: Solidity sources, tests, and deploy scripts. A worker here edits .sol + foundry config only. Regenerating the ABI that `sdk` consumes is a build-time artifact, not a cross-module edit.", + "owner": "" + } + ], + + "gates": { + "_note": "Run from repo root. Each mixed-stack gate runs the TS side then the Foundry side; a failure in either fails the gate. `(cd contracts && ...)` keeps forge in its own project dir without a persistent chdir.", + "install": "pnpm install --frozen-lockfile && (cd contracts && forge install)", + "build": "pnpm -r build && (cd contracts && forge build)", + "lint": "pnpm -r lint && (cd contracts && forge fmt --check)", + "typecheck": "pnpm -r typecheck", + "test": "pnpm -r test && (cd contracts && forge test)", + "test_affected": "pnpm --filter \"...[origin/main]\" test && (cd contracts && forge test)", + "coverage": "pnpm -r coverage && (cd contracts && forge coverage --report summary)", + "coverage_threshold": 80, + "e2e": "", + "security": "(cd contracts && slither . || true)" + }, + + "review": { + "lenses": ["correctness", "tests", "security", "performance"], + "consensus": "all", + "_consensus_note": "Solidity handles value — keep the `security` lens on. 'all' = every lens must approve.", + "skills": [] + }, + + "budget": { + "orchestrator_model": "opus", + "worker_model": "sonnet", + "explorer_model": "haiku", + "reviewer_model": "opus", + "max_parallel_workers": 3, + "_note": "3 modules → up to 3 parallel workers. Drop to 2 if human review/merge is the bottleneck." + }, + + "merge": { + "policy": "pr-per-agent", + "_policy_options": "pr-per-agent | orchestrated-sequential-merge", + "baseBranch": "main" + } +}