I build software with coding agents under positive control: every change planned, gated, reviewed and shipped the same way.
Positive control is an air traffic term. No aircraft moves without explicit clearance. Here, no change ships without one. One feature is on the scope now, flying the whole route. Pause it and step through at your own pace.
Log
Gates cleared 0 / 4Artifacts 0Nothing completed yet. Entries appear here as each step finishes.
- 15 Deploy done Shipped.
- 14 Merge done Merged. Issue closed, board Done.
- 13 G4 · Comments resolved cleared Clear to merge.
- 12 /pr_comment_resolver done Every conversation answered and resolved.
- 11 Human review done Review comments posted.
- 10 G3 · Deterministic gates cleared Every gate green.
- 09 /pr_submit done PR open. Board moves to Up for Review.
- 08 /pr_review --local done Self-review finished in two rounds or fewer.
- 07 /implement done Code and tests committed, one logical unit at a time.
- 06 G2 · Plan approved cleared Approved. Board moves to In Progress.
- 05 /task_plan done Task file, plan and branch ready.
- 04 G1 · Design approved cleared Approved. Sub-issues created on the board.
- 03 /segue → /segue_merge done Findings merged back. Workstream clean.
- 02 /feature_plan done Design doc drafted in docs/plans/.
- 01 /pick done Unshaped work, routed to design.
The thread
Every session starts here. It surfaces ready work in priority order and routes it by shape and board state.
Routing
- epic or unshaped
- Goes to /feature_plan to be designed and split.
- sized Todo issue
- Goes to /task_plan.
- In Progress
- Resumes in /implement from the task file.
- Blocked
- Shows the blocker instead of starting work.
Checked at the door
- sizing
- An issue fits when its acceptance criteria fit in five testable bullets. More than that is an epic.
The agent explores the codebase and drafts a design doc. Nothing is an issue yet. The doc is a proposal; issues are a commitment.
Produces
- docs/plans/<slug>-design.md
- The design doc, with the slice list.
- Status: Draft placeholders
- Stubs for the system and procedure docs this feature will need.
- feature/<slug>
- The feature branch name, written into the doc when there is more than one slice.
Valves, off the chain
- /grill
- Interviews the design in rounds until no question is left open.
- /research
- Reads primary sources and writes one cited note.
- /domain_model
- Settles a term doing two jobs. Writes the vocabulary and an ADR.
# Home thread — design Status: Approved (G1, 2026-10-03) Date: 2026-10-03 Slug: home-thread **Feature branch:** feature/home-thread The home page of markevans.io is one real feature moving through the delivery chain, played as a self-running, steppable sequence of flight strips on a git spine, with a radar scope as the console.
A question comes up that would derail the design. I open an isolated thread, settle it there, and bring back only the findings.
The cycle
- /segue
- Opens the thread and parks the workstream.
- /segue_resume, /segue_close
- Picks the thread up later, or finishes it.
- /segue_merge
- Returns the findings to the plan. The discussion itself stays out.
- /segue_kill
- A dead end. Records why it died and merges nothing.
While designing this page I set it aside for one question: what shows a change moving under control? I looked at the paper flight strips air traffic controllers pass between bays, and at how git draws a branch history. What came back: each step is a strip of ruled cells (Ctl, Bay, Rmk), the board's four states are the strip bay, and the sequence runs down a git graph that forks, merges and loops.
I read the design doc and approve it, send it back, or cut it down.
Gate
- blocks
- Issue creation.
- cleared by
- Mark.
- then
- A parent issue and sized sub-issues land on the board as Todo.
We drew four designs and compared them side by side, then took elements from two of them to agree on the initial design.
One sized issue becomes a task file, an implementation plan and a branch. The issue number threads through all of them.
Produces
- .llm/tasks/<n>_<slug>.md
- The task file. Any fresh session can resume from it.
- prefix/<n>/<slug>
- The branch.
- Base: feature/<slug>
- Recorded so every later command diffs against the right base.
- draft PR to main
- Opened with the first slice, so CI runs on the feature from day one.
- last action: the walkthrough
- The steps this change adds to the reviewer walkthrough, or one line on why it has nothing to show.
How rules are selected
- CLAUDE.md
- States the non-negotiables in short form and points at the index.
- docs/rules/INDEX.md
- Routes by the paths this task will touch.
- frontmatter: applies_to, triggers
- Path globs and keywords decide which rules match.
- frontmatter: tokens
- The read cost of each rule, so the plan loads only what it needs.
- frontmatter: modes, current_state
- Which kind of app the rule ships to, and whether this app matches it or diverges.
- docs/rules/SYMPTOMS.md
- Read only when routing by path missed.
registries · tailwind · tailwind-build · tokens-only · color-means-role · typeface-means-speaker · content-is-files · no-invented-content · testing
I read the implementation plan before any code exists.
Gate
- blocks
- Any code.
- cleared by
- Mark.
The first slice forecast about 1,600 lines across 33 files, past the 25-file limit. I split it: tokens, loader and gates first (#14), the static page second (#21), so nothing user-facing changed until the foundation was in.
The agent loads the task file and builds. Tests first, one commit per logical unit.
Rules in play
- path routing
- Each file edited routes to its rules through the index, the same way planning did.
- right and wrong pairs
- Every rule shows both versions side by side, so the agent sees the specific mistake to avoid.
Enforced while writing
- bin/hooks/post_edit
- RuboCop on Ruby files; a bracket-class check on Slim templates.
- .claude/settings.json deny list
- Refuses force-push, reset --hard, clean -fd and reading secrets.
- bin/hooks/session_end
- Flags draft doc placeholders left behind at the end of a turn.
Valves
- /diagnose
- A bug with no failing test. Builds a tight loop first, then hunts.
- /test_fix
- The suite is red. Triage and a local fix.
- scope escape
- Forecast passes 1,500 added lines or 25 files: stop, split a sub-issue, land this slice clean.
19 files changed, 1,040 lines added, 35 removed
Before any PR exists, an agent with a new context reviews the branch. It has not seen the implementation conversation, so it reads the diff cold.
Rounds
- round 1
- Full review of the diff. The implementer verifies each finding.
- disputes
- I rule on them.
- fixes
- Each lands with a test that failed first.
- round 2
- Reviews only the fixes from round 1. There is no round 3.
Also here
- /rails_code_review
- Reviews the branch against this stack’s conventions. Posts nothing.
- /run_lint
- RuboCop and bin/flay on the diff, fixed locally.
I invoke this one myself. Opening a PR has an audience.
In order
- bin/rails db:queries
- Regenerates the query ledger. Unreviewed queries get their plan printed for review.
- local suite
- Must be green before anything is pushed.
- docs
- Every placeholder is completed or deleted. The index is checked for duplicates and dead links.
- /qa_walkthrough
- A user-facing branch carries a reviewer walkthrough: a script in script/qa/ that builds the data, drives a browser to each page the branch touched and spotlights what changed. Skipping it is my call, and the PR body says why.
- PR
- Title carries the issue number. Body carries Closes #n.
An agent will eventually miss a rule, so the rules a machine can check are checked. None of these depend on the agent cooperating.
bin/gates · pre-push hook, again in CI
- rejected patterns
- Banned tokens in app/ and the Gemfile.
- app/ directory budget
- No new directory under app/ without an ADR.
- command mirror
- The Cursor commands match the Claude commands.
- foreign keys
- Every foreign key leads an index.
Their own CI jobs
- bin/flay
- Fails on Ruby this PR copied that its base did not have.
- db:queries:check
- Every SQL shape the suite emits has a reviewed line in db/queries.yml.
- ci.yml
- Ruby and JS security scans, RuboCop, the test suite.
- bin/lint-docs
- Doc checks, including real hosts or IPs leaked into a doc.
- api:contract:check
- API mode only: the OpenAPI contract agrees with the request tests.
Inside the test run
- Bullet
- Raises on an N+1 query.
- Lighthouse CI
- Accessibility.
- Slowpoke
- Suite speed.
Gate
- blocks
- The push, and the merge under branch protection.
scan_ruby ✓ · scan_js ✓ · lint ✓ · gates ✓ · test ✓
A person reads the PR. On a feature built in slices, each slice is read by /pr_review from a fresh session, and the feature PR is read by a human against the design doc.
Tools
- /pr_review
- Full-context diff review. Posts comments.
- /pr_qa
- A guided manual QA pass with a structured report. It starts from the walkthrough's screenshots, and the hands-on part begins where the walkthrough stops.
- /pr_fix_ci
- Reads a failed run and fixes it locally. Pushing the fix is mine.
The review shown here is slice #23's: eight comments from /pr_review, run by the agent in a fresh session. Each was answered with a fix commit. One added content:check, the gate that held this page until its content was filled.
Each review comment gets a fix or a reply, then its conversation is resolved. Other people are reading those threads, so I choose when this runs.
The cycle
- fix or reply
- One per comment.
- changes requested
- The board returns to In Progress until they are addressed.
- /resolve_conflicts
- Finishes a conflicted merge and regenerates what must not be hand-merged.
Nothing merges with an open conversation.
Gate
- blocks
- Merge.
- cleared by
- The reviewer and Mark.
One click. Everything after it is automation.
Automation
- Closes #n
- Closes the issue on merge.
- board
- The closed item moves to Done.
- branch
- Deleted automatically.
- feature branch
- After each slice, main is merged back in so the final merge stays small.
Merged to main in #22 on 2026-10-04, then held: the production build runs `content:check`, and this page's own placeholders failed it until each one was filled. Then `kamal deploy` to markevans.io.
After
- deploy
- Kamal 2, as the template configures it.
- /update_docs
- An on-demand deep pass over the docs that keeps the index honest.