main
John Lauer docs: user skills + README catch up with 0.2.35-0.2.38 (modal orphan, never half-answer a dialog, cleanup_libs/set_para… 4b57946 1mo ago

name: altium-bridge-test description: "DEVELOPER-only skill (user-invocable:false). The standard end-to-end verb battery for the Altium bridge: one runner that exercises EVERY altium_* verb in the live catalog against a real Altium on a real Windows box, on scratch copies of three test parts (leaded SOIC-8, QFN-24 with 9 heatsink vias, BGA-36). Coverage-enforced: a new verb with no entry turns the run red. Run it before every promotion to the public tier, after any change to the agent template or agent.rs/dialogs.rs, and once on every new Altium build or machine. Trigger words - test the altium bridge, altium verb test, verb battery, test all altium verbs, regression test altium, pre-release check altium, is altium-bridge safe to promote, run_verb_tests." user-invocable: false

The altium-bridge verb battery

dev-skills/altium-bridge-test/run_verb_tests.py drives every altium_* verb the LIVE bridge declares (altium_describe.allVerbs) through adom-bridge --target <box>, the way a user's AI does, and prints one line per verb plus a summary. It exists because of 2026-09-20: 0.2.20 looked finished on the machine it was written on, and the first verb tried on a second machine parked Altium on a script breakpoint. One machine and the verbs you happened to touch prove nothing.

python3 dev-skills/altium-bridge-test/run_verb_tests.py --target <machine> --thread "<your ai thread>"
    [--only verb1,verb2]   # re-run a few
    [--keep-open]          # do NOT close Altium at the end (default closes it: see Seats)
    [--json report.json]

Exit code 0 only when there are no FAILs and no uncovered verbs.

What a result means

Outcome Meaning
PASS ok the verb answered success:true (and its check, when the entry has one: pin counts, 9 via holes, 3 parts placed, netWorks:true)
PASS refused the verb answered success:false WITH a named reason. For verbs whose real inputs the battery does not have (an OutJob, an IntLib, an Altium 365 token, a project) a fast, named refusal is the contract under test
SKIP never run, reason printed. Exactly ONE verb: a365_logout, which really does sign the user out of Altium 365 on a configured machine and has no read-only mode
FAIL a timeout or transport error (the bridge wedged), success:false with NO reason, the wrong outcome for the entry, or a dialog left on the user's screen after the verb (checked after every verb with altium_check_dialogs)

The dialog check is the point of the whole thing: John's rule is that a dialog the user has to dismiss is a defect. A verb that "worked" and left a box up fails the battery.

Safety

  • It only ever touches scratch copies in <Documents>/Adom/altium-bridge-battery on the target (Battery.SchLib, Battery.PcbLib, battery.SchDoc, battery.PcbDoc), rebuilt from fixtures/ on every run. It never opens or saves a user document.
  • uninstall runs with dryRun:true only; ensure_licensed runs with defaults only (never signs out, never starts a trial).
  • Seats. An Altium 365 workspace has a fixed number of Author seats. An Altium left running on a test box starves every other machine into view-only mode, where new documents silently refuse to open (view_only_no_author_seat). The battery therefore ENDS with agent_stop + close. Use --keep-open only when you are about to keep working on that box, and never run the battery on two machines of the same workspace at once.
  • selftest_script_error flashes one Altium error box for under a second. Do not run the battery while someone is mid-edit in Altium on that machine.

The skip list is one verb, and stays that way

It used to hold nine. They were skipped when the only Altium lived on a machine someone was working on, and the reasons did not survive contact with a dedicated test box (John, 2026-09-21: "none of those verbs seem unsafe to me?" — he was right about eight of them). Each one either reads state or refuses by name BEFORE it touches anything, and proving that refusal is the whole point of a battery:

Verb What it actually does here
demo stage:0 is a BACKGROUND beat with a screenshot. Only demo {live:true} takes the foreground, and that is not run
foreground raises Altium on a box the runner already proved idle — run it last in phase 5
setup_trial with Altium CLOSED (phase 0) it reports the next action; the stage that could start a trial needs a signed-in, UNLICENSED Altium running
connect_workspace with Altium closed it is a clean altium_not_running refusal, so the workspace picker never opens
license an unknown action proves the dispatch and the named refusal; use_license / start_trial / sign_out are deliberately NOT exercised — the seat is shared and single
install_extension Altium is running by phase 2, and the verb refuses in that state (it holds AdomBridge.dll open)
a365_configure validates before it writes: no workspace = a named refusal, config untouched
a365_oauth_begin validates before it opens anything: no workspace configured = a365_workspace_required

Before you add a tenth skip, check whether the verb has a read-only mode, a safe stage, or a state in which it refuses by name. install (stage preflight), uninstall (dryRun) and ensure_licensed (defaults) all earn their coverage that way. A skip is an admission the contract is untested.

Adding a verb

Add the verb to src/bridge.json + verbs.rs as usual, then add ONE entry to matrix() in the runner: the phase, real args against the scratch fixtures, and expect (ok / refuse / any / skip with a SAFETY reason). Prefer ok with a check. Use refuse when the battery cannot supply the input; never use skip to dodge a verb that is merely awkward to test.

Fixtures

fixtures/Adom_Test.SchLib + .PcbLib are built from the TST_*-fp.json / TST_*-sym.json create files beside them (adom-footprint + adom-symbol, then adom-lbr import-kicad and adom-lbr assemble-altium --no-dedupe). Three deliberately different packages: gull-wing leads, an exposed pad with heatsink vias, an alphanumeric ball grid. Rebuild them when adom-lbr's Altium writer changes, so the battery also covers that lane.

Before promoting to the public tier

  1. Battery green on at least TWO Altium builds (today: Altium Designer 26.x and Altium Designer Develop behave differently on script errors; see outcome on selftest_script_error).
  2. Battery green on a machine that is NOT the one the change was written on.
  3. Ideally once on a from-scratch Windows install (the install lane is the first thing a public user touches and the battery assumes Altium is already there).
  4. Then run it once more against the PROMOTED artifact, installed from the PUBLIC manifest the way a new user would (bridge_install {manifestUrl: ".../adom-bridge-altium-manifest.json"}). Testing the insiders zip proves the build; only this proves the thing strangers actually download. Done for 0.2.39 on 2026-09-21: 121 pass / 0 fail / 1 safety skip.

Promotion itself is never the AI's call. release.py --promote refuses without the human's own words (--attest, 8+ chars, no placeholder). Quote them verbatim — they are the audit trail for why the public tier moved.

Someone else may be shipping this bridge too. Colby works on it in parallel. adom-wiki repo clone the page before editing and push only the files you changed, or you will revert releases that are newer than your tree. Check adom-wiki repo log adom/altium-bridge and the insiders version.json first: a tier can be deliberately rolled BACK (he held 0.2.40/0.2.41 there on 2026-09-21 for want of a licensed box), and that is not a bug to "fix".