When developerz.ai onboards a repository, it reads the marker files at the top
of the checkout, names the stack, and seeds three commands from that name:
bin/setup installs, bin/dev runs the app, bin/check proves a change. This
page lists the markers, the commands each stack is seeded with, what the setup
pass writes into your repository, and the exit codes bin/check can use to say
"I could not grade this" instead of "this change is broken".
Why the list exists, and what a repository outside it still gets, is on Your stack. The policy file that bounds what the agent may do has its own page: the maintainer.yml reference.
Stack detection
Detection reads the names of the files at the top level of the checkout. It runs nothing and fetches nothing. The first row that matches wins:
| Order | Stack | Marker |
|---|---|---|
| 1 | Ultimate | app.config.ts, plus an @ultimat3/* package in dependencies or devDependencies |
| 2 | Bun | bun.lock or bun.lockb |
| 3 | Node | package.json |
| 4 | Python | pyproject.toml, requirements.txt or setup.py |
| 5 | Go | go.mod |
| 6 | Rust | Cargo.toml |
| 7 | Ruby | Gemfile |
| Unknown | none of the above |
Ultimate needs both halves: app.config.ts alone is also the root config of
SolidStart and TanStack Start, and those repositories stay Bun or Node.
Ruby is last on purpose. A Gemfile is often auxiliary (Jekyll docs, fastlane
or danger beside a mobile or CI build), while package.json, pyproject.toml,
go.mod and Cargo.toml never are, so a Go repository with a docs Gemfile
stays Go. The same order means a Rails application that carries a
package.json is detected as Node, and its commands are corrected the way any
other stack's are (below).
Unknown, and commands your model writes
A repository outside every row is recorded as unknown, not pushed into the
nearest row. The table is a floor, not the answer: during onboarding your
account's own model reads the repository, runs its commands, and proposes all
three. That works for any stack, recognised or not. A proposal is refused
whole, and the floor kept, if any command is missing, runs past one line, or
carries something shaped like a credential.
An unknown repository with no accepted proposal gets placeholders that exit
1 until someone fills them in:
echo "TODO(setup): install dependencies"; exit 1
echo "TODO(setup): run the app"; exit 1
echo "TODO(setup): typecheck + lint + test"; exit 1
Seeded commands
| Stack | bin/setup |
bin/dev |
bin/check |
|---|---|---|---|
| Bun | bun install |
bun dev |
bun run typecheck && bun run lint && bun test |
| Node | npm ci |
npm run dev |
npm run lint && npm test |
| Python | python -m venv .venv && ./.venv/bin/pip install -r requirements.txt |
python -m app |
ruff check . && pytest |
| Go | go mod download |
go run ./... |
go vet ./... && go test ./... |
| Rust | cargo fetch |
cargo run |
cargo clippy -- -D warnings && cargo test |
| Ruby | bundle install |
bundle exec rackup |
bundle exec rubocop && bundle exec rake test |
| Ultimate | bun install |
bunx x dev |
bunx x build --target static && bunx x verify |
For every stack except Bun and Ultimate, the generated bin/setup first runs
command -v mise >/dev/null && mise install, so a toolchain version the
repository pins (mise.toml, .tool-versions, .ruby-version) is installed
without root before the setup command runs.
Monorepos. A workspace root (a pnpm-workspace.yaml or go.work file, a
workspaces field in package.json, or a [workspace] table in
Cargo.toml) widens bin/check for two stacks. Node becomes
npm run lint --workspaces --if-present && npm test --workspaces --if-present
and Rust becomes
cargo clippy --workspace -- -D warnings && cargo test --workspace. Bun, Go and
Ultimate already cover every member from the repository root.
When each script runs
bin/setupruns before any work, at the start of every session.bin/checkis the gate. A worker proves its own pull request with it, and deep review runs it over a diff. A repository with nobin/checkis graded with its stack'scheckfrom the table above (Ultimate apps ship their ownbin/check); anunknownrepository with nobin/checkhas no gate, so nothing is graded and nothing is graded red.bin/stopruns when a run ends, on every exit path. It is not part of the trio and gates nothing, but it must be idempotent, exit 0 when nothing is running, and never remove volumes or data. Whether or not you ship one, the runner also stops (never removes) any Compose container whose project directory is inside the checkout.
A repository that never wrote a bin/stop gets docker compose stop when a
Compose file (compose.yml, compose.yaml, docker-compose.yml or
docker-compose.yaml) sits at the root, and true otherwise. One that already
has bin/setup, bin/dev and bin/check but no bin/stop gets none: setup
never back-fills a teardown beside a gate that was already there.
What a setup pass writes
Setup never overwrites a file you wrote. Every file it creates is written only
if that path is absent, so your own scripts, agent files and config survive
verbatim. Two files are edited rather than created: .gitignore gets lines
appended, and .mcp.json gets the platform's MCP servers added under names it
does not already use (a malformed .mcp.json is left untouched). The one file
it replaces is its own onboarding scorecard, which each run rewrites.
What it may create:
CLAUDE.md, withAGENTS.mdas a pointer to it, and a specialist roster, skills and commands under.claude/agents/,.claude/skills/and.claude/commands/.bin/setup,bin/dev,bin/checkandbin/stop, from the table above or from your model's accepted proposal.- A proposed policy at
.dz/maintainer/maintainer.ymland a reviewer prompt at.dz/maintainer/reviewer.md. .github/workflows/ci.ymlrunningbin/check, only in a repository with no CI of its own. A repository that already has CI keeps it and gets a proposal to align it instead of a second pipeline..githooks/pre-commitrunning the samebin/check, only when no hook manager (husky, lefthook, pre-commit, simple-git-hooks) is already present.mise.toml, only when the repository pins a toolchain version somewhere and has no mise config yet..env.examplewith the names of the variables the pass learned about, never their values, and only when it learned some.docs/README.mdand an onboarding scorecard at.dz/onboarding/scorecard.md.
None of it is pushed to your default branch. It lands as one pull request from
the dz/onboarding branch, and merging that pull request is the opt-in.
A new project from nothing is a different path: a framework scaffold only runs
on a repository that holds nothing but README.md, LICENSE and .gitignore.
A .github/ directory counts as content, so a repository with only issue
templates is never scaffolded over.
Gate exit codes
bin/check can tell the platform it could not grade the change, and that is
never recorded as red. The exit code decides:
bin/check result |
Graded as |
|---|---|
| exit 0 | green |
| exit 75 | could not grade: a precondition is unmet |
| exit 126 or 127 | could not grade: the shell could not run the command (126 not executable, 127 not found) |
| exit 129 to 255, or killed by a signal | could not grade: the gate was killed |
| ran past the time limit | could not grade: timed out |
| any other exit, including 1, 2 and 128 | red |
The box can also decline to grade on its own account, when the gate hit a memory limit or the box was too loaded to start it. Those are never red either.
Exit 75: a precondition is unmet
Exit 75 is how your bin/check says a resource it needs (a database, Redis, a
credential) is unreachable from this box. On exit 75 the platform runs your
bin/setup once more and runs bin/check once more. If the second run grades,
that verdict stands; if it refuses again, the change is recorded as "could not
grade", never as a failing change. A repository that never exits 75 loses
nothing: its gate is graded exactly as before.
#!/usr/bin/env bash
set -euo pipefail
if ! pg_isready -q; then
echo "postgres is unreachable; refusing to grade" >&2
exit 75
fi
bun run typecheck && bun run lint && bun test
Why 75:
- It is
EX_TEMPFAILfromsysexits.h: "the request could not be handled right now, try later", which is exactly what an unreachable store is. - None of the tools in the seeded commands exits 75 on a failing test, lint or type error, so a real failure never reads as "could not grade".
- 126 and 127 belong to the shell and cannot carry a second meaning.
- Exit 2 is deliberately not in the contract:
tscexits 2 on real type errors, so 2 is red and stays red.
It is an exit code and not a message because exit 1 is ambiguous: the same script can exit 1 for "the store is unreachable, refusing to run" and for a failing test, and the platform never matches your script's output to tell them apart. Your wording can change without notice; the exit code is the contract.