code-development
Takes a feature, bugfix, or patch from "I want X" to an open pull request, in three tasks
that run strictly in sequence — scope, implement, document — so no task steps on another.
This skill has real side effects: it creates branches, commits, pushes, and opens PRs. It
never touches the default branch directly and never commits code whose tests haven't
actually passed.
Everything repo-specific — how to run the tests, what CI enforces, which branch to cut
from, whether PRs open ready or draft, whether screenshots are expected — comes from
.claude/code-development.yml in the repo being worked on, written by
/code-development init. Without that file the skill rediscovers the same facts on every
single run and has no record of the answers you gave last time.
Hard rules, restated up front because they're the ones that erode mid-task:
- Never guess on direction. Any design decision with more than one viable approach is
a stop-and-ask: present the options with tradeoffs and wait. Research first — the repo's
own conventions, then current best practice — before proposing anything.
- Task gates are hard. Task 2 does not start until the Task 1 plan has the user's
explicit sign-off. Task 3 does not start until Task 2's tests are green and committed.
No look-ahead, no "while I'm here."
- No commit until local tests pass. Run them for real. If they can't run, there is no
commit — say why and stop. Never claim a command ran that didn't. This governs commits
that change code; setup mode's single config commit is the one exemption, and it reports
a pre-existing red suite rather than committing on top of a green one it invented.
- Never push directly to the default branch, or to whatever
branch.default names.
All work lands on a feature/, fix/, or — in setup mode only — chore/ branch, via a
PR.
- Smallest change, explicit staging. No unrelated edits, no renames or refactors the
task doesn't require, and never
git add -A — stage each file by name.
- Setup mode commits exactly one path,
.claude/code-development.yml, and authors
nothing else — no code, no tests, no docs, no workflow. It runs the repo's own commands
to validate them; it never stages what those runs leave behind. A development run commits
only what the signed-off plan named.
- The config is the contract; the repo is the truth. Where they disagree — a recorded
command that no longer exists, a default branch that was renamed — stop and say which
key is stale. Never improvise a substitute command to keep the run moving.
The two modes
Pick the mode before doing anything else, and say which one you're in.
| Invocation |
Mode |
Writes anything? |
/code-development init, "set up code-development", "configure code-development" |
Setup — discover the toolchain, validate it by running it, write .claude/code-development.yml, open a PR for it |
One file, on a branch, after confirmation |
/code-development, "develop a feature", "fix a bug", "implement X" |
Development run — Phase 0, then Tasks 1–3, then the PR |
A branch, commits, a push, a PR |
Plain /code-development in a repo with no config offers Setup first. Say that's what
you're offering rather than silently interviewing. If the user declines, run the Phase 0
discovery inline for this run only and say plainly that nothing is being recorded — the
next run will rediscover all of it.
Environment and tooling
- Needs
git, gh, and a checked-out repo — Claude Code only. Prefer a connected GitHub
MCP server if one is in this session's tools; otherwise gh. Check the tools actually
available in this session, not a registry of installable ones.
- Push access is required in both modes. Both end in a pushed branch and a PR. If
neither path is authenticated with write access, stop and say so before Task 1 rather
than after the code is written.
- The repo is whatever repo is checked out. Discover it; never assume a name.
- Task 3 captures screenshots with Playwright, which needs its browsers installed
(
npx playwright install chromium or the repo's own equivalent). A repo whose config
records screenshots.mode: none never needs it.
This skill is repo-agnostic, so the quality toolchain is discovered, not assumed. When
.claude/code-development.yml exists, that discovery already happened and its result is in
the file. When it doesn't, read, in this order, whatever exists:
.github/workflows/*.yml — what CI actually enforces: linters, test commands, scanners,
matrices. This is the ground truth for what the code must pass.
Makefile, package.json scripts, pyproject.toml, tox.ini, go.mod, Cargo.toml —
how to run those same checks locally.
- Linter and scanner configs:
.eslintrc*, ruff.toml, .golangci.yml,
sonar-project.properties, .pre-commit-config.yaml, codeql config.
If no CI workflows exist, that is a finding, not a free pass: in a development run,
offer to add a basic lint + test workflow (its own commit, or its own stacked PR if
non-trivial). Offer — never silently create it, and never skip local checks just because CI
wouldn't have caught the gap. Setup mode makes no such offer; it records the absence in the
config's ci block and points at a development run.
SonarQube and CodeQL generally cannot run locally. Handle that honestly: run everything that
can run (linters, the test suite, any local scanner the repo configures), write the code
to the standards those scanners enforce (Task 2 lists them), and let ci-pipeline catch the
remainder after the push. Never report a scan as passed locally when it did not run locally.
Setup mode — /code-development init
Takes a repo from nothing to a committed, validated config, so every later run starts
from recorded fact instead of rediscovery. Build it from what the repo actually has rather
than by interrogating the user about things you can read.
1. Ground it. Confirm push access, then discover the repo, the current branch, the
default branch, and whether the tree is clean:
git rev-parse --show-toplevel && git branch --show-current
git remote -v
git status --porcelain # must be empty — see below
gh repo view --json nameWithOwner,defaultBranchRef
Stop now if the tree is dirty. Setup's commit has to contain the config and nothing
else, and you cannot guarantee that starting from someone else's uncommitted work. Checking
this here rather than at commit time matters, because step 7 writes a file and from then on
"dirty" no longer distinguishes your change from theirs.
If gh repo view errors, don't work around it — step 3 covers why it can fail.
2. Check for an existing config at .claude/code-development.yml (or .yaml). If one
exists, show it and ask whether to update it or keep it — never overwrite a config the user
hasn't seen.
3. Check the prerequisites that actually block people, in this order, and stop
helpfully on each:
- Not a git repo → nothing here works. Say so and stop.
- No remote (
git remote -v is empty) → the skill ends in a push and a PR, so there is
nothing to push to. Say so, name git remote add origin <url> as the fix, and stop.
- No
gh/MCP write access → same reason. Stop rather than discovering it after the code
is written.
- No detectable way to run tests — no test target in a Makefile, no test script in
package.json, no pytest/tox/go test/cargo test reachable, nothing in CI that
runs one. Stop here. Say plainly that /code-development refuses to commit without a
real passing test run (hard rule 3), so a repo with no runnable test command has no
usable gate. List exactly what you looked for and didn't find. Do not write a test
harness, a CI workflow, or a placeholder command to get past this — building those is a
development run's job, under the Task 1 gate, not setup's.
4. Read CI first — it's the ground truth. List the workflows and read what they
actually run:
ls .github/workflows/
gh workflow list --repo <owner/repo> --all
Take the concrete step commands out of the YAML — npm run lint, pytest -q, make check
— rather than inferring them from the language. Those are what the code must pass.
5. Derive the local equivalents from Makefile, package.json, pyproject.toml,
tox.ini, go.mod, Cargo.toml, and the linter/scanner configs. Note every check CI runs
that has no local equivalent — SonarQube, CodeQL, a hosted scanner, a matrix leg that
only exists on CI. That gap goes in the config verbatim so it's visible on every run instead
of being rediscovered or forgotten.
6. Ask only what genuinely can't be read, offering the derived answer for each so the
user is correcting rather than composing:
- which command is the test command, when the repo offers several (
test, test:unit,
test:ci) — offer the one CI uses
- whether PRs open ready for review or as drafts
- whether this repo has a UI worth screenshotting at all — offer
none for a library, a
CLI, or a backend service with no rendered surface
- how the app comes up for a screenshot run, and at what URL
- which paths count as docs, and where images live — offer what the repo actually has
7. Write .claude/code-development.yml, show it back in full, and ask for confirmation.
8. Validate it by actually running the recorded commands, before committing anything. A
config claiming npm test works when it doesn't is worse than no config at all — it turns
Task 2's gate into a lie that only surfaces mid-implementation.
Say which commands you're about to run and get a go-ahead first: these are the repo's own
scripts, and a test target can have side effects. Then run each of commands.test,
commands.lint, and any typecheck/format/build you recorded, and read the result
carefully, because the two failure modes mean opposite things:
- The command doesn't exist, isn't executable, or errors on invocation (
command not found, an unknown npm script, a missing module) → the config is wrong. Fix the entry
and re-run. Never commit a config in this state.
- The command ran and the repo's own tests or lint failed → the config is right and the
repo is red. That's not a setup problem. Record the config as-is, say clearly which
command failed and that it's pre-existing, and note that
/code-development will refuse
to commit until it's green.
If you fix an entry here, show the corrected config again before going further — the
user confirmed the version you had in step 7, not the one you changed.
If the user declines to run them, the commands are unvalidated — say so in the report
and in the PR body. Never describe them as verified.
9. Commit it on a branch and open a PR. This is the only write setup mode makes, and it
stays narrow. Step 1 established the tree was clean, so the config from step 7 should be the
only tracked change — but a validation run can leave untracked artifacts (coverage/,
.pytest_cache/, a dist/). Never stage those. If any of them aren't gitignored, say so;
that's a finding for a development run, not something to sweep into this commit:
git fetch origin <default branch>
git checkout -b chore/code-development-config origin/<default branch> # branch off the
# default, not
# whatever is checked out
git add .claude/code-development.yml # explicit path, never -A
git commit -m "Add code-development config for the /code-development skill"
git push -u origin chore/code-development-config
gh pr create --fill --base <default branch>
Never commit anything else in that commit, and never push to the default branch.
10. Ask whether to merge it. Merge only on an explicit yes, and only after the PR's own
checks are in a state the user accepts. If they'd rather review it themselves, leave the PR
open and say that the next /code-development run won't see the config until it lands on
the branch being worked from.
11. Say which branch they're on now, and switch back to where they started unless
they're continuing straight into a development run from here.
12. Offer to continue into a development run. The Task 1 sign-off gate still stands
between that point and any code being written, so continuing is safe — but say so rather
than assuming.
Setup mode never edits code, adds a test, adds a CI workflow, or starts Task 1. It
discovers, validates, records, and stops.
The config
.claude/code-development.yml, in the repo being worked on:
version: 1
branch:
default: main # what feature branches are cut from
prefixes:
feature: feature/
fix: fix/
commands: # the local gate — all of these must pass before a commit
test: "npm test"
lint: "npm run lint"
typecheck: "npm run typecheck" # omit if the repo has none
format: "npm run format -- --check"
build: "npm run build" # omit if a build isn't part of the local gate
ci:
workflows: [".github/workflows/ci.yml"]
enforces: ["lint", "test", "typecheck", "codeql"]
local_gap: "CodeQL runs only in CI — nothing local reproduces it"
pr:
base: main # a stacked PR targets the stage below instead
ready: true # false → open as a draft
conventional_commits: true
template: ".github/pull_request_template.md" # omit if the repo has none
docs:
paths: ["README.md", "docs/**"]
images: "docs/images"
screenshots:
mode: playwright-local # playwright-local | playwright-mock | none
start: "npm run dev" # omit when mode is none
url: "http://localhost:5173"
viewport: "1280x800"
Required: version, branch.default, and a commands block containing at least test.
Everything else is optional, and every omission has exactly one defined behavior — never
improvise a different one:
| Omitted |
Behavior |
branch.prefixes |
feature/ and fix/ |
commands.lint |
No lint step in the Task 2 gate — say so, don't invent one |
commands.typecheck / format / build |
Not part of the gate |
ci |
Nothing is claimed about what CI enforces; the CI-vs-local gap goes unstated |
pr.base |
branch.default |
pr.ready |
Ready for review, not draft |
pr.template |
The Finish template below |
pr.conventional_commits |
True |
docs.paths |
Discover docs/, README.md, a wiki dir — whatever the repo has |
docs.images |
docs/images |
screenshots.viewport |
1280x800 |
screenshots entirely |
Treat as mode: playwright-local, discovering the app's start command in Task 3 exactly as an unconfigured repo would |
Phase 0 — Orient
Three steps, in order. Verify with real commands; assume nothing.
1. Ground it.
git rev-parse --show-toplevel && git branch --show-current
gh repo view --json nameWithOwner,defaultBranchRef
git status --porcelain # dirty tree → stop and ask; never sweep stray work into a feature
2. Load the config. Read .claude/code-development.yml (accept .yaml too). Three
outcomes — say which one you're in before doing anything else, and never silently degrade:
- Found — echo the resolved config back in one short block: the test and lint commands
that will gate the commit, the branch it'll cut from, whether the PR opens ready or draft,
the screenshot mode, and the recorded CI-vs-local gap. The user should be able to catch a
wrong value here, before any of it matters. Skip toolchain rediscovery — the file is
the answer. Skipping it isn't the same as trusting it blindly: rule 7 still holds, and a
recorded command that turns out not to exist gets caught at the point of use in Task 2,
where it's a stop rather than a substitution.
- Absent — say so and offer
/code-development init. If they take it, run Setup, then
come back here. If they decline, run the toolchain discovery inline for this run only and
say plainly that nothing is being recorded.
- Present but unparseable, or missing a required key — stop. Name the offending key.
Don't fall back to defaults for a required field, and don't improvise a config to keep the
run going.
branch.default is the branch feature work is cut from, which is usually — but not always —
the repo's GitHub default. A project that develops off develop records that, and rule 4
still forbids pushing directly to either. If the two differ, say so in the echo rather than
letting it surprise someone at PR time.
3. Check the branch you'll actually cut from is current, now that you know which one it
is:
git fetch origin
git rev-list --count <branch.default>..origin/<branch.default> # >0 → local is behind:
# say so and ask whether to
# pull before branching
Classify the request — feature, bugfix, or patch. All three run all three tasks. A
bugfix can change how something looks, and stale screenshots left in the docs after a fix
are a defect; Task 3 may conclude "no doc changes needed," but only after actually checking,
never by category ("it's just a bugfix").
Then state the toolchain: the CI checks, the local equivalents, and any gap between them —
read from the config, or discovered inline if there isn't one.
Task 1 — Scope and design
Goal: a written plan with zero open questions, signed off by the user.
Open by reminding the user this task is best run in plan mode (Shift+Tab twice in Claude
Code), so nothing gets edited while the design is still open.
- Restate the request in your own words — what the change is for, not just what it says.
A fix that clears the symptom but contradicts the intent is the wrong fix.
- Read before asking: the code in the affected area, related modules, existing tests,
existing docs, and recent PRs touching the same files. Repo conventions win over generic
best practice when they conflict.
- Research best practices for anything unfamiliar — web search the library, pattern, or
security guidance involved. Cite what was found; "best practice says" with no source is a
guess wearing a suit.
- Enumerate every open question and every design decision with more than one viable
approach. For each decision: the options, their tradeoffs, which you'd pick and why. Ask
them together in one message where possible, not as a drip-feed.
- Size it. A standard task is one PR a reviewer can hold in their head — one concern, a
handful of files. Bigger than that → propose a stacked-PR breakdown: stage 1 branches
off
branch.default, each later stage branches off the previous, each PR targets the
branch below it, landed bottom-up. Docs get their own stacked PR on top of the
implementation stage(s) when the change is large; on a standard-size task they ride the
same PR as separate commits. Name every branch in the plan.
- Write the plan: scope, out of scope, each design decision as resolved, files expected to
change, test plan, doc and screenshot impact, PR structure (single or stacked, with
branch names and bases).
Gate: post the plan and stop. Task 2 starts only on explicit sign-off — "approved",
"go", or an edit followed by a go. Silence is not sign-off. If implementation later reveals
the plan was wrong, this gate reopens; the plan is amended and re-approved, not quietly
outgrown.
Task 2 — Implement
Goal: the planned change, at quality, committed on a branch with green local tests.
- Branch:
<branch.prefixes.feature><slug> or <branch.prefixes.fix><slug> — feature/
and fix/ by default — cut from an up-to-date branch.default, or from the previous
stage's branch when stacked.
- Implement exactly the plan — the smallest change that fully delivers it. Anything that
turns out to need more than the plan said is a stop: reopen the Task 1 gate.
- Write to scanner standards even where the scanner can't run locally. The recurring
Sonar/CodeQL/lint findings to design out rather than patch up afterward:
- Validate and bound every external input. Parameterize queries. Never build shell
commands, SQL, or file paths by concatenating user input.
- Handle every error path explicitly — no swallowed exceptions, no bare
except/empty
catch, no ignored return values.
- No secrets in code, config, tests, or fixtures. Ever. Not even placeholders that look
real.
- No duplicated blocks, no dead code, no TODO standing in for an implementation. Keep
cognitive complexity down by extracting functions, not by nesting deeper.
- Release everything acquired — files, connections, locks. Prefer context managers,
defer, try-with-resources.
- No new dependency and no version pin the plan didn't approve.
- Tests: targeted tests for the changed area, per the plan's test plan. A bugfix's test
must fail on the pre-fix code — verify that by actually running it against the unfixed
code, don't assume it.
- Run the gate: every command in the config's
commands block — test, lint, and any
typecheck/format/build recorded — or, with no config, the equivalents discovered in
Phase 0. All green → commit. Anything red → fix it or stop and report. Committing red is
never in bounds, and neither is narrowing the test run until it passes. If a recorded
command isn't found, the config is stale: stop, name the key, and say what the repo has
instead. Don't substitute a command of your own choosing.
- Commits: Conventional Commits when
pr.conventional_commits is true (feat:, fix:,
test:, refactor: only when the plan called for one), one concern per commit,
implementation only — docs come in Task 3's own docs: commits. Stage files by name.
Gate: tests green, work committed, tree clean. Report the exact commands run and their
real output before moving on.
Task 3 — Document
Goal: the docs match the code that was just written, images included.
- Find the docs:
docs.paths from the config, else docs/, README.md, a wiki directory
— whatever the repo actually has. No docs at all → create or extend README.md. That
is the floor, not an option to skip.
- Write or update: what the change does, how to use it, config or flags added, breaking or
behavior changes. For a bugfix: check whether existing docs describe the old, broken
behavior, and correct them.
- Screenshots, whenever the change is user-visible and
screenshots.mode isn't none:
- Get the application running locally with
screenshots.start at screenshots.url —
or, with no config, discover how from the README, scripts, and compose files, the same
way the toolchain was discovered. If a real run isn't possible in this environment,
fall back to mocked data or component-level rendering under Playwright — and say
plainly which of the two produced each image.
- Capture with Playwright at
screenshots.viewport (1280×800 by default, unless the
repo's existing images establish otherwise), saved to docs.images (else where
existing images live, else docs/images/), named for the feature, not the date.
- Stale-image sweep, every run, bugfixes included: list the existing doc images that
touch the changed area and re-capture any the change visibly altered.
- If neither a local run nor a mock will render: capture nothing, claim nothing. List the
exact captures still needed in the PR body as follow-up. A fabricated or misattributed
screenshot is worse than a missing one.
screenshots.mode: none means this repo has nothing to screenshot. Say that's why
there are no images; don't treat it as a skipped step.
- Commit docs separately (
docs: commits) on the same branch — or on the docs stage's
branch when stacked.
Finish — push and open the PR
Push the branch. For stacked work, push each stage's branch.
Open the PR with an explicit base — pr.base (defaulting to branch.default), or
the previous stage's branch for a stacked PR. gh pr create without --base targets the
repo's default branch, which silently flattens a stack into one enormous PR:
gh pr create --base <pr.base or the stage below> --title "<title>" --body-file <file>
Ready for review unless pr.ready is false or the user said otherwise; add --draft
when it is.
PR body, fully filled. Use pr.template if the repo has one; otherwise this template:
## What
<the change in plain terms — what it does and why>
## Changes
- <path> — <what changed and why>
## Tests
<exact commands run and their real results — or "not run — <reason>">
## Docs
<what was updated; screenshots added or replaced and how they were captured
(real run vs. mock); stale images swept — or why no doc change was needed>
## Risks / follow-up
<noticed but not handled; screenshot captures still needed; etc.>
Hand off. The push triggers CI, and ci-pipeline loads automatically from here —
watching runs, diagnosing failures, fixing or escalating is its job, not this skill's.
Say the handoff happened; do not duplicate it.
Scope boundaries
Always in bounds
- Reading anything in the repo; web research during Task 1.
- The planned change, its tests, its docs, its screenshots.
- Offering (never silently adding) a basic CI workflow when none exists — in a development
run, under the Task 1 gate.
- Writing
.claude/code-development.yml in setup mode, after showing it and getting
confirmation — committing that one path on a branch, pushing, opening a PR, and merging
that PR on an explicit yes.
- Running the repo's own recorded commands to validate the config in setup mode, after
saying which ones and getting a go-ahead.
Never in bounds, even when it would be faster
- Starting Task 2 without sign-off, or Task 3 with red tests.
- Committing or pushing code whose local tests didn't run or didn't pass.
- Pushing directly to the default branch or to
branch.default, force-pushing, or
git add -A.
- Weakening any check to get green — skipping tests, loosening lint rules, lowering
coverage,
continue-on-error, excluding files from a scanner.
- Expanding scope beyond the signed-off plan without reopening the gate.
- A new dependency, schema change, or workflow edit the plan didn't approve.
- Fabricating a screenshot, or presenting a mock as the real application.
- Writing, committing, or authoring any file other than
.claude/code-development.yml
while in setup mode — no code, no tests, no docs, no CI workflow. Setup records; it
doesn't build.
- Recording a command in the config without running it, or describing an unvalidated
command as verified.
- Substituting a different command when a recorded one is missing, instead of stopping.
- Merging the config PR without an explicit yes.
- Watching, re-running, or diagnosing CI — that's ci-pipeline's job.
Stop and ask when
- The repo has no remote, or
gh/MCP has no write access to it (Phase 0 and Setup) — both
modes end in a push and a PR.
- No test command is detectable anywhere, so there is no gate to commit behind (Setup).
- A config already exists and Setup was asked to write one (Setup).
- The config is missing a required key, won't parse, or names a command the repo doesn't
have (Phase 0 and Task 2).
- Any design decision has more than one viable approach — Task 1 exists to surface these,
not to smooth past them.
- The working tree is dirty (Phase 0 and Setup), or the branch you'd cut from is behind
origin (Phase 0).
- Implementation reveals the plan was wrong or incomplete.
- The correct fix and the fast fix disagree — surface the tradeoff, pick nothing.
- A schema change, new dependency, or workflow change turns out to be needed.
- Local tests can't run in this environment.
- The app won't run and mocks won't render, so a needed screenshot can't be captured.
- No CI workflows exist — offer to create them, then wait for the answer.
Reporting
Every task ends with its own block; the run ends with the PR link. Fixed format:
## code-development — <setup | scope | implement | document | finish> — <slug>
**Target:** <repo> @ <branch> (default: <trunk>)
**Config:** `.claude/code-development.yml` <loaded | written this run, PR <link> |
absent — discovered inline, nothing recorded>
**Status:** <config written, commands validated | config written, commands UNVALIDATED |
plan awaiting sign-off | committed, tests green | docs committed | PR open: <link>>
**Done:** <what this task actually produced>
**Commands run:** <exact commands and their real results, or "not run — <reason>">
**Open questions:** <anything blocking the next task, or "none">
Never fill Commands run with anything that did not execute. "Not run — " is a
correct answer; a fabricated pass is the one failure mode that makes this skill worse than
developing by hand. In setup mode the same rule covers validation: a command that wasn't run
is reported as unvalidated, never as working.
If something doesn't match reality
The config describes what the repo was when someone wrote it. The repo is what is true
now. When they disagree — the test script was renamed, the default branch moved, the docs
directory changed — trust the repo, say exactly what you found, and stop rather than
forcing the run to match the config. Tell the user which key is stale so they can fix
.claude/code-development.yml in the same breath.
No repo, no gh, no discoverable way to run the tests, an application that won't start, a
toolchain this skill can't identify — say exactly which, report what was and wasn't done,
and stop. Don't reconstruct test results from reading the code, don't infer what CI enforces
from the language alone, and don't present a mocked render as the live app. A plan built on
an unverified assumption gets signed off and then implemented — which is exactly how a wrong
guess becomes permanent.
1---2name: code-development3description: Guides a feature, bugfix, or patch through three tasks in strict sequence: (1) scope and design — read the repo, research best practices, surface every open question and design choice, and stop for explicit sign-off on a written plan; (2) implement — feature branch, smallest change, security first, written to pass SonarQube/CodeQL/lint, no commit until local tests actually run and pass; (3) document — update or create README/docs with fresh Playwright screenshots from a local run or mocks, sweeping stale images even on a bugfix. Ends by pushing and opening a fully filled PR (stacked PRs for larger work), then hands CI to ci-pipeline. `/code-development init` records validated test, lint and PR conventions in `.claude/code-development.yml`. Use on /code-development, "develop a feature", "fix a bug", or any ask to build, implement, fix, or patch code — even if coding already started. Not for CI failures (/ci-pipeline), releases (/release), or reviewing others' PRs. Needs git and gh — Claude Code only.4---56# code-development78Takes a feature, bugfix, or patch from "I want X" to an open pull request, in three tasks9that run strictly in sequence — scope, implement, document — so no task steps on another.1011This skill has real side effects: it creates branches, commits, pushes, and opens PRs. It12never touches the default branch directly and never commits code whose tests haven't13actually passed.1415Everything repo-specific — how to run the tests, what CI enforces, which branch to cut16from, whether PRs open ready or draft, whether screenshots are expected — comes from17**`.claude/code-development.yml` in the repo being worked on**, written by18`/code-development init`. Without that file the skill rediscovers the same facts on every19single run and has no record of the answers you gave last time.2021Hard rules, restated up front because they're the ones that erode mid-task:22231. **Never guess on direction.** Any design decision with more than one viable approach is24 a stop-and-ask: present the options with tradeoffs and wait. Research first — the repo's25 own conventions, then current best practice — before proposing anything.262. **Task gates are hard.** Task 2 does not start until the Task 1 plan has the user's27 explicit sign-off. Task 3 does not start until Task 2's tests are green and committed.28 No look-ahead, no "while I'm here."293. **No commit until local tests pass.** Run them for real. If they can't run, there is no30 commit — say why and stop. Never claim a command ran that didn't. This governs commits31 that change code; setup mode's single config commit is the one exemption, and it reports32 a pre-existing red suite rather than committing on top of a green one it invented.334. **Never push directly to the default branch**, or to whatever `branch.default` names.34 All work lands on a `feature/`, `fix/`, or — in setup mode only — `chore/` branch, via a35 PR.365. **Smallest change, explicit staging.** No unrelated edits, no renames or refactors the37 task doesn't require, and never `git add -A` — stage each file by name.386. **Setup mode commits exactly one path**, `.claude/code-development.yml`, and authors39 nothing else — no code, no tests, no docs, no workflow. It *runs* the repo's own commands40 to validate them; it never stages what those runs leave behind. A development run commits41 only what the signed-off plan named.427. **The config is the contract; the repo is the truth.** Where they disagree — a recorded43 command that no longer exists, a default branch that was renamed — stop and say which44 key is stale. Never improvise a substitute command to keep the run moving.4546## The two modes4748Pick the mode before doing anything else, and say which one you're in.4950| Invocation | Mode | Writes anything? |51|---|---|---|52| `/code-development init`, "set up code-development", "configure code-development" | **Setup** — discover the toolchain, validate it by running it, write `.claude/code-development.yml`, open a PR for it | One file, on a branch, after confirmation |53| `/code-development`, "develop a feature", "fix a bug", "implement X" | **Development run** — Phase 0, then Tasks 1–3, then the PR | A branch, commits, a push, a PR |5455**Plain `/code-development` in a repo with no config offers Setup first.** Say that's what56you're offering rather than silently interviewing. If the user declines, run the Phase 057discovery inline for this run only and say plainly that nothing is being recorded — the58next run will rediscover all of it.5960## Environment and tooling6162- Needs `git`, `gh`, and a checked-out repo — Claude Code only. Prefer a connected GitHub63 MCP server if one is in this session's tools; otherwise `gh`. Check the tools actually64 available in this session, not a registry of installable ones.65- **Push access is required** in both modes. Both end in a pushed branch and a PR. If66 neither path is authenticated with write access, stop and say so before Task 1 rather67 than after the code is written.68- The repo is **whatever repo is checked out**. Discover it; never assume a name.69- Task 3 captures screenshots with **Playwright**, which needs its browsers installed70 (`npx playwright install chromium` or the repo's own equivalent). A repo whose config71 records `screenshots.mode: none` never needs it.7273This skill is repo-agnostic, so the quality toolchain is **discovered, not assumed**. When74`.claude/code-development.yml` exists, that discovery already happened and its result is in75the file. When it doesn't, read, in this order, whatever exists:7677- `.github/workflows/*.yml` — what CI actually enforces: linters, test commands, scanners,78 matrices. This is the ground truth for what the code must pass.79- `Makefile`, `package.json` scripts, `pyproject.toml`, `tox.ini`, `go.mod`, `Cargo.toml` —80 how to run those same checks locally.81- Linter and scanner configs: `.eslintrc*`, `ruff.toml`, `.golangci.yml`,82 `sonar-project.properties`, `.pre-commit-config.yaml`, `codeql` config.8384**If no CI workflows exist**, that is a finding, not a free pass: in a development run,85offer to add a basic lint + test workflow (its own commit, or its own stacked PR if86non-trivial). Offer — never silently create it, and never skip local checks just because CI87wouldn't have caught the gap. Setup mode makes no such offer; it records the absence in the88config's `ci` block and points at a development run.8990SonarQube and CodeQL generally cannot run locally. Handle that honestly: run everything that91*can* run (linters, the test suite, any local scanner the repo configures), write the code92to the standards those scanners enforce (Task 2 lists them), and let ci-pipeline catch the93remainder after the push. Never report a scan as passed locally when it did not run locally.9495## Setup mode — `/code-development init`9697Takes a repo from nothing to a committed, **validated** config, so every later run starts98from recorded fact instead of rediscovery. Build it *from what the repo actually has* rather99than by interrogating the user about things you can read.100101**1. Ground it.** Confirm push access, then discover the repo, the current branch, the102default branch, and whether the tree is clean:103104```bash105git rev-parse --show-toplevel && git branch --show-current106git remote -v107git status --porcelain # must be empty — see below108gh repo view --json nameWithOwner,defaultBranchRef109```110111**Stop now if the tree is dirty.** Setup's commit has to contain the config and nothing112else, and you cannot guarantee that starting from someone else's uncommitted work. Checking113this here rather than at commit time matters, because step 7 writes a file and from then on114"dirty" no longer distinguishes your change from theirs.115116If `gh repo view` errors, don't work around it — step 3 covers why it can fail.117118**2. Check for an existing config** at `.claude/code-development.yml` (or `.yaml`). If one119exists, show it and ask whether to update it or keep it — never overwrite a config the user120hasn't seen.121122**3. Check the prerequisites that actually block people**, in this order, and stop123helpfully on each:124125- **Not a git repo** → nothing here works. Say so and stop.126- **No remote** (`git remote -v` is empty) → the skill ends in a push and a PR, so there is127 nothing to push to. Say so, name `git remote add origin <url>` as the fix, and stop.128- **No `gh`/MCP write access** → same reason. Stop rather than discovering it after the code129 is written.130- **No detectable way to run tests** — no test target in a Makefile, no test script in131 `package.json`, no `pytest`/`tox`/`go test`/`cargo test` reachable, nothing in CI that132 runs one. **Stop here.** Say plainly that `/code-development` refuses to commit without a133 real passing test run (hard rule 3), so a repo with no runnable test command has no134 usable gate. List exactly what you looked for and didn't find. Do **not** write a test135 harness, a CI workflow, or a placeholder command to get past this — building those is a136 development run's job, under the Task 1 gate, not setup's.137138**4. Read CI first — it's the ground truth.** List the workflows and read what they139actually run:140141```bash142ls .github/workflows/143gh workflow list --repo <owner/repo> --all144```145146Take the concrete step commands out of the YAML — `npm run lint`, `pytest -q`, `make check`147— rather than inferring them from the language. Those are what the code must pass.148149**5. Derive the local equivalents** from `Makefile`, `package.json`, `pyproject.toml`,150`tox.ini`, `go.mod`, `Cargo.toml`, and the linter/scanner configs. Note every check CI runs151that has **no local equivalent** — SonarQube, CodeQL, a hosted scanner, a matrix leg that152only exists on CI. That gap goes in the config verbatim so it's visible on every run instead153of being rediscovered or forgotten.154155**6. Ask only what genuinely can't be read**, offering the derived answer for each so the156user is correcting rather than composing:157158- which command is *the* test command, when the repo offers several (`test`, `test:unit`,159 `test:ci`) — offer the one CI uses160- whether PRs open ready for review or as drafts161- whether this repo has a UI worth screenshotting at all — offer `none` for a library, a162 CLI, or a backend service with no rendered surface163- how the app comes up for a screenshot run, and at what URL164- which paths count as docs, and where images live — offer what the repo actually has165166**7. Write `.claude/code-development.yml`**, show it back in full, and ask for confirmation.167168**8. Validate it by actually running the recorded commands, before committing anything.** A169config claiming `npm test` works when it doesn't is worse than no config at all — it turns170Task 2's gate into a lie that only surfaces mid-implementation.171172Say which commands you're about to run and get a go-ahead first: these are the repo's own173scripts, and a `test` target can have side effects. Then run each of `commands.test`,174`commands.lint`, and any `typecheck`/`format`/`build` you recorded, and read the result175carefully, because the two failure modes mean opposite things:176177- **The command doesn't exist, isn't executable, or errors on invocation** (`command not178 found`, an unknown npm script, a missing module) → the *config* is wrong. Fix the entry179 and re-run. Never commit a config in this state.180- **The command ran and the repo's own tests or lint failed** → the config is right and the181 repo is red. That's not a setup problem. Record the config as-is, say clearly which182 command failed and that it's pre-existing, and note that `/code-development` will refuse183 to commit until it's green.184185If you fix an entry here, **show the corrected config again** before going further — the186user confirmed the version you had in step 7, not the one you changed.187188If the user declines to run them, the commands are **unvalidated** — say so in the report189and in the PR body. Never describe them as verified.190191**9. Commit it on a branch and open a PR.** This is the only write setup mode makes, and it192stays narrow. Step 1 established the tree was clean, so the config from step 7 should be the193only tracked change — but a validation run can leave untracked artifacts (`coverage/`,194`.pytest_cache/`, a `dist/`). Never stage those. If any of them aren't gitignored, say so;195that's a finding for a development run, not something to sweep into this commit:196197```bash198git fetch origin <default branch>199git checkout -b chore/code-development-config origin/<default branch> # branch off the200 # default, not201 # whatever is checked out202git add .claude/code-development.yml # explicit path, never -A203git commit -m "Add code-development config for the /code-development skill"204git push -u origin chore/code-development-config205gh pr create --fill --base <default branch>206```207208Never commit anything else in that commit, and never push to the default branch.209210**10. Ask whether to merge it.** Merge only on an explicit yes, and only after the PR's own211checks are in a state the user accepts. If they'd rather review it themselves, leave the PR212open and say that the next `/code-development` run won't see the config until it lands on213the branch being worked from.214215**11. Say which branch they're on now**, and switch back to where they started unless216they're continuing straight into a development run from here.217218**12. Offer to continue into a development run.** The Task 1 sign-off gate still stands219between that point and any code being written, so continuing is safe — but say so rather220than assuming.221222Setup mode **never** edits code, adds a test, adds a CI workflow, or starts Task 1. It223discovers, validates, records, and stops.224225## The config226227`.claude/code-development.yml`, in the repo being worked on:228229```yaml230version: 1231232branch:233 default: main # what feature branches are cut from234 prefixes:235 feature: feature/236 fix: fix/237238commands: # the local gate — all of these must pass before a commit239 test: "npm test"240 lint: "npm run lint"241 typecheck: "npm run typecheck" # omit if the repo has none242 format: "npm run format -- --check"243 build: "npm run build" # omit if a build isn't part of the local gate244245ci:246 workflows: [".github/workflows/ci.yml"]247 enforces: ["lint", "test", "typecheck", "codeql"]248 local_gap: "CodeQL runs only in CI — nothing local reproduces it"249250pr:251 base: main # a stacked PR targets the stage below instead252 ready: true # false → open as a draft253 conventional_commits: true254 template: ".github/pull_request_template.md" # omit if the repo has none255256docs:257 paths: ["README.md", "docs/**"]258 images: "docs/images"259260screenshots:261 mode: playwright-local # playwright-local | playwright-mock | none262 start: "npm run dev" # omit when mode is none263 url: "http://localhost:5173"264 viewport: "1280x800"265```266267Required: `version`, `branch.default`, and a `commands` block containing at least `test`.268Everything else is optional, and **every omission has exactly one defined behavior** — never269improvise a different one:270271| Omitted | Behavior |272|---|---|273| `branch.prefixes` | `feature/` and `fix/` |274| `commands.lint` | No lint step in the Task 2 gate — say so, don't invent one |275| `commands.typecheck` / `format` / `build` | Not part of the gate |276| `ci` | Nothing is claimed about what CI enforces; the CI-vs-local gap goes unstated |277| `pr.base` | `branch.default` |278| `pr.ready` | Ready for review, not draft |279| `pr.template` | The Finish template below |280| `pr.conventional_commits` | True |281| `docs.paths` | Discover `docs/`, `README.md`, a wiki dir — whatever the repo has |282| `docs.images` | `docs/images` |283| `screenshots.viewport` | `1280x800` |284| `screenshots` entirely | Treat as `mode: playwright-local`, discovering the app's start command in Task 3 exactly as an unconfigured repo would |285286## Phase 0 — Orient287288Three steps, in order. Verify with real commands; assume nothing.289290**1. Ground it.**291292```bash293git rev-parse --show-toplevel && git branch --show-current294gh repo view --json nameWithOwner,defaultBranchRef295git status --porcelain # dirty tree → stop and ask; never sweep stray work into a feature296```297298**2. Load the config.** Read `.claude/code-development.yml` (accept `.yaml` too). Three299outcomes — say which one you're in before doing anything else, and never silently degrade:300301- **Found** — echo the resolved config back in one short block: the test and lint commands302 that will gate the commit, the branch it'll cut from, whether the PR opens ready or draft,303 the screenshot mode, and the recorded CI-vs-local gap. The user should be able to catch a304 wrong value here, before any of it matters. **Skip toolchain rediscovery** — the file is305 the answer. Skipping it isn't the same as trusting it blindly: rule 7 still holds, and a306 recorded command that turns out not to exist gets caught at the point of use in Task 2,307 where it's a stop rather than a substitution.308- **Absent** — say so and offer `/code-development init`. If they take it, run Setup, then309 come back here. If they decline, run the toolchain discovery inline for this run only and310 say plainly that nothing is being recorded.311- **Present but unparseable, or missing a required key** — stop. Name the offending key.312 Don't fall back to defaults for a required field, and don't improvise a config to keep the313 run going.314315`branch.default` is the branch feature work is cut from, which is usually — but not always —316the repo's GitHub default. A project that develops off `develop` records that, and rule 4317still forbids pushing directly to either. If the two differ, say so in the echo rather than318letting it surprise someone at PR time.319320**3. Check the branch you'll actually cut from is current**, now that you know which one it321is:322323```bash324git fetch origin325git rev-list --count <branch.default>..origin/<branch.default> # >0 → local is behind:326 # say so and ask whether to327 # pull before branching328```329330Classify the request — **feature, bugfix, or patch**. All three run all three tasks. A331bugfix can change how something looks, and stale screenshots left in the docs after a fix332are a defect; Task 3 may conclude "no doc changes needed," but only after actually checking,333never by category ("it's just a bugfix").334335Then state the toolchain: the CI checks, the local equivalents, and any gap between them —336read from the config, or discovered inline if there isn't one.337338## Task 1 — Scope and design339340Goal: a written plan with zero open questions, signed off by the user.341342Open by reminding the user this task is best run in **plan mode** (Shift+Tab twice in Claude343Code), so nothing gets edited while the design is still open.3443451. Restate the request in your own words — what the change is *for*, not just what it says.346 A fix that clears the symptom but contradicts the intent is the wrong fix.3472. Read before asking: the code in the affected area, related modules, existing tests,348 existing docs, and recent PRs touching the same files. Repo conventions win over generic349 best practice when they conflict.3503. Research best practices for anything unfamiliar — web search the library, pattern, or351 security guidance involved. Cite what was found; "best practice says" with no source is a352 guess wearing a suit.3534. Enumerate every open question and every design decision with more than one viable354 approach. For each decision: the options, their tradeoffs, which you'd pick and why. Ask355 them together in one message where possible, not as a drip-feed.3565. Size it. A standard task is one PR a reviewer can hold in their head — one concern, a357 handful of files. Bigger than that → propose a **stacked-PR breakdown**: stage 1 branches358 off `branch.default`, each later stage branches off the previous, each PR targets the359 branch below it, landed bottom-up. Docs get their own stacked PR on top of the360 implementation stage(s) when the change is large; on a standard-size task they ride the361 same PR as separate commits. Name every branch in the plan.3626. Write the plan: scope, out of scope, each design decision as resolved, files expected to363 change, test plan, doc and screenshot impact, PR structure (single or stacked, with364 branch names and bases).365366**Gate:** post the plan and stop. Task 2 starts only on explicit sign-off — "approved",367"go", or an edit followed by a go. Silence is not sign-off. If implementation later reveals368the plan was wrong, this gate reopens; the plan is amended and re-approved, not quietly369outgrown.370371## Task 2 — Implement372373Goal: the planned change, at quality, committed on a branch with green local tests.3743751. Branch: `<branch.prefixes.feature><slug>` or `<branch.prefixes.fix><slug>` — `feature/`376 and `fix/` by default — cut from an up-to-date `branch.default`, or from the previous377 stage's branch when stacked.3782. Implement exactly the plan — the smallest change that fully delivers it. Anything that379 turns out to need more than the plan said is a stop: reopen the Task 1 gate.3803. Write to scanner standards even where the scanner can't run locally. The recurring381 Sonar/CodeQL/lint findings to design out rather than patch up afterward:382 - Validate and bound every external input. Parameterize queries. Never build shell383 commands, SQL, or file paths by concatenating user input.384 - Handle every error path explicitly — no swallowed exceptions, no bare `except`/empty385 `catch`, no ignored return values.386 - No secrets in code, config, tests, or fixtures. Ever. Not even placeholders that look387 real.388 - No duplicated blocks, no dead code, no TODO standing in for an implementation. Keep389 cognitive complexity down by extracting functions, not by nesting deeper.390 - Release everything acquired — files, connections, locks. Prefer context managers,391 `defer`, try-with-resources.392 - No new dependency and no version pin the plan didn't approve.3934. Tests: targeted tests for the changed area, per the plan's test plan. A bugfix's test394 must fail on the pre-fix code — verify that by actually running it against the unfixed395 code, don't assume it.3965. Run the gate: every command in the config's `commands` block — `test`, `lint`, and any397 `typecheck`/`format`/`build` recorded — or, with no config, the equivalents discovered in398 Phase 0. All green → commit. Anything red → fix it or stop and report. Committing red is399 never in bounds, and neither is narrowing the test run until it passes. **If a recorded400 command isn't found**, the config is stale: stop, name the key, and say what the repo has401 instead. Don't substitute a command of your own choosing.4026. Commits: Conventional Commits when `pr.conventional_commits` is true (`feat:`, `fix:`,403 `test:`, `refactor:` only when the plan called for one), one concern per commit,404 implementation only — docs come in Task 3's own `docs:` commits. Stage files by name.405406**Gate:** tests green, work committed, tree clean. Report the exact commands run and their407real output before moving on.408409## Task 3 — Document410411Goal: the docs match the code that was just written, images included.4124131. Find the docs: `docs.paths` from the config, else `docs/`, `README.md`, a wiki directory414 — whatever the repo actually has. **No docs at all → create or extend `README.md`.** That415 is the floor, not an option to skip.4162. Write or update: what the change does, how to use it, config or flags added, breaking or417 behavior changes. For a bugfix: check whether existing docs describe the old, broken418 behavior, and correct them.4193. Screenshots, whenever the change is user-visible and `screenshots.mode` isn't `none`:420 - Get the application running locally with `screenshots.start` at `screenshots.url` —421 or, with no config, discover how from the README, scripts, and compose files, the same422 way the toolchain was discovered. If a real run isn't possible in this environment,423 fall back to mocked data or component-level rendering under Playwright — and say424 plainly which of the two produced each image.425 - Capture with Playwright at `screenshots.viewport` (1280×800 by default, unless the426 repo's existing images establish otherwise), saved to `docs.images` (else where427 existing images live, else `docs/images/`), named for the feature, not the date.428 - **Stale-image sweep, every run, bugfixes included:** list the existing doc images that429 touch the changed area and re-capture any the change visibly altered.430 - If neither a local run nor a mock will render: capture nothing, claim nothing. List the431 exact captures still needed in the PR body as follow-up. A fabricated or misattributed432 screenshot is worse than a missing one.433 - `screenshots.mode: none` means this repo has nothing to screenshot. Say that's why434 there are no images; don't treat it as a skipped step.4354. Commit docs separately (`docs:` commits) on the same branch — or on the docs stage's436 branch when stacked.437438## Finish — push and open the PR4394401. Push the branch. For stacked work, push each stage's branch.4412. Open the PR with an **explicit base** — `pr.base` (defaulting to `branch.default`), or442 the previous stage's branch for a stacked PR. `gh pr create` without `--base` targets the443 repo's default branch, which silently flattens a stack into one enormous PR:444445 ```bash446 gh pr create --base <pr.base or the stage below> --title "<title>" --body-file <file>447 ```448449 Ready for review unless `pr.ready` is false or the user said otherwise; add `--draft`450 when it is.4513. PR body, fully filled. Use `pr.template` if the repo has one; otherwise this template:452453 ```454 ## What455 <the change in plain terms — what it does and why>456457 ## Changes458 - <path> — <what changed and why>459460 ## Tests461 <exact commands run and their real results — or "not run — <reason>">462463 ## Docs464 <what was updated; screenshots added or replaced and how they were captured465 (real run vs. mock); stale images swept — or why no doc change was needed>466467 ## Risks / follow-up468 <noticed but not handled; screenshot captures still needed; etc.>469 ```4704714. Hand off. The push triggers CI, and **ci-pipeline loads automatically from here** —472 watching runs, diagnosing failures, fixing or escalating is its job, not this skill's.473 Say the handoff happened; do not duplicate it.474475## Scope boundaries476477**Always in bounds**478479- Reading anything in the repo; web research during Task 1.480- The planned change, its tests, its docs, its screenshots.481- Offering (never silently adding) a basic CI workflow when none exists — in a development482 run, under the Task 1 gate.483- Writing `.claude/code-development.yml` in setup mode, after showing it and getting484 confirmation — committing that one path on a branch, pushing, opening a PR, and merging485 that PR on an explicit yes.486- Running the repo's own recorded commands to validate the config in setup mode, after487 saying which ones and getting a go-ahead.488489**Never in bounds, even when it would be faster**490491- Starting Task 2 without sign-off, or Task 3 with red tests.492- Committing or pushing code whose local tests didn't run or didn't pass.493- Pushing directly to the default branch or to `branch.default`, force-pushing, or494 `git add -A`.495- Weakening any check to get green — skipping tests, loosening lint rules, lowering496 coverage, `continue-on-error`, excluding files from a scanner.497- Expanding scope beyond the signed-off plan without reopening the gate.498- A new dependency, schema change, or workflow edit the plan didn't approve.499- Fabricating a screenshot, or presenting a mock as the real application.500- Writing, committing, or authoring **any file other than `.claude/code-development.yml`501 while in setup mode** — no code, no tests, no docs, no CI workflow. Setup records; it502 doesn't build.503- Recording a command in the config without running it, or describing an unvalidated504 command as verified.505- Substituting a different command when a recorded one is missing, instead of stopping.506- Merging the config PR without an explicit yes.507- Watching, re-running, or diagnosing CI — that's ci-pipeline's job.508509## Stop and ask when510511- The repo has no remote, or `gh`/MCP has no write access to it (Phase 0 and Setup) — both512 modes end in a push and a PR.513- No test command is detectable anywhere, so there is no gate to commit behind (Setup).514- A config already exists and Setup was asked to write one (Setup).515- The config is missing a required key, won't parse, or names a command the repo doesn't516 have (Phase 0 and Task 2).517- Any design decision has more than one viable approach — Task 1 exists to surface these,518 not to smooth past them.519- The working tree is dirty (Phase 0 and Setup), or the branch you'd cut from is behind520 origin (Phase 0).521- Implementation reveals the plan was wrong or incomplete.522- The correct fix and the fast fix disagree — surface the tradeoff, pick nothing.523- A schema change, new dependency, or workflow change turns out to be needed.524- Local tests can't run in this environment.525- The app won't run and mocks won't render, so a needed screenshot can't be captured.526- No CI workflows exist — offer to create them, then wait for the answer.527528## Reporting529530Every task ends with its own block; the run ends with the PR link. Fixed format:531532 ## code-development — <setup | scope | implement | document | finish> — <slug>533534 **Target:** <repo> @ <branch> (default: <trunk>)535 **Config:** `.claude/code-development.yml` <loaded | written this run, PR <link> |536 absent — discovered inline, nothing recorded>537 **Status:** <config written, commands validated | config written, commands UNVALIDATED |538 plan awaiting sign-off | committed, tests green | docs committed | PR open: <link>>539540 **Done:** <what this task actually produced>541 **Commands run:** <exact commands and their real results, or "not run — <reason>">542 **Open questions:** <anything blocking the next task, or "none">543544Never fill **Commands run** with anything that did not execute. "Not run — <reason>" is a545correct answer; a fabricated pass is the one failure mode that makes this skill worse than546developing by hand. In setup mode the same rule covers validation: a command that wasn't run547is reported as unvalidated, never as working.548549## If something doesn't match reality550551The config describes what the repo *was* when someone wrote it. The repo is what is true552now. When they disagree — the test script was renamed, the default branch moved, the docs553directory changed — **trust the repo, say exactly what you found, and stop** rather than554forcing the run to match the config. Tell the user which key is stale so they can fix555`.claude/code-development.yml` in the same breath.556557No repo, no `gh`, no discoverable way to run the tests, an application that won't start, a558toolchain this skill can't identify — say exactly which, report what was and wasn't done,559and stop. Don't reconstruct test results from reading the code, don't infer what CI enforces560from the language alone, and don't present a mocked render as the live app. A plan built on561an unverified assumption gets signed off and then implemented — which is exactly how a wrong562guess becomes permanent.