`agentic-agents-core-models` carried the 36s scaled estimate while measuring
56.3s (n=6, p90 58.6s) across 260 compact jobs on 2026-08-16. Packed beside
`agentic-agents-core-runtime-hosted-1` (60.4s) it built the only bin running
>=1.25x its prediction: compact-large-19 ran 122s of work priced at 88s, which
made it the most frequent slowest job in the workflow.
Add the measured value to the hybrid hint map, which already exists for exactly
these Blacksmith-specific observations. The packer then separates the two, and
the tallest actual compact bin drops 122s -> 109s with no bin above 1.25x and
no change to row counts (48 push / 56 pull-request), so this costs no extra
runner registrations.
Two things deliberately left alone. The `agentic-gateway-core-3` 140s pin looks
like a 2x over-prediction against its 68.5s median, but run 31938297538 really
did take 138.0s (109.03s + 28.98s across its two configs) on a run whose fleet
slowdown factor was 0.98 -- a genuine tail on a healthy run, so unpinning it
would rebuild a >200s bin whenever the tail lands. Every other bin already sits
within 1.24x of its prediction.
The replaced guard pinned one bin arrangement (`runtime-hosted-1` not sharing a
job with `agents-core-tools`) with no stated failure mode, so any honest refit
broke it. It now asserts the property that comment was reaching for -- both
weight sources survive rebalancing under the body ceiling -- plus the specific
regression this fixes. Mutation-checked: dropping the hint fails the new guard.
Measurement note for the next refit: sum a shard's per-config Duration lines
before taking a median. Pooling them reads as a large over-prediction that is
not there.
`checks-windows-node-test` ran the whole 222-266s Windows body in a single
Blacksmith lane, so every run that scheduled it finished at 287-334s against a
~180s plateau. Measured across 45 canonical main runs it was the wall in all
seven runs that triggered it (324-519s vs a 210s median elsewhere).
Blacksmith's Windows class admits exactly 2 concurrent jobs (run 31865243804),
which is why the earlier 3-part split failed: its tail queued 211s behind a
finished part. Pin the split width to that measured capacity instead. The two
parts are balanced by measured per-project wall time -- part 1 keeps the old
list 1 plus list 2's non-infra projects (~108s), part 2 takes list 2's infra
project plus the old list 3 (~112s) -- so both land near 113s and the lane
stops setting the wall.
One canonical partition now serves every backend; the github-mode/dispatch
3-part variant and its separate file inventory are gone, along with the
single-lane `task: test` case and the per-row `matrix.runner` the collapsed
lane needed. The complete 65-file Windows inventory is preserved exactly, and
the guards pin the width on both sides (mutation-checked: restoring 3 parts
fails the matrix guard).
* refactor(ui): align approval page with redesigned approval card
Follow up #124767 by carrying severity accents and identity chips onto the standalone approval page.
Remove the unused execApproval.labels severity, plugin, and agent keys.
* refactor(ui): keep approval-page helpers in repo idiom
Reverts drive-by rewrites from the build pass: restore the exhaustive
decisionLabel switch (the ternary lost the never check), the early-return
appliedDecisionMatches, explicit gatewayCode comparisons, and align the
severity mapping shape with the sibling exec-approval-card.ts.
* test(ui): type approval severity fixtures
* test(scripts): wait for a parseable descendant pid in Bun smoke cleanup
The readiness gate only required the descendant pid file to exist, but
existsSync can observe it between open() and write(); a loaded runner
then reads an empty file and the integer assertion flakes (seen on PR
#124816 CI run 31971191374). Readiness now requires parseable content.
* fix(state): store in-root agent database registry paths relative to the state dir (schema v9)
Copied state directories retained stale absolute agent database registry rows. The combined gateway store then merged old and new copies, causing every sessions.list request to fail with SessionCanonicalKeyMigrationRequiredError.
Schema v9 stores in-root registry paths relative to the state directory; migration rewrites eligible rows, deletes stale duplicates, and preserves external paths.
* fix(voice-call): describe state-relative registry migration
* fix(state): preserve registry locator traversal
* test(macos): move PortGuardian schema-version boundary to v9
The store opens any state database up to maximumSupportedSchemaVersion,
which the schema v9 bump moved from 8 to 9. Shift the supported/newer
partition in the boundary test accordingly.
* fix(state): re-anchor copied default-layout registry rows instead of deleting them
Resolve the ClawSweeper P1 by preserving genuine external default-layout registrations, re-anchoring copied rows when an in-root counterpart exists, and deleting only dual default-layout conflicts.
* fix(cli): report unknown subcommands even with --help
* fix(cli): scope Commander hook lint exceptions
* fix(cli): document Commander hook assertion
* refactor(cli): delegate the Commander help hook through super
`checks-ui-e2e-real-gateway` folded `hybrid` into the `github` breaker clause
instead of taking the attempt-gated route its sibling `checks-ui-e2e` uses, so
it was the only Chromium lane pinned to hosted capacity on attempt 1. Measured
across 46 canonical main runs it averaged 205s (max 225s) against a 150-190s
plateau, making it the run's slowest job whenever the compact Node matrix
behaved.
Route it like `checks-ui-e2e`: Blacksmith 16-vCPU on attempt 1, hosted on rerun,
manual dispatch, fork, and same-repo PR retry. Cache inputs stay on the shared
pnpm store for both backends, matching the sibling lane.
The guard now derives both Chromium lanes' `runs-on` from one pinned template so
a hosted-only divergence cannot return unnoticed; mutation-checked by reverting
the workflow clause (2 failures).
A linked worktree whose base predates (or carries) wrapper changes relative
to origin/main previously hit a hard refusal, even though the canonical
checkout held exactly the trusted origin/main wrapper the refusal message
told the operator to go run by hand. When the canonical checkout is clean
and byte-identical to fetched refs/remotes/origin/main, exec it with a loud
stderr notice instead; advisory dev-wrapper opt-in keeps precedence, and
the refusal remains when no anchor-matching wrapper exists on disk.
Also records the squash-merged stacked-branch rebase gotcha
(git rebase --onto origin/main <landed-branch>) in the PR maintainer skill.
Fork pull requests were pinned to GitHub-hosted runners while maintainer
PRs rode Blacksmith. That split arrived with ci.yml's first commit
(f4f990a) and was never a reasoned posture -- no rationale in docs,
commit bodies, or the ci-limits skill -- and this week's Blacksmith
tranches widened the gap: maintainer walls are ~3:36 while the last fork
PR I measured (#124633) took 13m0s. We already paid for it once in
#118530, which raised the cross-repo artifact budget to 35 minutes
because contributor PR #117992 timed out twice on hosted capacity.
Runner choice now follows author_association: OWNER, MEMBER, COLLABORATOR,
and CONTRIBUTOR get Blacksmith; FIRST_TIME_CONTRIBUTOR, FIRST_TIMER, NONE,
and MANNEQUIN stay on hosted runners, which are free for public repos, so
an unreviewed author cannot spend Blacksmith capacity. Earning the fast
path requires a landed commit, which requires a maintainer merge.
Note for anyone tempted to trim that list: maintainers report CONTRIBUTOR
here, not OWNER/MEMBER, because org membership is concealed -- steipete's
74 sampled PRs are all CONTRIBUTOR. Dropping CONTRIBUTOR would move
maintainer PRs to hosted.
Scope is deliberately runner-only: 27 runs-on clauses. The 34
dependency-cache/use-actions-cache conditions and 6 job ifs stay
fork-gated, because cache poisoning is a different risk from runner
choice -- a fork run still never writes an archive a trusted run restores.
Verified by evaluating all 25 configurable runs-on expressions: maintainer
PR 12/25 Blacksmith, returning-contributor fork 12/25 (identical), unknown
author 0/25, FIRST_TIME_CONTRIBUTOR 0/25, push to main 12/25 unchanged.
Guard tests gained trusted/untrusted fork cases; 119 pass.
Two pieces of dead workflow surface, found auditing where CI time goes:
- ci-timings-summary was hard-disabled (`if: ${{ false && ... }}`) with a
TODO to re-enable or delete it after the next timing-optimization
review. That review happened; the local `pnpm ci:timings` helper is what
we actually use, and docs already pointed there. The job carried a
25-entry needs list that had to be kept in sync to stay lintable.
- build-artifacts exported four `*-result` outputs that no job or workflow
reads.
Removing the job lets the gate guard assert the stronger invariant it
wanted all along: ci-gate needs *every* job in the file, so a new lane
cannot slip in ungated (28 jobs, 27 gated, zero exceptions).
No runtime behavior changes: the job could never run and the outputs had
no consumers. Also audited every `pnpm <script>` and `node scripts/...`
reference in ci.yml for rot -- all resolve.
build-artifacts is the wall's pole in 4 of the last 5 main runs (171-186s,
~15-20s ahead of the next lane), so its serial steps are the wall. The
Doctor plugin-index proof, singleton smoke, and startup-memory check ran
as their own 13s step even though they are independent dist readers that
the 47s artifact-check wave could absorb.
They now run inside that wave: on Blacksmith all seven start together, so
the verifiers cost the wave's max instead of 13s of serial time; hosted
runners still serialize the three through run_verifier so the RSS ceiling
measures an unloaded process. The step drops its selection gate because
the verifiers always run -- each artifact check already self-gates on its
own RUN_* flag, so a run with no checks selected still verifies.
Proof: extracted the step body and ran it with stubbed pnpm/node. Both
modes behave (Blacksmith 7 checks started, hosted-with-nothing-selected
still runs the 3 verifiers), and a failing verifier exits 1 with its
::error annotation in both -- the wave cannot swallow it.
checks-node-core-tooling-2 failed on main (run 31943910358) with
'expected 1786879225197 to be >= 1786879225197.2246': the repaired output
mtime landed a fraction of a millisecond below the input it had to clear.
isArtifactSetFresh repairs output mtimes to exactly ceil(newestInput), so
it leaves zero headroom for sub-millisecond write rounding or lagging
metadata on CI filesystems. When the repair lands at or below its input
the mtime fast path never engages, and every later invocation in that
checkout falls back to re-hashing every input byte -- the expensive path
this repair exists to avoid, gating the d.ts emit that is the slowest
build-all phase (25.9s of a 61s build).
Neither macOS APFS nor an idle Linux ext4 Testbox reproduces the
shortfall in 300 runs, so the repair now clears the newest input by a
whole millisecond instead of matching it. The assertion pins that
headroom, making the test deterministic where it was previously
load-dependent (fails 615 vs 616 without the fix).
* fix(ui): reset logs when source changes
Reload the active tail without a cursor when logs.tail switches files so the Control UI never mixes sources or skips a new file prefix.
* fix(ui): reset log source after reconnect
* test(ui): harden logs lifecycle proof
* test(ui): route Logs lifecycle through real Gateway lane
Pin OCM v0.2.32 so Performance validation uses upstream native .mts discovery and transitive workspace dependency handling instead of the local Bash/tsx trampoline.
* feat(workers): run device sessions from Gateway bundles
Install the current Gateway bundle before a device environment becomes ready, verify it at attach and tunnel boundaries, launch only from the immutable namespaced bundle directory, and retire stale environments for idempotent reprovisioning. Remove the local execution mode and preserve the node-local build claim only as temporary inventory metadata for the final projection/cleanup slice.
* docs(runners): record Gateway bundle cutover
* test(ci): repair runner validation fixtures
# Conflicts:
# src/scripts/test-projects.test.ts
* fix(workers): surface outdated node recovery
Keep legacy runner inventory diagnostic-only while exposing the update-and-reconnect action through node, environment, provider, placement, and Control UI surfaces.
* fix(workers): reject legacy inventory with recovery
* fix(workers): bundle worker deploy closure
* test(workers): close bundle cutover gates
* fix(workers): compose browser runtime at build
* fix(workers): satisfy bundle cutover gates
* fix(workers): route temp runtime through infra
* docs(workers): align bundle host guidance
* fix(ui): fence outdated session destinations