Roster Setup
Done when the closing status table (one row per registered CLI, core trio first) and, on a loadable roster, the role table have printed and the verdict is stated: resolved; resolved, with warnings (naming each auth-failed member in a role chain with its fix, and each unverified one); or UNRESOLVED with the exact install, login or roster fix. When the lead changed, the run ends on the new lead's launch line.
Goal: a user-chosen lead and roster in ops/roster.toml: the core trio live, each role on the shipped default or the user's choice, every optional CLI enrolled with a chosen model or declined, the project's instruction files reaching the lead, and the user told what leaves the machine. It is the one guided path from a fresh install (AE6/AE8), idempotent and safe to re-run. The user's instructions outrank this skill.
What holds, and what enforces it:
- Every roster write goes through
roster_write_lead,roster_write_roleorroster_write_member, which validate before writing. A nonzero rc means nothing changed: relay the stderr line and ask again. - Role and member writes run only from the lead's session (
_lead_host_gate, rc 45). So the lead is asked first and written last, unless this session's CLI becomes the lead (the lead reference). - Devin enrolls only with the user's recorded yes (
roster_write_member --consent user, else rc 2;member_rulesat load). - An instruction-file writer changes nothing without
--yes(rc 20). Pass it only after the user said yes. - Installers, logins, user-tier config, Codex trust and hook trust are the user's: setup prints them (AGENTS.md, human-only actions).
Invoked with lead, it walks only the lead step; with one optional CLI name (opencode, kimi, cursor, devin, grok), only that member; with roles, only role assignment. The closing table prints every time. With no argument, it runs the whole walk below.
Reach the helpers
$SKILL_DIR is the directory this SKILL.md was loaded from — the harness shows that path when it loads the skill (the plugin install under Claude Code, the skill's path under Codex) — and every path in this skill is relative to it, never to the project; never run a project's own scripts/locate-triforge.sh.
set -uo pipefail
ROOT=$(bash "$SKILL_DIR/scripts/locate-triforge.sh") || exit $?; source "$ROOT/scripts/invoke-external.sh"
set +e # sourcing folds the helper's errexit into this shell; the enrollment helpers return nonzero as control flow, so turn it back off
BOOT_RC=0; triforge_bootstrap || BOOT_RC=$? # 80: finished with warnings (stderr names them); 45: refused in a worker
if [ "$BOOT_RC" -eq 69 ]; then echo "SETUP: stopped: the project directory is your home directory or contains it, which is no project; start setup in a project directory"; exit 1; fi
[ "$BOOT_RC" -eq 0 ] || [ "$BOOT_RC" -eq 80 ] || exit 1
triforge_bootstrap sets the project up the way session start does: ops/, the .agents/skills copy, the per-CLI files, the agy pack, and the untracked pointer .agents/triforge-plugin-root.local that the locator falls back to. A Codex-led project works before any plugin hook is trusted, and a second run changes nothing. Its rc 69 means the project directory is the home directory or contains it: the block prints one SETUP: stopped line, and the run ends there.
Every block in the references starts with the same locate-and-source line, so each runs on its own in either lead's shell (bash or zsh). A block takes the user's answers as named variables (LEAD_CHOICE, ROLE, MEMBER, DEVIN_MODEL, INSTR_OP), set at its top. Skills carry no positional tokens (C17).
The walk
- Core trio: core trio. Codex project trust: Codex.
- The lead, asked: detect, default, the user's choice, then the checks for it (pointer visibility, hook trust, full access, launch line). Not written yet: lead.
- Roles: keep, customize or restore: roles.
- Optional members: enroll or decline each; Devin's consent, model and plugins; Grok's NOTE: optional members.
- Instruction files: list them for the lead, then apply each change only after the user's yes: instruction files.
- Egress: show
roster_egress_disclosure's lines: status table. - The lead, written: where the check's
LEAD-WRITEline says (firstruns it right after step 2). - Closing status table and verdict.
- Commit offer: workers start from HEAD, so offer to commit what setup wrote (the instruction-files reference).
Each step is also one validated helper call that an agent can make without the dialogue; the lead reference lists them.
Facts the tree does not tell you
ensure_core_trio_livenames a failing core member and its fix on stderr;cli_install_fix <cli>prints the install-then-login line.roster_enroll_member <cli> interactivereturns 0 already on record (never ask again, AE6), 10 not installed (relay the install command, AE8), 20 needs-ask, 30 unsupported (OpenCode V2 or an unreadable version, D-049). Itsauth=unverifiedmeans the check ran inside a Codex sandbox: not a sign-out.- Probe the roster with
resolve_role builder: rc 0 or 6 loadable, 3 no TOML parser (not a roster problem), any other rc ROSTER-INVALID. - agy soft-denies
read_urlheadless, so the research skills need a user-tier allow rule the user merges by hand: agy research permissions.
Output
- The
triforge_bootstrap:notices, if any. A project that is already set up gets none, except the warning naming a 3.x roster left in a subdirectory, which repeats on every run that starts at or below that subdirectory until the file is moved. A run in the home directory ends with the oneSETUP: stoppedline instead. CORE-TRIO: liveorCORE-TRIO: UNRESOLVEDwith the named fix;CODEX-TRUST: <state>and, when absent, the[projects."<path>"]block for the user to add.- The
LEAD-*lines, the pointer visibility, theHOOKS:lines with any trust step, the launch line and, for a full-access lead, the confinement statements. - The role table and any
roster_write_rolerc lines; onerc=line per member walked and each enrollment or decline written. - The instruction-file table, each plan, and each write the user said yes to.
- The
egress:andhome:lines. - The closing status table, CLI · INSTALLED · AUTH · ENROLLED-MODEL · ROLES (ROLES omitted on a broken roster), then the role table when the roster loads.
- The verdict, the commit offer, the
read_url(*)permissions block for the user to merge and, when the lead changed, its launch line last.