YotoShelf
Contribute

How It Works

The AGENTS.md contributor contract: stop conditions, blast radius limits, and the one-thing-at-a-time rule.

YotoShelf uses an explicit contributor contract documented in AGENTS.md. The rules keep changes safe, reviewable, and reversible, and they apply equally to human contributors and AI coding agents. What follows is that file, read from the repository when this page was built.

Stop conditions

If any of these are true, STOP immediately. Do not attempt another fix. Explain what happened and wait for the human.

  • Tests failed twice on the same task. You tried a fix, tests broke, you tried again, tests still break. STOP. git checkout -- . and explain.
  • You’re about to modify a 4th file. Your change touches 3 files already and you need a 4th. STOP and explain what you’re doing.
  • You’re modifying a test to make your code pass. STOP. Your code is wrong, not the test. Revert.
  • You’re not sure what a function does. STOP. Read it. Read its tests. Read its callers. Then proceed.
  • The build has been broken for more than 2 attempts. STOP. Revert everything: git checkout -- . and start over with a different approach.

The rules

These are non-negotiable. Violating them wastes human time.

1. Read before write

Before modifying ANY file:

  1. Read the entire file you’re about to change
  2. Read the test file for that package
  3. Run existing tests: go test ./internal/{pkg}/... -v
  4. Only then make your change
  5. Run tests again immediately after

2. Test before fix

Before fixing any bug:

  1. Write a test that demonstrates the bug
  2. Run the test — it MUST fail
  3. Fix the code
  4. Run the test — it MUST pass
  5. Run ALL tests: go test ./... -count=1 — nothing else broke

If you can’t write a failing test, you don’t understand the bug.

3. Revert on regression

After EVERY change, run: go test ./... -count=1

If ANY package that was previously passing now fails:

git checkout -- .

Revert first. Think second. Explain what you tried and why it broke. Do NOT try to fix the regression with another patch.

4. One thing at a time

Work on exactly ONE bug or feature per commit. No batching. No “while I’m here” changes.

  1. State what you’re fixing (one sentence)
  2. Write the failing test
  3. Make the minimal change
  4. Verify: go test ./... -count=1
  5. Commit
  6. Move to the next thing

If you find a second bug while fixing the first, NOTE it and move on.

5. Blast radius limit

Each commit touches at most 3 files. If your fix needs more than 3 files, STOP and explain what you’re doing. The human decides.

6. Protected files — do not modify without human approval

These files affect the entire system:

  • internal/api/helpers.go — shared helpers
  • internal/api/errors.go — error constants
  • internal/api/api.go — API struct + middleware
  • internal/server/server.go — server wiring
  • internal/db/migrations/ — database schema
  • sqlc.yaml — sqlc configuration
  • .gitlab-ci.yml — CI pipeline
  • AGENTS.md — this file

If you need to change a protected file, explain why and wait for approval.

7. Never modify tests to fix code

If your code change breaks an existing test, your code is wrong — not the test. Revert your change. The tests are the specification.

Exception: if a test is genuinely wrong (tests the wrong behavior), explain why before changing it.

8. Verify everything

After every change:

just check

This runs every local-runnable CI gate:

  • check:go — golangci-lint, go test ./..., go build, govulncheck, file-size limits (Go ≤400 / Svelte ≤250 LOC)
  • check:frontendnpm ci, npm run build, npm audit --omit=dev --audit-level=high
  • check:i18nscripts/check-i18n.sh (no hardcoded English strings)
  • sqlc drift: run just generate if you touched SQL or Go types; types.ts must not differ from HEAD
  • openapi drift: run just generate if you changed any huma operation; commit the updated types.ts

If just check passes locally, it passes in CI.

Prohibited patterns

  • No raw http.Error() — use huma errors or Err()
  • No raw SQL in handlers — use sqlc
  • No raw fetch() in frontend — use the shared client
  • No any types in TypeScript — use generated types
  • No hand-editing generated files (types.ts, db/gen/, openapi.yaml)

Read the whole contract before opening a merge request: AGENTS.md.