1.5. Workspace
In one glance
- You will: Break the documentation checker on purpose, restore it, and learn which of the repository’s checks runs at which moment.
- You need: The base installation from 1.0. System and a checkout with no changes you care about losing.
- Time: about 20 minutes, hands-on.
Why the course pages are checked against the repository
This repository treats documentation as checked source. tools/bin/conventions docs reads every page against the actual tree and fails when the two disagree: paths named in prose must exist, mise run tasks must be declared in a manifest, quoted tool versions must equal the pins, included source regions must have one start and one end marker, and every Mermaid diagram must carry a prose alternative beside it.
The checker exists because prose drift is silent. A page pointing at a Go file renamed eleven months ago breaks no build and no test; the cost lands months later, on a reader, and in a teaching repository the pages are the product. You will watch the checker catch a missing path, then sort authority from disposable output and match each check to its moment. The offending sentence was appended to this very page, and the checker was run from the repository root:
tools/bin/conventions docscontent/1. Setup/1.5. Workspace.md: line 105: literal repository path does not exist: agents/go/nope.goLine 105 is two past the last line of this page as it ships, which is where an appended sentence lands. Delete it, run the same command, and the checker goes quiet and exits zero.
That binary is part of the toolchain you already installed: mise run install builds it into tools/bin, and mise run check:docs runs this exact command before building the site, so the hook, CI, and your terminal all decide the same way. Rename a task and the page that documented it fails before the rename is committed.
You have just watched documentation treated as something that can be wrong, not merely old.
Which files are authority, and which are disposable output
One rule sits underneath all the others: a small set of files decide what is true. Everything a command produces records one moment rather than a fact about the project.
Authority lives in mise.toml for task names and pinned tools; in agents/go/go.mod, evals/go.mod, and tools/go.mod for declared dependencies, with their go.sum files owning exact resolution; in AGENTS.md for repository invariants and operating limits; and in dprint.json plus the three .golangci.yml files for mechanical style. Use whatever editor you like, as long as it runs gopls and mise run format gets the last word on style, not your editor’s plugin.
Everything below is generated, ignored by Git, safe to delete, and never a source of truth:
agents/go/.state/holds the writable runtime databases and crash-recovery journals.site/holds the rendered documentation site.coverage.outand its reports are local measurements.results.jsonand any artifact named by an--outputflag record one run and nothing more..envand local credentials are private configuration.
One exception hides inside the first bullet. A state restore copies a snapshot of those databases back over the live ones, and a dot-prefixed .restore-* file in the state directory is the journal a half-finished restore left behind, recording which copy of the data was real. It is evidence, not litter to sweep up because a status command looks noisy: read one before you remove it. 2.4. Sessions has you plant one, watch startup refuse to guess at it, then delete it deliberately.
The three Go modules are separated for the reason 1.1. Go argues: the black-box evaluation harness must stay unable to import agent internals. And when you change a fact, change its owning source, its test, and its public contract together — never copy a dependency version into prose when the lock owns it.
Lefthook runs the same mise tasks, scoped to what changed
Local hooks are a second implementation of CI in most repositories, which is why they drift and then get bypassed. Here they are thin: lefthook only calls mise run tasks, so a hook and a workflow cannot disagree about what “checked” means.
This page’s Markdown source carries both hook task lists in a comment that check:docs rebuilds from lefthook.yml, so the two cannot drift. pre-commit formats staged Markdown, configuration, and Go, runs one check:* task per kind of file you staged, then scans that diff for secrets; each check is globbed to the paths it owns, so a one-word README fix does not pay for the Go linters. pre-push drops the globs and runs the model-, container-, cluster-, and cloud-free check:core plus the full test suite, so nothing reaches a remote unchecked, and bypassing a hook only moves the failure later. Hosted CI prepares the same deterministic surface with mise run install:validation, which installs every account-free dependency — the base tier plus the platform tier the full mise run check needs — and starts no model or platform service.
Choose the smallest check that honestly settles what you changed:
| Moment | Command | What it decides |
|---|---|---|
| While editing Go | module-local mise run check and mise run test | Formatting, lint, race-tested behavior, measured coverage |
| While editing docs | mise run check:docs and mise run check:links | Page contracts, the rendered site, and local links |
| Before pushing | mise run check:core and mise run test | The whole deterministic repository |
| Maintainer validation | mise run install:platform, then mise run check | Core plus the infrastructure, licence, and security profiles, which need the platform tier |
| Release candidate | configured eval:* and platform smoke tasks | Model and runtime behavior tied to one source commit |
Report a measured percentage next to the floor it cleared, and remember that neither number says the tested behavior is right — 0.2. Evidence owns that distinction.
Run git status --short before you start and after you finish. A dirty worktree you did not create is someone else’s work, and a repository-wide mise run format or git restore will overwrite it — scope both to what you own.
Your turn: break the ordered-list rule and read the failure
The ordered-list rule below is subtler than a missing path, and enforced just as literally.
Predict first: this course’s Markdown style requires every ordered-list item to be written 1., so the numbering stays dynamic when the list is edited. If you write 2. instead, does the checker ignore it, reformat it, or refuse?
- Mode:
temporary experiment. - Goal: watch the checker name a file, a line, and the exact rule, then restore the page and watch it go quiet.
- Files to touch: only this page. The Go modules, the seed data, and your
.envare untouched. - Preflight: confirm the page is unmodified with
git diff --quiet -- "content/1. Setup/1.5. Workspace.md", which is silent and exits zero when it is clean. - Steps: from the repository root, append a bogus list item with
printf '\n2. A renumbered list item.\n' >> "content/1. Setup/1.5. Workspace.md", then runtools/bin/conventions docsand read the failure. - Gate that proves completion: the checker exits non-zero and names the file, the line, and the rule:
content/1. Setup/1.5. Workspace.md: line 105: ordered Markdown items must use `1.`, not `2.`- Final state: restore the page with
git restore -- "content/1. Setup/1.5. Workspace.md", then runtools/bin/conventions docsagain and confirm it prints nothing and exits zero.
The rule is minor. Where it lives is not: it is a Go test in tools/internal/conventions, so a documentation contract fails the build the way a compiler error does. When Chapter 4 asks you to add a rule of your own, that is the seam you will extend.
What you can do now
- You can say why a
2.list item failstools/bin/conventions docs, and read the file, line, and rule from one message. - You can say which files are authority, which are disposable records, and why a
.restore-*journal is neither. - You can pick the smallest check that honestly settles the change in front of you, and name what each one leaves open.
git status --shortshows only changes you meant to make.
“The repository is clean” is now a checkable claim rather than a feeling: a checker that reads your prose against the code, a hook that runs the tasks you run, and a coverage floor reported per package rather than behind a total.
Continue to 2. Agents, where you assemble and run the one agent object every later chapter extends, on a model running on your own hardware.