Repository navigation
Wave 2: job chains, builds, energy, and five workloads that refuse to guess - #3
Merged
Merged
Conversation
Wave 2. Chains, builds, energy, and five new workloads — plus a migration
defect this wave surfaced.
depends_on makes a chain one enqueue. A job names ids it waits for, is inserted
'waiting', and is promoted when the last of them closes; a failed or cancelled
dependency fails its waiters down the whole chain rather than parking them
forever. A promoted spec can carry ${jobs.<id>.artifact} and
${jobs.<id>.metrics.<key>}, substituted whole so a metric stays a number. That
is build → install → ui-test without a script watching the queue.
preemptible lets a twelve-hour drain step aside for a two-minute install. The
beacon answers preempt:true only when a strictly higher-priority queued job
would go to the same claimant; the runner checkpoints, uploads, and posts
preempted:true, which requeues with params.resume_from and hands the attempt
back. Stepping aside never counts against max_attempts, because it is not a
failure. Equal priority does not preempt.
build on the machine agent closes the loop the artifact store was built for:
check out a ref, build it, publish it with the headers that stamp publish_seq,
and a later job asking for "latest" resolves to it. It posts pending at claim
under its own status context rather than sharing the collector's, because two
writers on one GitHub context means the loser's verdict silently disappears.
Energy is measured honestly or not at all. energy_method is declared in config,
never inferred; a shared plug reports the pool's draw and refuses to divide it;
the integration skips gaps rather than bridging them and returns null rather
than zero when it has fewer than two samples. What it reports is watt-hours at
the wall above a measured idle baseline, including charging — stated everywhere
it appears, because a wall measurement cannot separate the two.
The Evals page counts what it excludes. The plant-ID write-up's accuracy rode in
decode_tok_s because vision had no field of its own, and those rows cannot be
queried today; a page that quietly dropped them would repeat the mistake it
exists to prevent.
Five workloads arrive with their metric names, their views and their refusals:
speech-eval and embed-eval on the phones, vantage from wherever an agent sits,
locale-shots, app-soak and a11y-audit on the host, self-check on the hosts
themselves. Every one of them refuses rather than approximates where a platform
cannot answer: no PSS on an iOS simulator, no locale on a physical iPhone, no
bold text below Android 12, no in-device shaping on an unrooted phone.
Three bugs found by doing the work.
Swift's convertToSnakeCase splits on capitals and a digit is not one, so
recallAt1 encoded as recall_at1 — one underscore short of the declared name,
which means a metric that silently never arrives. Explicit CodingKeys now.
The old runSoak ran simctl against a physical iPhone's UDID, so a real-device
soak reported the process DEAD at every check.
And the mono repo migration left .gitmodules inside runner-android/, where git
never looks. The gitlink survived with no URL registered against it, so
`git submodule update --init` in a fresh clone silently found nothing to do.
Moved to the root with its path rewritten; verified it registers.
Verified: the collector's suite passes with 552 assertions, the machine agent's
110 tests pass, the Android unit tests and debug build succeed, and the iOS app
builds from a clean xcodegen run off project.yml with zero xcframework
references in the committed project.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The first wave in the mono repo, so it is one PR instead of three.
Job chains
depends_onmakes build → install → ui-test a single enqueue. A job names ids it waits for, is insertedwaiting, and is promoted when the last closes. A failed or cancelled dependency fails its waiters down the whole chain rather than parking them forever. A promoted spec can carry${jobs.<id>.artifact}and${jobs.<id>.metrics.<key>}, substituted whole so a metric stays a number rather than becoming a string.waitingis counted separately fromqueuedon the overview. A chain parked on a dependency is not work the fleet is failing to pick up, and folding the two would make a healthy pipeline look like a backlog.Preemption
A twelve-hour
draincan step aside for a two-minuteinstall. The beacon answerspreempt: trueonly when a strictly higher-priority queued job would go to the same claimant; the runner checkpoints, uploads, and postspreempted: true, which requeues withparams.resume_fromand hands the attempt back. Stepping aside never counts againstmax_attempts, because it is not a failure.build
Closes the loop the artifact store was built for. Check out a ref, build it, publish it with the headers that stamp
publish_seq, and a later job asking"sha256": "latest"resolves to it — verified end to end.Two findings corrected my plan:
report_to.github_statuswas never unfed. The collector already postssuccess/failureon every final row. The agent uses its own contextfleet-runner/buildrather than sharing one, because two writers on one GitHub context means the loser's verdict silently disappears. What it genuinely adds ispendingat claim, a state the collector cannot post because it does not know when work started.POST /artifactswithx-artifact-app/x-artifact-build/x-artifact-platformis what stamps a published build.Energy, measured honestly or not at all
energy_methodis declared in config, never inferred. A pool that omits it gets no figure.plug-sharedreports the pool's draw and refuses to divide it. The devices are not identical, they are not all busy, and a per-device number arrived at by division would be indistinguishable in storage from a measured one.Evals as a page
/api/evalsand an Evals page pivot eval sets by model and device, with a Markdown export. It counts and lists the rows it excluded, naming the metrics each one does carry. The plant-ID write-up's accuracy rode indecode_tok_sbecause vision had no field of its own; a page that quietly dropped those rows would repeat the mistake it exists to prevent.Five workloads that refuse rather than approximate
speech-evalandembed-evalon the phones,vantagefrom wherever an agent sits,locale-shots,app-soakanda11y-auditon the host,self-checkon the hosts themselves.Every one names its limits instead of filing a number that means something else: no PSS on an iOS simulator (only host RSS is available, which counts shared pages Android's PSS does not), no locale on a physical iPhone, no bold text below Android 12, no in-device shaping on an unrooted phone, no cold-start on iOS where
simctlreturns at process spawn rather than first frame.Two checks worth calling out because they catch a green run that measured nothing:
locale-shotsfails a device when two locales produce byte-identical screenshots. An app whose locale never reached it is otherwise indistinguishable from a correct run: the setting reads back fine, every folder exists, and every folder is in English.embed-evalasserts that two identical strings embed to cosine 1 and that two different strings do not. A constant non-zero vector passes the first check perfectly and scores a corpus at chance. This is the same class of failure as the iOS Simulator's all-zero logits tensor, which only a second device disagreeing ever caught.Three bugs found by doing the work
convertToSnakeCasesplits on capitals, and a digit is not one.recallAt1encoded asrecall_at1— one underscore short of the declared name, i.e. a metric that silently never arrives. ExplicitCodingKeysnow.runSoakransimctlagainst a physical iPhone's UDID, so a real-device soak reported the process DEAD at every check..gitmodulesinsiderunner-android/, where git never looks. The gitlink survived with no URL registered against it, sogit submodule update --initin a fresh clone silently found nothing to do. Moved to the root with its path rewritten, and verified it registers.Verification
assembleDebugsucceedproject.yml; committed project has 0 xcframework referencesKnown gaps, stated rather than implied
runner-ios/Frameworks/does not exist in the mono repo (the 854 MB xcframework is gitignored and did not come across), andrunner-android/third_party/llama.cppis an empty directory until the submodule is initialised. The newembed()entry points on both platforms are written against the same modern API the existing code uses, but need a real native build to confirm. Your old clones still have both if you want them locally.buildhas only ever runkind: "npm". The gradle and xcode command lines are unit-tested but have not driven a real compiler.dnctl/pfctlshaping path from Wave 1 remains untested and opt-in.a11y-auditon iOS needs a small XCUITest addition inrunner-ios/to print the accessibility tree; the parser is already here and the failure names exactly what to add.🤖 Generated with Claude Code