Runners¶
Two things run marola work without a person watching each step: a self-hosted GitHub Actions
runner on the maintainer's machine, and the mip-solve-perpetual skill working through a task
list. Neither starts on its own: a human runs the command.
Self-hosted Actions runner¶
Only one workflow may use it, marola-ml's GPU publish (marola-sea-publish.yml); the guard
below enforces that. The tools (tools):
runner-preflightchecks the machine can run the workflows: nix, python3, node, jq, curl, a usable docker, no passwordless sudo (a workflow that calls it would hang), disk, and with a token whether a runner carries thedependabotlabel. It queriesMAROLA_REPO.setup-runners Nregisters N runners, each in its own directory underMAROLA_RUNNER_ROOTnamedMAROLA_RUNNER_PREFIX-<i>, withMAROLA_RUNNER_LABELS, againstMAROLA_RUNNER_REPO, copied from the runner release inMAROLA_RUNNER_SOURCE, and installs them as user services (--foregroundprints therun.shcommands instead). One runner takes one job at a time, so N is how many jobs run in parallel. The registration token is fetched per run and needsgh.--statusand--removeinspect and undo it.gha-runner up|down|status|logsruns one registered runner fromGHA_RUNNER_DIRin the background after the preflight, logging to.tmp/gha-runner.log.uprefuses a second listener on the same registration;downasks the process table, not a pidfile, and stops every listener on the machine, thesetup-runnersclones included.tempswatches CPU and GPU temperature during a long training run, against each part's own throttle and shutdown points.
The guard. A public repo's self-hosted runner runs whatever a pull request asks it to.
workflow-runners [.github/workflows] fails when a runs-on: or reusable-workflow runner:
outside marola-sea-publish.yml names self-hosted or is an expression, or when that workflow
gains a trigger a pull request can reach (pull_request, pull_request_target,
workflow_call). MIP-0065 §5.2.
The variables are on config.
Unattended MIP runs¶
/marola-devkit:mip-solve-perpetual NNNN [NNNN…] works through MIP-NNNN.tasks.md one task at a
time: one branch, one PR, the repo's gates green before each commit, trailers as usual, just pr
to open the PR. With no argument it picks the easiest actionable Draft MIP and says so.
- One typed turn. It sets a
/goal(every row has an open PR or is logged blocked after two failures) and a/loop. The loop keeps that human-started turn going; it cannot start the skill. A cron or wakeup whose prompt is the slash command arrives as plain text, and the Skill tool refuses it (human-only). So a run lasts as long as the session and the usage guard allow, and the next start is a person typing the command. A cloud routine would survive a closed laptop, but is not assumed available. - No
ghlogin is not a stop. It still pushes, and appends each task's exactgh pr createcommand toGH_POST_MORTEM.md(gitignored, append-only) for a human to replay. - Usage guard, before every task. From the 5-hour and weekly
used_percentage/resets_at(via theheavy-usageplugin's status line; elsecost-split --estimateagainst a stated budget), it projects end-of-window use linearly, adds the next task's estimated cost, and proceeds below 75%/85%, finishes the current PR and stops below 90%/95%, and stops at once above.heavy-usage's own wind-down line wins when it is more conservative, but it is a prompt, not a block, and its data goes stale when no status line renders. - Model and effort. A mechanical task starts at low effort, a substantive one at default; a failure raises effort before any model switch, and a switch to a costlier model needs the budget check to clear and ends with that task.
- Checkpoint contract. Before a usage stop, the current task is either done (gates green, committed, pushed, PR opened or logged) or abandoned with a clean tree and one line in the report. Never half-built state.
- Stop when every row has a PR or failed twice, when the guard trips with the checkpoint met,
or at a decision only a human can make. It prints
GOAL_COMPLETE:orGOAL_FAILED:.
Merging stays a human act. The skill's disallowed-tools drop gh pr merge and
gh pr close, but that list is documented to clear after a message, so the real boundary is the
consuming repo's .claude/settings.json permissions.deny for gh pr merge, gh pr close,
gh stack merge, gh stack unstack and gh stack delete. Check it is there before a run.