Executor protocol
Rules for the agent that executes this plan. They exist so that a sequence of small, verifiable steps adds up to the destination without drift.
Before any task
- Read
README.md, this file, andSTATUS.md. - Pick the lowest-numbered wave that has a task in state
todowhose dependencies are alldone. Pick any such task in that wave. - Read the task’s batch file section and every design doc the task cites. Read the files listed under Touches and their direct imports. Do not read the rest of the repository unless a step tells you to.
- Set the task to
in-progressinSTATUS.mdwith today’s date.
Branching and commits
- All work goes on the integration branch
experiment/redline, created once frommain. Do not open merge requests per task; the verifier reviews the whole branch at the gate. - One commit per task. The message is
<type>(<scope>): <summary>on the first line, a blank line, a short body, then the trailerPlan-Task: <id>.typeis one offeat,fix,test,docs,ci,ops,refactor. - No attribution trailers of any kind. No
Co-Authored-By. No generated-with lines. The only permitted trailer besidesPlan-TaskisClaude-Session:. - Before every commit:
npm testmust pass. If the task touched any.astrofile,client.js, anything underservice/, or anything undertest/fixtures/ortest/e2e/,npm run test:e2emust also pass (once T0.3 exists). - Commit the
STATUS.mdupdate in the same commit as the task.
Acceptance
- A task is
doneonly when every command under its Acceptance heading produces the stated output. Run them; do not reason about them. - Never change an expected value in a test to make it pass. Never delete or skip an existing test. If an existing test is wrong because the task changed a contract on purpose, the task says so explicitly; otherwise it is a bug in your change.
- Test names given in a task are exact. Use them verbatim so the gate can grep for them.
- Exact message strings live in
client.jsasREDLINE_MESSAGES. Tests import them; never retype them.
Blockers
If an acceptance command cannot be made to pass after two honest attempts:
- Set the task to
blockedinSTATUS.md. - Append an entry to
BLOCKERS.mdwith the task id, the exact command, the exact output, and what you tried. - Revert uncommitted changes for that task (
git checkout -- .andgit clean -fdlimited to the files you touched). - Move to the next eligible task. Do not try to work around a gate.
Freedom to refactor
This plan is a truth-seeking experiment, not a maintenance release. There is no refactor boundary. If a task is easier or more honest with a change the task did not foresee, make the change: move modules, change the Dockerfile, restructure the service, replace the identity layer, hold comments in an interim schema. The two things that must survive any such change:
- Propagation. Every reader action still ends in git or GitLab,
attributed to a person. An interim store is allowed only with a task that
propagates its contents down, and
STATUS.mdmust say what is interim. - Proof. The acceptance tests of the task, and every existing test, still pass or are deliberately changed in the same commit with the reason in the commit body.
Record every unforeseen refactor in the task’s STATUS.md note so the
verifier can find it.
What stays with the operator
Cloudflare, Tunnel, Access, DNS, host nginx, release channels and the GitLab host are operated by a human. Tasks marked (human) are theirs; you prepare files and record the evidence they give you.
Dependencies and style
- Pin devDependencies to exact versions. A dependency a task did not list is fine when the task needs it; name it in the commit body.
- Once T0.6 is done, run
npx prettier --check .,npx eslint .andnpx tsc --noEmitbefore every commit; CI runs the same three. - Match the existing style: tabs, single quotes, trailing commas, semicolons.
Status values
todo, in-progress, done, blocked, human (waiting on the operator),
deferred (moved out of the current stage by a plan amendment; the note says
where it went). An executor never sets deferred on its own.
Record the commit short SHA for done.