Your first PR
Not every contribution touches the parser. A typo fix, a clarified sentence, a broken link — these are real, welcome PRs, and they ride a much lighter path than the add-a-notation TDD flow. This chapter is that lightweight path. For parser changes (a new 青空文庫 notation, a lexer phase, a renderer shape), follow the full TDD flow in Development loop → Adding a new 青空文庫 notation instead.
Before your first commit: environment setup
If this is a brand-new checkout, get the environment standing first:
just setup # one-shot first-time environment bootstrap
# or, equivalently:
./bootstrap
That builds the dev image and installs the lefthook git hooks. If
hooks ever stop firing later, re-run just hooks (see
Troubleshooting → Hooks not firing).
The lightweight path (a doc / typo fix)
-
Pick a small fix. A typo, a stale link, an unclear sentence in the handbook (
crates/aozora-book/src/…) or a top-level doc. Keep it to a single logical change. -
Branch.
mainis branch-protected — never commit to it directly.git switch -c docs/fix-ruby-example-typo -
Edit the
.md. Use only your editor here; no parser code is involved, so there’s nothing to compile. -
Verify locally. Two quick gates cover doc-only changes:
just typos # spelling across the tree just book-build # the mdbook handbook still buildsjust book-buildcatches broken intra-book links and bad Markdown that a typo check won’t. Both run inside the dev container, matching what CI runs. -
Commit with a signed, Conventional Commit. Doc changes take the
docs:type:git commit -m "docs: fix ruby example in the notation chapter"Both requirements are enforced by hooks: the
commit-msghook rejects a non-Conventional subject, and the signing layers reject an unsigned commit. Scope is optional for cross-cutting doc edits; use one when the change is crate-local (e.g.docs(render): …). See Conventional commits for the accepted types. -
Push and open a PR. The PR title mirrors the commit subject (
docs: …). The PR template walks the checklist — keep it. CI re-runs the same gates you ran locally.
If a commit is rejected for signing, or a hook misbehaves, jump to Troubleshooting & gate recovery.
The inner loop (while you iterate)
For anything beyond a one-line fix, run a watcher in a second terminal so feedback is continuous instead of per-commit:
just watch # default check job — recompiles on save
just watch-lint # fmt + clippy on save
just watch-test # nextest on save
The watcher runs inside the dev container, so it detects saves against the bind-mounted source. See Development loop → Watch mode for the in-watcher keybindings.
How this differs from a parser change
A doc fix is intentionally cheap. A parser change is not: it lands a failing test first, then the fix, and extends every test layer the new shape touches. The contrast is deliberate.
| Doc / typo fix | New notation / parser change | |
|---|---|---|
| Touches | A .md file | Spec fixture → AST → lexer → renderer → invariants |
| Verify | just typos, just book-build | just test, just prop, just coverage |
| Commit type | docs: | feat: / fix: / perf: … |
| TDD | Not applicable | Red test first, then green — required |
Both paths share the same two hard rules: signed commits and Conventional Commits. Everything else scales with the size of the change.
See also
- Development loop — the daily loop and the full notation TDD flow.
- Troubleshooting & gate recovery — when a hook or gate blocks you.
- Testing strategy — what the parser-change path is verifying.