Without notes, state yesterday’s main idea and one unresolved question.
Codex as a constrained pair programmer
TypeScript, Packages, and Tooling
Objective
Use an agent for small, reviewable changes with explicit verification.
TypeScript is similar to design-rule checking: it catches invalid connections before runtime but cannot prove system behavior.
- plan before implementation
- small diffs
- typecheck, lint, and tests
- diff review
Why this matters
An AI coding agent will change files on your disk. Used carelessly it produces code you cannot explain in a repository you can no longer trust. Used with discipline it is a fast, tireless pair programmer. The difference is not the tool and it is not the prompt — it is a workflow you run every single time: checkpoint, scope, plan, small diff, read, check, commit. Today you learn that workflow as a procedure, and it is the practice the rest of this course is built on.
Checkpoint first
Before an agent touches anything, your repository must be in a known-good committed state.
git status
nothing to commit, working tree clean
That sentence is the entire safety net. If the tree is clean and the last commit passes your checks, whatever appears afterwards is exactly what the agent did — nothing of yours is mixed in — and one command puts you back. Skip it, and the agent's changes and your own half-finished edits become one undifferentiated mess with no clean state to return to.
Commit before you start, even if the message is dull. git commit -m "checkpoint before agent" is
a perfectly good commit.
The known-good build
Before flashing new firmware you keep a known-good image, because "it worked an hour ago" is not a state you can flash. A clean commit is that image. The agent's output is a candidate build: you compare it against the known-good one, test it, and keep it only if it earns its place. Without the golden image, every change is irreversible.
Repository instructions and permissions
Two settings turn a general tool into one that fits your project.
Repository instructions are standing rules that apply to every request, kept in a file at the
repository root. Codex reads AGENTS.md. Put in it what you would tell a new contributor on
their first morning: the commands to run, the conventions to follow, the things not to touch.
# Repository instructions
- TypeScript, strict mode. Do not introduce `any` or `as` assertions.
- Validate all external data at the boundary; see `src/parse.ts`.
- Run `npx tsc --noEmit` and `npm run build` before reporting done.
- Never edit `package-lock.json` by hand or add dependencies without asking.
Writing that file once is worth more than any individual prompt: it applies to every request you will ever make in this repository.
Permissions decide what the agent may do without asking. Codex has two independent controls,
visible in codex --help:
--sandbox— what it may touch:read-only,workspace-write(edit files in your project folder), ordanger-full-access.--ask-for-approval— when it must stop and ask:untrusted(approve anything beyond a small trusted set of commands),on-request(it decides when to ask), ornever.
Start at the restrictive end. Explore a new repository read-only, move to workspace-write for
real edits, and approve commands one at a time until you have seen what it proposes.
The bypass flag
--dangerously-bypass-approvals-and-sandbox disables both protections and lets generated
commands run unsandboxed against your machine. Its own help text calls it EXTREMELY DANGEROUS.
Do not use it on a machine you care about.
Plan before implementation
A scoped prompt is the difference between a review you can do in five minutes and a diff you will never finish reading. A good one has five parts:
- One goal, one rule. "Reject components whose
stockis not a whole number." Not "improve validation." - Where it goes. "In
src/parse.ts, insideparseComponent." - Constraints. "No new dependencies. No
any. Do not change the function's signature." - How it will be checked. "It must pass
npx tsc --noEmitandnpm run build." - A hard stop before editing. "Propose a plan first. Do not edit any file yet."
That last part is the one beginners skip and the one that pays most. A wrong plan costs thirty seconds; the wrong implementation of a wrong plan costs an hour and leaves the repository dirty.
Read the returned plan against three questions. Does it change the files you expected, and only those? Does it name the edge cases you thought of — and any you did not? Does it say what it will not do? If any answer is unsatisfying, correct the plan and ask again. This is still the cheap phase.
Pair mode — send this before granting any write access
"First inspect the project. Propose a plan for one validation rule. Do not edit yet. Name affected files, edge cases, and checks." Approve the plan explicitly, in your own words, before letting it write anything.
Small diffs
A diff is the list of lines that changed. Small diffs are not a preference; they are the only way review stays honest. A twenty-line diff gets read line by line. A four-hundred-line diff gets skimmed, and a skimmed diff is an unread diff with extra steps.
So: one rule per request, one request per commit. If a plan touches six files, split it into three requests and commit between each. The next request then starts from a clean, checked state.
Reading the diff line by line
Ask for a summary of changed files, then look yourself. Never accept the summary as the review.
git diff --stat
src/rules.ts | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
That tells you the shape: one file, four added lines, one removed. If the shape does not match the plan you approved, stop and read no further — something else happened. Then the diff itself:
git diff
--- a/src/rules.ts
+++ b/src/rules.ts
@@ -1,3 +1,6 @@
export function isLowStock(item: { stock: number }, threshold: number): boolean {
- return item.stock < threshold;
+ if (!Number.isInteger(item.stock)) {
+ throw new Error("stock must be a whole number");
+ }
+ return item.stock <= threshold;
}
How to read it: lines starting - were removed, + were added, a leading space means unchanged
context. @@ -1,3 +1,6 @@ says this block starts at line 1 and grew from 3 lines to 6.
Now the important part. That diff was asked for a whole-number check and contains one. It also
quietly changes < to <=, altering what "low stock" means for every item at exactly the
threshold. It is plausible, it compiles, and it changes behaviour you did not ask to change.
Put four questions to every changed line:
- What does this line do? If you cannot say, you cannot ship it.
- Was it in the plan? Unrequested changes are the ones that bite.
- What happens at the edges? Zero, negative,
NaN, empty string, missing property. - What did it delete? Removed lines are invisible in the running app and are where behaviour silently disappears.
Signing off someone else's work
An inspector who signs a job because the person doing it seemed confident is not an inspector. Reading the diff is the inspection, and your commit is your signature on it.
Running the checks
Reading proves intent. Running proves behaviour. After every generated change, in this order:
npx tsc --noEmit # typecheck: types agree
npm run check-format # lint/format: style is consistent, from Day 29
npm run build # it compiles and bundles
From Week 11 this list gains npm test, and automated tests become the strongest gate of the
three. Until then, exercise the feature in the browser yourself — including the failing case. A
generated validation rule that has never been given bad input has not been tested.
Recovering from a mess
When the result is wrong, do not negotiate with it. Return to the checkpoint.
- Discard changes to tracked files:
git restore . - Discard one file:
git restore src/parse.ts - Unstage without losing edits:
git restore --staged . - Park changes to look at later:
git stash, thengit stash popto bring them back.
These commands destroy uncommitted work
git restore . permanently discards every uncommitted change to tracked files, including yours.
git clean -fd deletes untracked files and folders outright — no Trash, no undo. Run
git status first and read it, and use git stash instead when you are unsure. This is exactly
why you committed a checkpoint before starting.
Walkthrough
Work in ~/fullstack-journey/inventory-ts.
git status→ clean. If not, commit.- Write
AGENTS.mdwith the four rules shown above. Commit it. - Start the agent read-only:
codex --sandbox read-only, and send the planning prompt from the[!ai]callout above. - Read the plan. Confirm it names
src/parse.tsand no other file, and lists at least the2.5,-1, andNaNcases. Correct it if not. - Approve it and let it write:
codex --sandbox workspace-write --ask-for-approval untrusted. git diff --stat, thengit diff. Apply the four questions to every+and-line.npx tsc --noEmit, thennpm run build.- In the browser, add a component with
stockof2.5and confirm it is rejected with a message. git add -Aand commit with a message naming the rule.
Checkpoint
Point at each changed line and say what it does and why it is there. If one line defeats you, do not commit — ask for an explanation of that line specifically, or delete it and write it yourself.
Your turn
- Run steps 1–9 above for one rule of your choosing: whole-number stock, non-empty
name, or a maximumstock. Exactly one. - Keep
notes/day-34.mdas you go: the prompt you sent, the plan you approved, thegit diff --statoutput, one line you questioned, and the check results. - Deliberately practise recovery. Ask for a second change, then throw it away with
git restore .and confirm withgit statusthat the tree is clean again. - Re-run the Week 4 behaviour — add, delete, search, refresh — to confirm nothing else broke.
You are done when
You have one commit whose every line you can narrate from memory, and notes showing the plan came before the code.
Common pitfalls
- Starting from a dirty tree. Your work and the agent's become indistinguishable, and there is nothing to restore to.
- Reading the summary instead of the diff. The summary describes intent.
git diffshows what happened, and the gap between them is where bugs live. - Accepting a plausible change you cannot explain. Plausible is not correct — the
<to<=above compiles perfectly. - Asking for one big feature. The diff becomes unreviewable, so it goes unreviewed. Split it.
- Granting full access to save time. You spend the saved minutes debugging what you never saw.
Verify it yourself
Open today's reference, the Codex CLI documentation, alongside codex --help in your terminal.
- Find the documented name and location of the repository-instructions file. Does it match the
AGENTS.mdused above, and can such instructions live anywhere else? - Compare the sandbox and approval modes in
codex --helpwith the docs. Which combination do the docs recommend for a first session in an unfamiliar repository?
Record both answers in notes/day-34.md. Knowing your tool's permission model from its own
documentation, rather than from a lesson, is not optional.
The hour
- 0–5 min Recall
- 5–20 min Learn
Read only the listed concept notes and official reference sections needed today.
- 20–48 min Build
Ask Codex to add one inventory validation rule. Require a plan, implementation, checks, and changed-file summary.
- 48–55 min Explain and verify
Run the result, inspect evidence, and explain the data/control flow in your own words.
- 55–60 min Quiz and commit
Complete the quiz, record one lesson, and commit the verified change when applicable.
What to hand in
One reviewed commit where every generated line is understood.
Working with AI today
Define one small task, review the plan, inspect the diff, run checks, and explain every changed section.
First inspect the project. Propose a plan for one validation rule. Do not edit yet. Name affected files, edge cases, and checks.
References
End-of-day quiz
Explain-back gate
Pass the quiz above to unlock completion.
Quiz + explain-back checks required.