MotirBuilding in public
MOTIR · moooon
onMotir
You’re viewing a public project. Anyone can view it — no account needed. Sign in to submit, upvote, or comment on requests.View-only — you can’t edit work items
MOTIR-2612

A credential-free post-publish job must assert the PUBLISHED image's CLI carries every command the shipped docs name — today `sandbox-published` logs in and reads only `--version`

Done
Description

Repo: motir-core. One PR. Carved out of MOTIR-2611 by its 2026-08-10 re-plan — it was that card's AC 5, and it is the one deliverable of that card the correction leaves entirely intact.

The gap, measured on origin/main @ 816eb501

Three checks already guard the sandbox release. None of them reads what is INSIDE the published artifact on a stranger's behalf:

lane / jobwhat it askscredentialblind to
sandbox-images.ymlsandbox-published (:255)pull every digest, run motir --versiondocker/login-action (:261)asks as the PUBLISHER; --version is a number, not a command set
sandbox-images.ymlsandbox-public (:320)can a stranger pull it at allnone — by constructionsays nothing about CONTENT
sandbox-staleness.yml (MOTIR-2131)is main far ahead of the newest cli-v*none (reads git)never pulls anything

So the question "does the command the docs tell a reader to run exist in the image they are told to pull?" is asked by nothing. That is not hypothetical: MOTIR-2131 shipped a :claude whose CLI predated motir login while /docs/sandbox promised it, and it stayed green for five days.

The source of truth already exists and is machine-readable

lib/apiDocs/sandbox.ts annotates each step with the commands that step tells the reader to run — cliCommands: ['login'] (:267), ['link'] (:289), ['doctor'] (:310). That array is the expected set; deriving the assertion from it means a future step that names a new command is covered the day it is written, with no second list to keep in sync.

Do this

  1. Add packages/cli/sandbox/smoke/assert-commands.mjs — same shape as its two siblings (assert-public.mjs, assert-current.mjs): zero dependencies, runnable from any shell, a header comment stating why it exists. Given an image ref, it resolves the manifest, runs the image's motir help, and asserts every command in the expected set appears as a top-level command. Take the expected set from SANDBOX_GUIDE's cliCommands so the docs remain the single source.
  2. Add a sandbox-commands job to .github/workflows/sandbox-images.yml, gated on publish like sandbox-public is, that runs it against the just-published tags. It holds NO credential: no docker/login-action, no packages: permission, no Authorization header — the anonymity is the point, and assert-public.mjs's header explains why at length. Model the job on sandbox-public, not on sandbox-published.
  3. Run it control-first, exactly as assert-public.mjs does: the probe must be shown able to FAIL (a known-absent command name) before its pass means anything.
  4. Unit-test the script (packages/cli/test/sandboxCommands.test.ts, mirroring sandboxCurrent.test.ts) and extend the workflow-wiring guard (packages/cli/test/sandboxCi.test.ts) to assert the new job carries no login step and no packages: scope — the guard is what stops a later edit from quietly re-crediting it.

Acceptance criteria

  1. packages/cli/sandbox/smoke/assert-commands.mjs exists, exits 0 when the image's motir help lists every expected command, non-zero when one is missing, and sends no Authorization header on any request.
  2. The expected command set is DERIVED from lib/apiDocs/sandbox.ts's cliCommands, not restated — adding a cliCommands entry to a guide step changes what the job asserts, proven by a test.
  3. A sandbox-commands job exists in .github/workflows/sandbox-images.yml, runs only when publish is true, and contains no docker/login-action step and no packages: permission — asserted by packages/cli/test/sandboxCi.test.ts, not only by reading the YAML.
  4. The script runs its control (a command name known to be absent) and reports INDETERMINATE rather than PASS if the control does not fail.
  5. packages/cli/test/sandboxCommands.test.ts covers: all-present, one-missing, control-broken, and malformed motir help output.
  6. The PR body shows the script run against the CURRENTLY published ghcr.io/moooon-b-v/motir-sandbox:claude with its output quoted — it must PASS today (0.2.0 has login), which is what makes it a regression guard rather than a bug report.

Context refs

  • .github/workflows/sandbox-images.ymlsandbox-published (:255, logged in) and sandbox-public (:320, credential-free); the latter is the model.
  • packages/cli/sandbox/smoke/assert-public.mjs — the header explains the publisher-vs-consumer distinction and the control-first discipline; copy both.
  • packages/cli/sandbox/smoke/assert-current.mjs + .github/workflows/sandbox-staleness.yml — the script-plus-thin-caller arrangement to mirror.
  • packages/cli/test/sandboxCurrent.test.ts, packages/cli/test/sandboxCi.test.ts — the unit-test and wiring-guard patterns.
  • lib/apiDocs/sandbox.tscliCommands at :267 / :289 / :310.

Out of scope

Cutting any tag — that is MOTIR-2611's sibling release card. Changing the guide's prose — that is MOTIR-2611 itself.