Agents will invent your stack on day one. You should treat every invention as untrusted. Keep the agent read-only until local tests pass. That is the entire onboarding rule for juniors.
You clone a repo and feel pressure to ship. A chat box offers a complete local setup. It guesses the package manager and the test runner. It may also invent ports, paths, and env keys. Those guesses look helpful and they are often wrong. You apply a diff you cannot explain. Your first PR then teaches the team a bad habit.
The rule in one line
No AI writes until you run the real tests. You may ask questions, but you may not patch files. Record observed facts in a small YAML file. Do not let the model draft that file.
This workflow is a proposal for a first clone. Label every script below as unexecuted until you review it. You still own the merge button after the gates pass.
Gate 1: Hand-write a repo fact file
Open CI first. Then open the README. Copy commands you can see, never ones you imagine. Create .repo-facts.yml at the repo root. Fill only fields you verified in the tree. Leave a field blank if you have not seen it. A blank field is safer than a confident lie.
# .repo-facts.yml
# Hand-written on day one. Do not auto-generate.
package_manager: "" # npm | pnpm | yarn | bun | other
install_command: "" # copied from CI or docs
test_command: "" # copied from CI, not from chat
lint_command: ""
dev_command: ""
runtime: "" # language + version if documented
default_branch: ""
ci_file: "" # e.g. .github/workflows/ci.yml
forbidden_paths:
- ".env"
- ".env.*"
- "secrets/"
- "credentials/"
facts_verified_at: "" # ISO date you filled this
Now hunt those values with commands, not chat.
- List workflow files in the default CI folder.
- Search for the real test invocation next.
- Print the default branch from origin metadata.
- Confirm the lockfile that actually exists.
- Paste only what those commands printed.
ls .github/workflows 2>/dev/null || ls .gitlab-ci.yml Makefile 2>/dev/null
rg -n "npm test|pnpm test|yarn test|pytest|go test|mvn test|cargo test" \
.github Makefile package.json pyproject.toml go.mod 2>/dev/null
git remote show origin | rg "HEAD branch"
ls package-lock.json pnpm-lock.yaml yarn.lock bun.lockb \
poetry.lock Cargo.lock go.sum 2>/dev/null
You now have a fact file the agent cannot own. If CI and README disagree, stop. Ask a human. Do not average the two answers.
Gate 2: Queue questions instead of diffs
Create .day1-questions.md with your real unknowns only. One question per heading. One file path per question. You must open that path before you ask.
# Day-one question queue
## Q1. What installs dependencies in CI?
- Opened: .github/workflows/ci.yml
- Status: unanswered
- Answer:
- Verified in tree: no
## Q2. Which test command must stay green?
- Opened: package.json (scripts.test) and ci.yml
- Status: unanswered
- Answer:
- Verified in tree: no
## Q3. Which paths must never appear in a diff?
- Opened: .gitignore and .repo-facts.yml
- Status: unanswered
- Answer:
- Verified in tree: no
Use the queue in this exact order.
- Pick the top unanswered question.
- Open the cited file in your editor.
- Ask the agent in read-only mode.
- Verify the answer against the open file.
- Mark the question answered or still blocked.
Never skip step two. The file is the source. If the model says UNKNOWN, you stop. You do not let it fill the gap.
Here is a read-only prompt template. It is unlabeled as executed. Paste it only after the file is open.
You are read-only. Do not propose file patches.
Do not invent commands missing from .repo-facts.yml.
Question: Why does CI set CI=true for the test job?
I opened: .github/workflows/ci.yml lines 40-62.
Answer only from that file. Say UNKNOWN if missing.
If you need another file, name it. Do not guess.
Keep secrets out of the prompt. Do not paste .env. Do not paste tokens. Do not paste customer data. A question about a port is fine. A dump of credentials is not.
Gate 3: Unlock writes after a green run
The agent stays locked until tests actually run. You run the command from .repo-facts.yml yourself. A small script records the unlock stamp. Review this script before you chmod it.
#!/usr/bin/env bash
# scripts/day1-unlock.sh
# Proposed local gate. Review before you trust it.
set -euo pipefail
FACTS=".repo-facts.yml"
STAMP=".ai-write-unlock"
QUESTIONS=".day1-questions.md"
if [[ ! -f "$FACTS" ]]; then
echo "missing $FACTS — write facts by hand first" >&2
exit 1
fi
need() {
local key="$1"
local val
val="$(rg -N "^${key}:" "$FACTS" | sed 's/^[^:]*:[[:space:]]*//; s/"//g')"
if [[ -z "$val" ]]; then
echo "blank field: $key" >&2
exit 1
fi
printf '%s' "$val"
}
install_cmd="$(need install_command)"
test_cmd="$(need test_command)"
branch="$(need default_branch)"
if [[ -z "${install_cmd}" || -z "${test_cmd}" ]]; then
echo "install_command and test_command must be filled" >&2
exit 1
fi
current="$(git rev-parse --abbrev-ref HEAD)"
echo "current branch: $current (default is $branch)"
if [[ ! -f "$QUESTIONS" ]]; then
echo "missing $QUESTIONS — queue your unknowns first" >&2
exit 1
fi
if rg -q "Status: unanswered" "$QUESTIONS"; then
echo "unanswered questions remain in $QUESTIONS" >&2
exit 1
fi
echo "running install: $install_cmd"
eval "$install_cmd"
echo "running tests: $test_cmd"
eval "$test_cmd"
date -u +"%Y-%m-%dT%H:%M:%SZ" > "$STAMP"
echo "writes unlocked at $(cat "$STAMP")"
echo "AI may edit only after you re-read the fact file."
Make it executable only after you read every line.
chmod +x scripts/day1-unlock.sh
./scripts/day1-unlock.sh
Add the stamp to .gitignore. The stamp is local proof, not a commit. You still write the code. The stamp only means tests ran on your machine.
.ai-write-unlock
.repo-facts.yml
.day1-questions.md
Keep .repo-facts.yml untracked if it contains machine-specific paths. Commit a .repo-facts.example.yml if your team wants the template.
Decision table for the first ticket
Use this table before you accept any model output.
| Situation | Agent mode | Your next action |
|---|---|---|
| Fact file still has blank required fields | Read-only | Hunt CI and docs |
| Question queue has an unanswered item | Read-only | Open the cited file |
| Model invents a command not in facts | Reject | Re-copy from CI |
| Install fails on a missing lockfile | Read-only | Ask a human |
| Tests fail on a clean clone | Read-only | Do not generate fixes yet |
| Tests pass and queue is empty | Writes allowed | Touch one file per turn |
Diff includes .env or secrets |
Reject | Reset the working tree |
| You cannot explain the diff aloud | Reject | Revert and re-read |
One file per turn is enough on day one. Broader refactors wait for a named ticket and a reviewer.
After unlock: a tight write loop
Writes are not a blank check. Keep the loop small.
- Restate the ticket in one sentence.
- Name the single file you will change.
- Ask for a patch against that file only.
- Read the patch as if a stranger sent it.
- Run the recorded test command again.
- If tests fail, revert. Do not stack guesses.
git checkout -- path/to/the/file
./scripts/day1-unlock.sh
If you cannot name the file, you are not ready. Go back to Gate 2. Ask one more question. Stay read-only.
A short explain-back note belongs on the PR. Three sentences. No model prose.
What changed: trimmed the timeout in worker/retry.ts.
Why: CI already sets the same timeout in ci.yml.
How I know: I re-ran `pnpm test` after the edit.
If you cannot write those three lines, drop the patch. The agent did the thinking. You did not.
Where a free local helper fits
Gate 2 needs answers, not patches. A local helper can sit on that question queue. Keep prompts short and file-scoped. Do not upload the whole tree on day one.
Disclosure: This article was prepared as part of MonkeyCode's product outreach. MonkeyCode is an open source coding assistant. The operator notes free model access and a free server option. Use that pair for read-only questions if it fits your team rules. It does not replace the hand-written fact file. It does not unlock writes. You still run the tests.
Skip any helper that wants secrets in the prompt. Skip any helper that drafts .repo-facts.yml for you. The value is the gate, not the brand.
Limitations
This gate cannot close if CI has no tests. A green local run is not a design review. The fact file goes stale after the next refactor. You must refresh it when install or test commands change. The unlock stamp is easy to fake. A determined person can skip the script. The method only works if you refuse to skip it.
Eval of install_command is a sharp edge. Only paste commands you copied from CI. Never paste a command the model invented. If the command looks like a curl-pipe installer, stop. Ask a teammate.
Windows shells will need a port of the script. The YAML fields stay the same. The rule stays the same.
Who should skip this
Skip this if you already own the stack. Skip this during a real production incident. Skip this if your team forbids local models or extra YAML files. Skip this on a throwaway prototype with no shared CI. Staff who wrote the repo do not need a passport. Juniors joining a brownfield service do.
Close
Your first job is not a clever patch. Your first job is a true test command. Keep writes locked until that command is green. Then change one file. Then explain the change in three lines. That is how you join a repo without inheriting an agent's guesses.
Top comments (0)