The onboarding conductor
Who this page is for
This is the page you hand to an agent, not the one you read yourself. For the operator's view — the prompt to paste and the questions you will be asked — see One-prompt install.
The canonical copy lives in the repository
docs/en/onboarding.md in the M8Shift repository is the authority, and a hermetic test extracts its command sequence and executes it. This page mirrors it so that an agent has a public URL it can actually fetch. If the two ever disagree, the repository wins and the divergence is a defect worth reporting.
The governing principle
No guessed path, no silent default, no unverified success.
- Every location is asked, never assumed. A guessed path is paid for later: a lane whose runner lived in a temporary worktree died seven days after a choice nobody was warned about.
- Every question names its recommended default and what the choice engages, not merely what it does.
- An absent prerequisite is a finding stated before the first write, never a mid-install failure.
- A launched lane is reported by what was verified on disk, never by what was started.
Step 0 — the terrain, before anything is written
Run the read-only probe. It never writes, and its exit code gates everything after it:
python3 -B scripts/onboard-probe.py --json- Exit 0: proceed.
- Exit 3: stop here. Report every
blockingfinding to the operator verbatim, with the remedy the probe names. On Windows without WSL and without Git Bash: install WSL (preferred) or Git for Windows, reboot if WSL asked for it, run the probe again. Nothing has been written — say so. advisoryfindings are reported but do not block. A missinggitstill leaves the core install viable through the Python fallbacks.
If the probe is not available to you
The probe ships inside the repository, and the public mirror is currently behind the release that added it. If you cannot fetch scripts/onboard-probe.py, do not skip the step — establish the same facts by hand and report them before writing anything:
- the operating system;
- on Windows only, whether WSL or Git Bash is present — if neither, stop and say so;
python3 --versionand whethergitis present.
State clearly that you checked by hand rather than with the probe, so the operator knows which instrument produced the answer.
Step 1 — where the instance lives
The kit installs per instance, so the install directory and the instance directory are the same one. That makes this a single question:
"Where does this instance live?"
Recommended default: a directory named after the instance, beside the project it will coordinate — never inside a temporary or generated tree. What it engages: the journal, the kit scripts, the runtime sidecars and the turn history all live there, and moving it later is a first-class relocation, not a rename.
Ask now as well:
"Will any agent run headless?"
Recommended default: yes. It installs the reference lane runner with the kit, manifest-verified like everything else. Answering no keeps the kit smaller and the runner can be added later by re-running the installer with the flag.
Then run the installer against the answered path, and without its default init — otherwise it creates a default-roster instance before the operator has answered a single roster question:
bash install.sh --dir "<ANSWERED_INSTANCE_DIR>" --no-init --with-runnerReport what the installer verified — checksum lines, capability lines — not merely that it exited zero.
Step 2 — a shift now, or stop here
"Configure a shift instance now, or stop after the installation?"
Recommended default: configure now. What stopping engages: the operator can run this document again from step 3, and nothing is lost. If they stop, the directory holds the verified kit and no instance — no journal, no roster, nothing initialized. Say exactly what remains to be done by hand, and end.
Step 3 — name the instance and initialize
"What is the instance named?"
It engages the relay's identity in every status surface and notification. Initialization runs inside the answered directory — there is no directory flag, the kit initializes where it lives. Report the working directory you are about to use before running it:
cd "<ANSWERED_INSTANCE_DIR>" && python3 m8shift.py init --name "<INSTANCE_NAME>" --agents "<ROSTER>"Step 4 — roster and modes
"Which agents compose the roster?" and, for each one, "Interactive, or headless with a runner?"
What headless engages: a resident listener polls the relay and spends that provider's budget on the agent's turns. Interactive engages nothing resident.
cd "<ANSWERED_INSTANCE_DIR>" && python3 m8shift-runtime.py providers initThen write each headless agent's entry with values the operator confirmed — provider, a real accessible model id, and the runner argv — and read the entry back to them before saving. The product fails closed here by design: a lane never launches on an unset or invalid model pin. Verify before launching anything:
cd "<ANSWERED_INSTANCE_DIR>" && python3 m8shift-runtime.py providers checkStep 5 — launch the lanes, and verify them
If at least one agent is headless:
"Launch its lane now?"
Recommended default: yes. What it engages: a resident process on this host, surviving this conversation.
cd "<ANSWERED_INSTANCE_DIR>" && python3 m8shift-runtime.py listener start --agent "<AGENT>" --provider
cd "<ANSWERED_INSTANCE_DIR>" && python3 m8shift-runtime.py listener status --agent "<AGENT>" --jsonThe status document must show ALIVE, a resident process, and this instance's relay root for every launched lane. Report those exact fields.
The exit code is not the verification
listener status exits zero even for a dead lane. The ALIVE-and-resident evidence is the proof. A lane you started but did not observe is not a lane you report as running.
If no lane is launched, state what the operator runs later — the two commands above — and end cleanly. Launched lanes stay resident after this conversation ends. That is their point.
Keeping the protocol current
An instance created months ago runs a version nobody checked, and that stays invisible until someone looks. Make looking cheap:
python3 -B scripts/onboard-probe.py --update-check --target "<INSTANCE_KIT_DIR>" --source "<RELEASE_SOURCE_DIR>"- Exit 0 (
current): the installed core's bytes hash to the digest the source manifest pins. Equal version strings alone never certify currency — a stale or tampered core at the right version passes a string compare and fails this one. - Exit 3 (
drift): report the detail, version drift or same-version bytes that no longer match the manifest, and offer the documented upgrade — pause, update, doctor, checksums, resume — run only on the operator's explicit yes. - Exit 3 (
unverifiable): the named source has no readable manifest entry. The probe refuses to certify currency without byte evidence. Say so.
Both paths are local and named explicitly. The probe never reaches for the network on its own, and fetching a fresh release source is itself a choice the agent asks about.
The nominal sequence
Placeholders in angle brackets are the operator's answers.
python3 -B scripts/onboard-probe.py --json
bash install.sh --dir "<INSTANCE_DIR>" --no-init --with-runner
cd "<INSTANCE_DIR>" && python3 m8shift.py init --name "<INSTANCE_NAME>" --agents "<ROSTER>"
cd "<INSTANCE_DIR>" && python3 m8shift-runtime.py providers init
# the agent writes the operator-confirmed provider entry (step 4)
cd "<INSTANCE_DIR>" && python3 m8shift-runtime.py providers check
cd "<INSTANCE_DIR>" && python3 m8shift-runtime.py listener start --agent "<AGENT>" --provider
cd "<INSTANCE_DIR>" && python3 m8shift-runtime.py listener status --agent "<AGENT>" --json