From d961c6a1a7115cfed370f68d674f14c014383b18 Mon Sep 17 00:00:00 2001 From: "developerz-ai[bot]" <286432749+developerz-ai[bot]@users.noreply.github.com> Date: Fri, 18 Sep 2026 14:05:22 +0000 Subject: [PATCH] chore: establish the AI-first base for tesote/coding-challenge-quick Dz-Task-Id: tsk_a4773748e1d7b371892f56d5c269f462 --- .claude/agents/json-persistence.md | 28 ++++++++++ .claude/commands/feature.md | 47 +++++++++++++++++ .claude/commands/planx.md | 40 ++++++++++++++ .claude/skills/verify-change/SKILL.md | 41 +++++++++++++++ .codegraph/.gitignore | 5 ++ .dz/maintainer/maintainer.yml | 62 ++++++++++++++++++++++ .dz/maintainer/reviewer.md | 29 ++++++++++ .dz/onboarding/scorecard.md | 76 +++++++++++++++++++++++++++ .githooks/pre-commit | 7 +++ .github/workflows/ci.yml | 15 ++++++ .mcp.json | 35 ++++++++++++ AGENTS.md | 8 +++ CLAUDE.md | 40 ++++++++++++++ bin/check | 5 ++ bin/dev | 5 ++ bin/setup | 6 +++ docs/README.md | 11 ++++ 17 files changed, 460 insertions(+) create mode 100644 .claude/agents/json-persistence.md create mode 100644 .claude/commands/feature.md create mode 100644 .claude/commands/planx.md create mode 100644 .claude/skills/verify-change/SKILL.md create mode 100644 .codegraph/.gitignore create mode 100644 .dz/maintainer/maintainer.yml create mode 100644 .dz/maintainer/reviewer.md create mode 100644 .dz/onboarding/scorecard.md create mode 100755 .githooks/pre-commit create mode 100644 .github/workflows/ci.yml create mode 100644 .mcp.json create mode 100644 AGENTS.md create mode 100644 CLAUDE.md create mode 100755 bin/check create mode 100755 bin/dev create mode 100755 bin/setup create mode 100644 docs/README.md diff --git a/.claude/agents/json-persistence.md b/.claude/agents/json-persistence.md new file mode 100644 index 0000000..6889232 --- /dev/null +++ b/.claude/agents/json-persistence.md @@ -0,0 +1,28 @@ +--- +name: json-persistence +description: Owns person JSON persistence in lib/person_repo.rb and tmp/persons.json. Use for save, load, JSON, serialization, file storage, uuid. Do NOT use for spec writing or app.rb changes. +--- + +> Specialist definition — drafted by dz-runner setup mode from this repo, then owned by its maintainers. Correct it in place; setup never overwrites a definition that exists. + +## Identity + +You are an elite software engineer and the pilot of this task: own the outcome, decide inside your authority, optimise for the codebase's next year. Your duty: make the change in your brief and prove it with the repo's own checks. + +- Weigh long-term cost: maintainability, the next person debugging it, one-way doors (stored shapes, public contracts). +- Say "I don't know — checking", then check; never guess. +- Escalate only a true blocker: a destructive or irreversible action, auth, data integrity, a principle. +- Autonomy runs inside the leashes: always disclose you are a bot; everything is audited; policy and merge gates are machines, never you; never reply to another bot. +- You may be any model on any provider key: never name a model or assume vendor behaviour. +- Typed tools first: prefer the audited tools you were given over a raw command; batch independent calls, serialize only one that needs a prior result. +- Scale effort to the task: a one-line fix is not a research project; a migration is not one shot. +- Read the code before changing it. Reproduce a bug with a failing test first, then fix the cause. +- Make the smallest correct change. Done means a tool result in this run proves it, not a diff written. + +Implement `save` inside `Person::PersonRepo` in `lib/person_repo.rb` following the code comment's contract: write `tmp/persons.json` as `{ "persons": [ { "id": , "full_name": ..., "age": ... } ] }`. +- Read the existing file, append the person, rewrite it — never overwrite blindly. +- Create `tmp/` with `require 'fileutils'` + `FileUtils.mkdir_p('tmp')` if it does not exist. +- Generate ids with `require 'securerandom'` (`SecureRandom.uuid`) so they persist across runs. +- Keep `PersonRepo` a nested class of `Person` — the spec resolves it as `PersonRepo` via spec_helper. +- Store flat hashes (id/full_name/age), not marshalled objects, so the file stays human-readable JSON. +- Prove with `bundle exec rspec` and inspect `tmp/persons.json` after `ruby app.rb`. diff --git a/.claude/commands/feature.md b/.claude/commands/feature.md new file mode 100644 index 0000000..611af29 --- /dev/null +++ b/.claude/commands/feature.md @@ -0,0 +1,47 @@ +--- +description: Take one change in tesote/coding-challenge-quick from idea to merged — explore, build, prove it with bin/check, open one pull request, watch it land. Use for feature, implement, build it, ship this, fix and merge. +argument-hint: +allowed-tools: Read, Write, Edit, Glob, Grep, Bash, Task +--- + +# /feature — idea to merged in tesote/coding-challenge-quick + +> Workflow command — established by dz-runner setup mode. Correct it in place; +> setup never overwrites a command that exists. + +**Done means MERGED.** A green local gate is not done, and an open pull request is not +done. Report what you verified, never what you assume happened. + +## Goal + +$ARGUMENTS + +## Steps + +1. Restate the goal in one line. If it names a URL or an issue, read it first. +2. **Distrust the paperwork.** Check any plan, status file or design doc against the code + and `git log` before working from it — trackers rot in both directions. Say plainly + which claims you falsified, and fix the doc in the same change. +3. Explore read-only, in parallel, into a ranked worklist of PR-sized batches. +4. Build primitive-first: one reusable primitive with its first real caller. No + abstraction before a consumer. +5. **Prove it.** `bin/setup` after any dependency change, then `bin/check`, which here is + `bundle exec rspec`. + Red is not a pull request: fix the cause, never silence the gate. Add the failing test + first — a green run that never went red proves nothing. +6. **One pull request in flight, start to finish.** Open it, watch it to merged, run + `git fetch`, and only then start the next. Each merge rebases the base under the + other, and green-when-opened is not green-to-merge. +7. Re-check the original symptom after the merge, and leave the issues and docs true. + +## Fan-out rules — restate these to every agent you spawn + +- **One checkout. No git worktrees.** Work would land in a directory nobody is looking at, + and each tree costs its own install and its own test database. Disjoint file sets are a + scheduling problem you can see; isolation is one you cannot. +- **Exclusive file sets.** Two agents never write one file, and a rename or a signature + change pulls every caller into the same set. +- **No git in a spawned agent** — no add, commit, branch, stash or push. You own git, and + you touch it only once every agent has returned. +- **Scope each agent to the files it edited.** The repo-wide gate is yours, run once, at + the end. diff --git a/.claude/commands/planx.md b/.claude/commands/planx.md new file mode 100644 index 0000000..7e1a22d --- /dev/null +++ b/.claude/commands/planx.md @@ -0,0 +1,40 @@ +--- +description: Write a self-contained implementation plan under docs/plans/ for another agent to execute. Use for plan, planx, design the work, break this down. +argument-hint: +allowed-tools: Read, Glob, Grep, Bash, Write +--- + +# /planx — plan a piece of work in tesote/coding-challenge-quick + +> Workflow command — established by dz-runner setup mode. Correct it in place; +> setup never overwrites a command that exists. + +**Plan only.** No implementation, no edits outside the plan directory. A plan that lives +only in a session's context dies with the session; on disk it is reviewable, correctable, +and restartable by whoever picks it up. + +## Goal + +$ARGUMENTS + +## Steps + +1. Resolve the directory: `date +%Y %m %d`, then the highest existing `1NN-*` under + `docs/plans///
/` plus 1, else `101`. Slug kebab-case, 5 words max. +2. Explore read-only first — find the patterns to follow and the files to change. +3. Write `overview.md`: goal (1-2 sentences), context (only the facts the executor needs, + as `path:line`), the slice files in execution order, done-when, risks. +4. Write one `-.md` per separable AREA OF WORK — `01-data-model.md`, + `02-api.md`, `03-frontend.md`. Each carries: what it depends on, files to change + (`path:line` — what, why), ordered steps, the tests to add, done-when. +5. Write `status.yml`: `plan`, `title`, `status`, `owner`, `percent`, `current_focus`, + `slices`, `evidence` (commits/PRs), `last_updated`. + +## Rules + +- **Reference, never restate.** Cite `path:line`; pasted code is stale the day it is pasted. +- **Self-contained.** The executor reads `overview.md`, its own slice, and what those cite. +- **Split by area of work, never by chapter.** A slice boundary is the file-set boundary the + build fans out on, and that file set is one pull request. +- **`status.yml` is the only tracker.** No checkboxes in a `.md` — two trackers disagree. +- **Skip the plan for a one-file fix.** Writing it costs more than the change. diff --git a/.claude/skills/verify-change/SKILL.md b/.claude/skills/verify-change/SKILL.md new file mode 100644 index 0000000..26c8216 --- /dev/null +++ b/.claude/skills/verify-change/SKILL.md @@ -0,0 +1,41 @@ +--- +description: Prove a change in tesote/coding-challenge-quick before committing or opening a pull request — the repo's own install, run and check commands. Use before every commit. +--- + +# Verify a change in tesote/coding-challenge-quick + +> House skill — established by dz-runner setup mode from this repo’s own +> detected commands. Correct it in place; setup never overwrites a skill that exists. + +## The one gate + +`bin/check` is the gate. CI runs it and the pre-commit hook runs it, so a change that +passes it locally passes everywhere. + +| Step | Command | What it is | +|------|---------|------------| +| install | `bin/setup` | bundle install | +| run | `bin/dev` | ruby app.rb | +| prove | `bin/check` | bundle exec rspec | + +## Rules + +- Run every command from the repo root — the scripts `cd` there themselves. +- `bin/check` red is not a pull request. Fix the cause, never silence the gate. + +## Exit codes `bin/check` must use + +A coding box runs this gate before it commits, and it has to tell a FAILING change +from a gate it could not run — otherwise correct work is thrown away as if it were +red. Exit 1 cannot carry that difference, so one code is reserved: + +| Exit | Means | What the platform does | +|------|-------|------------------------| +| 0 | the change passes | commit it | +| 75 | a resource the gate NEEDS is unreachable (a database, a service, a credential) — it refused to grade | refuse the commit, and blame the environment, never the change | +| any other non-zero | the gate ran and the change is red | refuse the commit, and blame the change | + +- Guard preconditions FIRST and `exit 75` when one is unmet. `exit 1` there tells the + platform your change is broken when it is your environment that is. +- Changed a dependency? Re-run `bin/setup` before `bin/check`, or you are proving a stale tree. +- Add the test that fails first, then the fix — a green run that never went red proves nothing. diff --git a/.codegraph/.gitignore b/.codegraph/.gitignore new file mode 100644 index 0000000..d20c0fe --- /dev/null +++ b/.codegraph/.gitignore @@ -0,0 +1,5 @@ +# CodeGraph data files — local to each machine, not for committing. +# Ignore everything in .codegraph/ except this file itself, so transient +# files (the database, daemon.pid, sockets, logs) never show up in git. +* +!.gitignore diff --git a/.dz/maintainer/maintainer.yml b/.dz/maintainer/maintainer.yml new file mode 100644 index 0000000..20382d2 --- /dev/null +++ b/.dz/maintainer/maintainer.yml @@ -0,0 +1,62 @@ +# .dz/maintainer/maintainer.yml — how the developerz.ai maintainer agent may act on tesote/coding-challenge-quick. +# Proposed by dz-runner setup mode; MERGING this pull request is the opt-in. +# +# This is the AUTONOMOUS posture: PRs merge when the machine gates pass, and the bot +# qualifies issues and dispatches coding. Releases follow the `release:` block below. +# The keys below are the ones you tune by hand — every other knob falls back to the +# schema defaults. Full reference: https://developerz.ai/docs/maintainer-yml +version: 1 + +# Services this repo's own verify gate needs running before it can grade a diff. +# None detected — leave the array empty or declare the service this gate needs. +services: [] + +# handoff: coding dispatches to the developerz fleet (BYOVM). `fleet` needs no +# `webhook_ref`: the fleet claims each task directly, and no external webhook fires. +handoff: + mode: fleet + # autonomous posture: go straight to coding rather than asking the reporter first. + ask_reporter_first: false + +# pr: +# - `auto_merge: true` must be written out: the schema default does not count as +# opting in, and without this line the free and open-source tiers keep +# auto-merge off. +# - `wait_for_other_bots` lists the review bots the agent defers to; it never +# argues with another bot. +# - `require_review: true` — CI green alone is not enough; a PR nobody reviewed +# does not merge. +pr: + auto_merge: true + wait_for_other_bots: [coderabbit, copilot] + require_review: true + large_threshold_lines: 500 + +# release: derived from the detected mechanism. The comment below names which +# one was observed for this repo; the rendered line is the safe default either way. +release: + # No release mechanism was observed in the entries supplied; the agent is not cutting releases on this repo. + manager: none + channels: [] + +# review: the developerz.ai reviewer, alongside any review bot already installed. +# `force: true` runs it beside CodeRabbit rather than standing down; remove it once +# you have moved off the other bot. `request_changes: false` and `approval: false`: +# the first pass comments only — it never blocks a merge and never approves. +review: + enabled: true + force: true + depth: inline + request_changes: false + approval: false + +# triage: ask reporters for repro before dispatching coding — keeps the coding +# queue honest about which issues are dispatch-ready. +triage: + ask_for_repro: true + +# escalate: the schema defaults already cover these; stated so you can see what +# stays with a human. +escalate: + always: [hostile_tone, direct_mention] + ask_before_acting: [breaking_change, major_dep_bump, first_time_contributor, large_pr] diff --git a/.dz/maintainer/reviewer.md b/.dz/maintainer/reviewer.md new file mode 100644 index 0000000..3428fd2 --- /dev/null +++ b/.dz/maintainer/reviewer.md @@ -0,0 +1,29 @@ +# Reviewer instructions — tesote/coding-challenge-quick + +> Read by the developerz.ai reviewer at EVERY review of this repository, at the reviewed +> commit, as this repo's own layer of the reviewer's prompt — established by dz-runner setup mode. +> It refines the platform rules (a finding is cited or withheld; the bot always discloses) +> and never overrides them. Edit freely; keep it under 12,000 characters — past that it is cut. + +## Stack + +Ruby + Bundler + RSpec. The gate is `bin/check` (`bundle exec rspec`): a change that does not pass it is not proven, whatever its description says. + +## Review conventions +- Hold every change to the README's requirements: `Person` constructor (first name, last name, age), unique uuid `id`, `full_name`, `save` via `PersonRepo`. +- Verify data is written to `tmp/persons.json` in the documented shape and survives a reload — read the file, don't trust the spec alone. +- Require `# frozen_string_literal: true` at the top of every Ruby file. +- Expect `expect` syntax in specs (spec_helper disables monkey-patching and old `should`). +- Implementation belongs in `lib/person.rb` and `lib/person_repo.rb`; `app.rb` stays the demo entry point. + +## Gates that matter +- `bundle exec rspec` exits 0 — all specs green (they fail until the challenge is implemented; that is the baseline, not a broken gate). +- `ruby app.rb` runs to completion and writes `tmp/persons.json`. +- Person ids are UUIDs and identical before save and after reload. +- No new dependencies: Gemfile is rspec-only and that is intentional. + +## Never flag +- The intentionally empty/stub bodies the challenge ships (`PersonRepo#save`, the `# ...` markers, the skeleton specs). +- Missing linter, typecheck, CI config, or Rakefile — none exists in this repo by design. +- The `RACK_ENV` line in spec_helper (unused leftover, harmless) and the `vendor/` cache directory. +- One-file-per-class layout and absence of namespaces beyond `Person::PersonRepo`. diff --git a/.dz/onboarding/scorecard.md b/.dz/onboarding/scorecard.md new file mode 100644 index 0000000..4d44e12 --- /dev/null +++ b/.dz/onboarding/scorecard.md @@ -0,0 +1,76 @@ +# AI-first scorecard + +> Written by the developerz.ai maintainer agent — established by dz-runner setup mode. Each onboarding run rewrites this file whole; the latest run wins, and the same card is stored on the platform. + +- Run: `run_2ecbddb5a7ab4d5c9e4bcb006d489a18` +- Computed: 2026-09-18T14:05:21.897Z +- Proof: **unprovable** — `bin/check` did not run green at head on this box. Nothing in the repository’s own code was changed to force it; the excerpt below is the record. +- Score: 9 of 15 items ok + +## `policy` — ok + +- .dz/maintainer/maintainer.yml established +- .dz/maintainer/reviewer.md established + +## `brain` — ok + +- CLAUDE.md established + +## `roster` — ok + +- .claude/agents/json-persistence.md established + +## `skills` — ok + +- .claude/skills/verify-change/SKILL.md established + +## `scripts` — unprovable + +- bin/setup established +- bin/dev established +- bin/check established +- ./bin/setup at head: not_runnable, exit 127 (0.0s) + +## `wiring` — ok + +- .mcp.json established + +## `commands` — ok + +- .claude/commands/planx.md established +- .claude/commands/feature.md established + +## `access` — gap + +- asked for 0 credentials + +## `layout` — gap + +- migration to the house layout filed as tasks +- apps/coding-challenge-quick: 7 move(s) planned as one task + +## `smoke_test` — unprovable + +- spec/person_repo_spec.rb in the repository +- ./bin/check at head: not_runnable, exit 127 (0.0s) + +## `env_example` — gap + +- nothing recorded + +## `agents_md` — ok + +- AGENTS.md established + +## `per_workspace_brains` — ok + +- single-package repository: 0 workspaces +- CLAUDE.md established + +## `docs_readme` — ok + +- docs/README.md established + +## `gate_proved` — unprovable + +- ./bin/check at head: not_runnable, exit 127 (0.0s) diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 0000000..c3b9929 --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +# pre-commit gate — established by dz-runner setup mode. Runs the SAME +# `bin/check` CI runs, so a commit that would fail CI fails here first. +# ACTIVATE IT (once, per clone): git config core.hooksPath .githooks +set -euo pipefail +cd "$(dirname "$0")/.." +exec bin/check diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..9f2867f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,15 @@ +# CI green-gate — established by dz-runner setup mode. +# Runs the SAME `bin/check` a coding agent runs locally, so a green local run +# cannot land red here. Adjust the toolchain step below if the default runner +# image does not carry yours (e.g. actions/setup-node, oven-sh/setup-bun). +name: check + +on: [push, pull_request] + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: bin/setup + - run: bin/check diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 0000000..4bd7cb6 --- /dev/null +++ b/.mcp.json @@ -0,0 +1,35 @@ +{ + "mcpServers": { + "sentry-mcp": { + "type": "stdio", + "command": "bunx", + "args": [ + "@sentry/mcp-server" + ], + "env": { + "SENTRY_ACCESS_TOKEN": "${SENTRY_ACCESS_TOKEN}", + "SENTRY_HOST": "${SENTRY_HOST}" + } + }, + "codegraph": { + "type": "stdio", + "command": "codegraph", + "args": [ + "serve", + "--mcp" + ], + "env": { + "DO_NOT_TRACK": "1", + "CODEGRAPH_TELEMETRY": "0", + "CODEGRAPH_NO_UPDATE_CHECK": "1" + } + }, + "developerz": { + "type": "http", + "url": "https://mcp.developerz.ai/v1/mcp", + "headers": { + "Authorization": "Bearer ${DEVELOPERZ_API_KEY}" + } + } + } +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..2f3e473 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,8 @@ +# tesote/coding-challenge-quick + +> Pointer — established by dz-runner setup mode. Two names, ONE brain. + +This repository's brain is [`CLAUDE.md`](CLAUDE.md) — read it before +you touch anything here. `AGENTS.md` exists because some agents look for this name +first; it is a pointer, never a second copy, because two brains drift and the reader +cannot tell which one is current. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..02469ff --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,40 @@ +# tesote/coding-challenge-quick + +> Project brain — established by dz-runner setup mode from this repo, then owned by +> its maintainers. Correct it in place; setup never overwrites a brain that exists. + +Partially-implemented Ruby person-management coding challenge (Person/PersonRepo persisted to tmp/persons.json), tested with RSpec; no CI or linting present. + +## Stack + +Ruby + Bundler + RSpec + +Toolchain: `mise.toml` — `bin/setup` runs `mise install`; the box carries only bun. + +## Commands + +| Task | Command | +|------|---------| +| setup | `bin/setup` — bundle install | +| dev | `bin/dev` — ruby app.rb | +| check | `bin/check` — bundle exec rspec | + +## Structure + +- `app.rb` — entry point; builds four `Person` objects and calls `save` on each. Requires `lib/person` and `lib/person_repo`. +- `lib/` — `person.rb` (`Person`) and `person_repo.rb` (`Person::PersonRepo`, nested); these are the implementation targets and the only code the app loads. +- `spec/` — RSpec suite (`spec_helper.rb` requires `../lib/person_repo` then `../lib/person`); `person_spec.rb`, `person_repo_spec.rb`. +- `tmp/` — storage location for `tmp/persons.json` written by `PersonRepo` (must be created if missing). +- `vendor/` — Bundler gem cache; never edit. +- Dependency rule: `app.rb` → `lib/*`; `spec/spec_helper.rb` → `lib/*`; `Person#save` delegates to `PersonRepo`; `PersonRepo` owns all file I/O to `tmp/persons.json`. + +## Conventions + +- Every file starts with `# frozen_string_literal: true` — keep it. +- Test with RSpec only; expect syntax enforced by spec_helper (`config.expect_with :rspec`, monkey-patching disabled). +- Run the suite with `bundle exec rspec`; run the app with `ruby app.rb`. +- The suite is the README's success criteria: it and `ruby app.rb` must pass/run without errors after implementation. +- Persist person records to `tmp/persons.json` in the documented shape (`{ "persons": [ { "id": uuid, "full_name": ..., "age": ... } ] }`); create `tmp/` if missing. +- Use UUIDs for person ids; ids must persist across save/load. +- No linter or typechecker is configured — do not invent one; match the existing simple class style. +- Keep classes in `lib/`, plain requires via `require_relative`, no framework. diff --git a/bin/check b/bin/check new file mode 100755 index 0000000..3910ebf --- /dev/null +++ b/bin/check @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +# one-command check (typecheck + lint + test) — established by dz-runner setup mode. One command; adjust as the repo grows. +set -euo pipefail +cd "$(dirname "$0")/.." +bundle exec rspec diff --git a/bin/dev b/bin/dev new file mode 100755 index 0000000..2e61a17 --- /dev/null +++ b/bin/dev @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +# one-command dev — established by dz-runner setup mode. One command; adjust as the repo grows. +set -euo pipefail +cd "$(dirname "$0")/.." +ruby app.rb diff --git a/bin/setup b/bin/setup new file mode 100755 index 0000000..bb62b81 --- /dev/null +++ b/bin/setup @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +# one-command setup — established by dz-runner setup mode. One command; adjust as the repo grows. +set -euo pipefail +cd "$(dirname "$0")/.." +command -v mise >/dev/null && mise install +bundle install diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..61147f8 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,11 @@ +# tesote/coding-challenge-quick — docs + +> Index — established by dz-runner setup mode. Add a line when you add a page. + +| Where | What lives there | +|---|---| +| [`idea/`](idea/) | Why this repository is shaped the way it is: the product idea, the architecture, the principles a change is weighed against. *(not created yet)* | +| [`standards/`](standards/) | How work is done here: the rules a change follows, and for each rule the check that enforces it. *(not created yet)* | + +The brain itself is the repo root's [`CLAUDE.md`](../CLAUDE.md); the +commands are `bin/{setup,dev,check}`.