Do not start your first hour with an agent. Prove one named green test on this checkout. Freeze a session card before any generated edit.
A new repo is not a chat window. Agents invent install steps with calm confidence. README scripts often lag the real lockfile. You then ship a story you cannot rerun.
Generated code can hide work you never ran. Reviewers still ask for a local command. Your first hour should make hiding impossible.
Why this belongs on day one
You join a team and clone the default branch. Ticket text often names a feature, not a test. The agent starts editing files you cannot map. Owners, runtimes, and CI commands stay hidden.
Juniors skip this because setup feels unproductive. It is the only work that makes later review honest. You cannot defend a diff you never executed.
The session card rule
Name one existing test before you open a model. Run that test by exact command on your machine. Write the result into a session card file. Only then let an agent touch a bounded path.
Treat the card as a gate, not a diary. No card means no agent session and no PR. Do not paste the ticket into a prompt first.
Fields you must record
Keep the session card small and boring. Record only facts you can recheck later.
Use these eight fields, nothing clever:
-
head_sha— current commit, no floatingHEAD. -
branch— the branch you will actually push. -
dirty_paths— files already changed in the tree. -
runtime— language binary and version string. -
test_command— one command, copied verbatim. -
test_exit_code— zero, or you stop here. -
owners—CODEOWNERSlines for your path. -
last_author— last merge that touched that path.
If a field is missing, write unknown. Do not let the agent invent a substitute value. Unknown owners mean you ask a human next.
Proposed script
Label this as a proposed local script. Run it in a throwaway clone first. It prints JSON to stdout and exits nonzero on a red test.
#!/usr/bin/env bash
# proposed: session-card.sh — run in a throwaway clone first
set -euo pipefail
TARGET_PATH="${1:-.}"
TEST_COMMAND="${2:-}"
if [[ -z "${TEST_COMMAND}" ]]; then
echo "usage: $0 <path> <exact test command>" >&2
exit 2
fi
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
echo "not a git checkout" >&2
exit 2
fi
HEAD_SHA="$(git rev-parse HEAD)"
BRANCH="$(git branch --show-current || echo unknown)"
DIRTY="$(git status --porcelain -- "${TARGET_PATH}" | awk '{print $2}' | tr '\n' ' ' | sed 's/[[:space:]]*$//')"
RUNTIME="$( (python3 --version || node --version || go version || echo unknown) 2>/dev/null | head -n 1)"
set +e
bash -lc "${TEST_COMMAND}"
TEST_EXIT=$?
set -e
OWNERS="unknown"
if [[ -f CODEOWNERS ]]; then
OWNERS="$(grep -v '^#' CODEOWNERS | grep -v '^[[:space:]]*$' | grep -F "${TARGET_PATH}" | tr '\n' ' ' | sed 's/[[:space:]]*$//')"
[[ -z "${OWNERS}" ]] && OWNERS="unknown"
elif [[ -f .github/CODEOWNERS ]]; then
OWNERS="$(grep -v '^#' .github/CODEOWNERS | grep -v '^[[:space:]]*$' | grep -F "${TARGET_PATH}" | tr '\n' ' ' | sed 's/[[:space:]]*$//')"
[[ -z "${OWNERS}" ]] && OWNERS="unknown"
fi
LAST_AUTHOR="$(git log -1 --format='%an <%ae>' -- "${TARGET_PATH}" || echo unknown)"
DIRTY_JSON="${DIRTY:-none}"
python3 - <<PY
import json, os
print(json.dumps({
"head_sha": os.environ["HEAD_SHA"],
"branch": os.environ["BRANCH"],
"dirty_paths": os.environ["DIRTY_JSON"],
"runtime": os.environ["RUNTIME"],
"test_command": os.environ["TEST_COMMAND"],
"test_exit_code": int(os.environ["TEST_EXIT"]),
"owners": os.environ["OWNERS"],
"last_author": os.environ["LAST_AUTHOR"],
"target_path": os.environ["TARGET_PATH"],
}, indent=2))
PY
exit "${TEST_EXIT}"
Export the values before the Python block if your shell needs them. A minimal wrapper looks like this.
export HEAD_SHA BRANCH DIRTY_JSON RUNTIME TEST_COMMAND TEST_EXIT OWNERS LAST_AUTHOR TARGET_PATH
HEAD_SHA="$(git rev-parse HEAD)"
# ...same assignments as above, then python3 - <<'PY'
Save the JSON as session-card.json at the repo root. Do not commit it unless your team asks. Paste it into the PR body instead.
Hour-one steps
Follow this order. Do not skip to a model window.
- Clone with the team's documented URL. Avoid random forks on day one.
- Check out the branch named in the ticket. Record the SHA immediately.
- Install from the lockfile, not from memory. Prefer
npm ci,pnpm i --frozen-lockfile, orpip install -ronly as the repo documents. - Find one existing test near your path. Use
rg,git grep, or the test runner list command. - Copy the exact command from CI config when you can. Check
.github/workflows,Makefile, orpackage.jsonscripts. - Run
session-card.sh <path> '<command>'. Stop if the exit code is not zero. - Read
CODEOWNERSand the last author. Send a short ping if both areunknown. - Bound the agent to that path only. Refuse extra files in the first session.
Example commands you can copy. Replace the test name with a real one from your tree.
git clone git@github.com:example/app.git
cd app
git checkout -b first-hour/session-card
git rev-parse HEAD
# discover, then freeze — do not invent the command
git grep -n "describe(\|def test_" -- '*.test.ts' '*.py' | head
rg -n "npm test|pytest|go test" Makefile package.json .github/workflows || true
chmod +x session-card.sh
./session-card.sh src/billing "npm test -- --runTestsByPath src/billing/invoice.test.ts"
# or
./session-card.sh pkg/bill "pytest pkg/bill/test_invoice.py::test_total -q"
If the named test is red on a clean checkout, you do not have an agent problem. You have an environment problem. Ask a teammate before generating code.
Decision table
Use this table before you open any model.
| Situation | Open an agent? | Next action |
|---|---|---|
| Named test is red on clean HEAD | No | Fix install, toolchain, or secrets with a human |
owners is unknown and last author is gone |
No | Ask in the team channel before editing |
| Dirty files you did not create | No |
git status, then stash or reset with review |
| Test is green, path is owned, tree is clean | Yes | Bound the path; keep the card in the PR |
| Agent wants to change lockfiles or CI | No | Split that work; it is not a first PR |
| You cannot rerun the card command | No | Do not request review |
Print the table in your notes if it helps. The point is a stop rule, not a vibe.
First PR after the card exists
Keep the first PR inside the path you measured. One test file and one production file is enough. Do not let the agent “clean up” neighbors.
Paste the session card at the top of the PR body. Then add the command a reviewer can rerun. Example markdown:
## Session card
- SHA: 9f3c1a2
- Path: src/billing
- Command: npm test -- --runTestsByPath src/billing/invoice.test.ts
- Exit: 0
- Owners: @billing-oncall
- Last author: Alex <alex@example.com>
## Replay
npm ci
npm test -- --runTestsByPath src/billing/invoice.test.ts
Ask the agent for a diff against that SHA only. Reject a patch that retargets main while you slept. Reject a patch that rewrites the test command.
If the model changes assertions to match new code, stop. Your job on day one is a green replay, not a greener story. Restore the original test and shrink the change.
When a free server is useful
Laptops on day one are often thin. You may lack a local model runtime. You still need the session card before any remote chat.
Disclosure: This article was prepared as part of MonkeyCode's product outreach. MonkeyCode is an open-source project with free model access and a free server option. Use it only after session-card.json exists and the named test is green on your checkout.
Keep the same path bound on the server. Paste the card fields into the session notes. Do not paste secrets, .env files, or production dumps. If the server cannot rerun your exact test command, treat the output as untrusted text.
If you cannot explain the diff
Revert beats a guessed explanation. Use the SHA from the card as the recovery point.
git fetch origin
git switch main
git revert --no-edit <pr-merge-sha>
# or restore your branch to the frozen card SHA
git switch first-hour/session-card
git reset --hard "$(python3 -c 'import json;print(json.load(open("session-card.json"))["head_sha"])')"
Do not open a second agent to “fix the revert.” Rerun the named test first. If it fails on the frozen SHA, your card was already a lie.
Limitations
This workflow does not replace CI. A green local test can still miss OS, timezone, or service wiring. CODEOWNERS grep is naive and misses glob owners. Monorepos may need a package filter the script does not infer.
The script also trusts the command you pass. A wrong command that exits zero is a false gate. You must copy the command from CI or a teammate, not from a model.
It will not help if tests need private services you cannot run. In that case, record unknown for the test and pair with the owner. Do not fake a unit test the suite never had.
Who should skip this
Skip this if you already run the full suite locally every morning. Skip it during an active incident when a lead already named the command. Skip it if policy forbids extra servers or extra JSON in PRs.
Staff engineers pairing on a known path can shorten the card. Juniors on a first checkout should not. The card is for people who still need a map.
Do not start your first hour with an agent. Name one green test, freeze the card, then keep the first PR small enough to replay.
Top comments (0)