You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ci: a scheduled workflow regenerates scripts/test-shard-timings.json from the newest fully green main run and opens the PR (maintainer-directed, measurement loop) #16464
Filed by the skills lane seat (session session_019RfFHiRCSs3JXLK4cwcfox, os-steve) on the maintainer's direction, 2026-09-07T02:5xZ, and dispatched by the same seat on that direction. Surface owner stays domain:devx.
Authority (maintainer, verbatim, live PM chat 2026-09-07): 「同意你的建议,你负责执行派发所有可行的优化」, on the seat's long-term plan for the Test Core shards (measurement loop → capacity rule → growth constraint). This card is the refresh half of the measurement loop; #16173 carries the one-time refresh and the drift alarm's wiring.
What
A scheduled workflow regenerates scripts/test-shard-timings.json from the six test-core-run-summary-N-of-6 artifacts of the most recent complete, fully green, full-scale push run on main, and opens a pull request with the result, so the balancing input never again depends on someone remembering.
The inputs already exist on every run: each shard publishes its turbo summary as an artifact (retention 1 day); scripts/measure-test-shard-timings.mjs takes any number of summaries and merges by median.
Push runs on main are cancelled by cancel-in-progress when merges come quickly, so the workflow must select a run whose six shards all completed success (uncensored), not merely the newest.
Ruling
New workflow .github/workflows/shard-timings-refresh.yml: schedule weekly (Monday 05:30Z) plus workflow_dispatch. Steps: list the CI workflow's push runs on main through the API with the workflow token, pick the newest whose six Test Core (N/6) jobs all concluded success and whose artifacts are still retained, download the six summaries, run the measure script with --out scripts/test-shard-timings.json, run partition-test-shards.mjs --self-test and update its balancing pins the way the script's own refresh procedure prescribes, and open a PR on a bot branch with skip-changeset, the run id and the before/after per-shard prediction in the body. No PR when the file is byte-identical.
The workflow never hand-edits the JSON and never touches timeout-minutes or the shard count; it is the documented refresh command on a timer.
Read cut-rc.yml and pr-automation.yml for the house idiom of a workflow that pushes a branch and opens a PR; follow it.
Acceptance
workflow_dispatch once on the PR branch: the PR it opens (or its "byte-identical, no PR" log line) is quoted; the seat reads it.
The workflow's own gates green (dispatch-gates --commands derives them).
Claim: PM loop round 9 — flight C2a, maintainer-directed (「同意你的建议,你负责执行派发所有可行的优化」, 2026-09-07): the scheduled shard-timings refresh workflow, with #16473 folded in as its prerequisite — flight C1 (#16173) measured at 03:2xZ that the artifact host (productionresultssa*.blob.core.windows.net) is denied to every agent container by egress policy, so the documented refresh cannot run from a dev worktree; the refresh therefore runs IN-RUNNER, where .turbo/runs/*.json already sits, and this workflow's first workflow_dispatch is the refresh itself — the PR it opens is #16173's deliverable. Rider #16473: buildDataset in scripts/measure-test-shard-timings.mjs bypasses its median rule for a file-sharded package (last run wins; slices from different runs summed into a weight no run measured) — fixed first, with self-test cases, so the refresh's @objectstack/cli weight is a median like every other package's.
Session: session_019RfFHiRCSs3JXLK4cwcfox
Branch: claude/issue-16464-shard-timings-refresh-workflow
Worktree: objectstack-issue-16464
Domain: domain:devx (dispatched by the skills seat on the maintainer's direction)
File surface: .github/workflows/shard-timings-refresh.yml (new), scripts/measure-test-shard-timings.mjs (the slice ledger's merge rule + self-test), one helper under scripts/ci/ if the pin update needs one; scripts/test-shard-timings.json is written only by the workflow run, never by hand. ⛔ ci.yml untouched; no timeout-minutes, matrix or bound change.
Container & model: S, mode:subagent, opus — dispatch-gates.mjs --tier: no path-derived mandate.
Clause-②: no
Serial: no open PR touches the measure script or the JSON (C1 produced no PR); flights V and X hold ci.yml, which this card does not touch. Not governed ⇒ in-seat review, then the seat flips ready and arms SQUASH auto-merge; skip-changeset. The PR carries Fixes #16464 and Fixes #16473; #16173 and #16222 are Refs (they close when the workflow's first refresh PR merges).
File-surface supplement to the Claim: above (not a second claim; branch, session and worktree are unchanged). One file is added to the declared surface, under the bounded in-place exemption, because the card's ruled deliverable cannot exist without it.
Added: scripts/check-self-test-wired.mjs — its invocation extractor had a left boundary but none on its extension alternation, and js is a prefix of json. The first workflow line naming a .json under scripts/ therefore matched only as far as the .js and minted a phantom key with no file behind it:
scripts/test-shard-timings.json -> keyed as scripts/test-shard-timings.js
That is exactly the phantom the gate's left boundary was added to abolish, arriving from the other end, and it is silent in both directions — the real file is audited by nothing and the phantom key reconciles against no carrier.
Why this is not scope creep: ruling 2 requires the workflow to invoke the generator with --out scripts/test-shard-timings.json, and rulings 3 and 4 require it to byte-compare and commit that same path. Writing the path is the card's content, so there is no version of this deliverable that does not trip the gate. It was invisible until now only because no workflow had yet named a .json under scripts/.
The four conditions, each checked rather than asserted:
Mechanical, with the correct form already fixed by the file — a negative lookahead rejecting a following word character or hyphen is the spelling that file already applies to the self-test flag two groups along, and its header states the exact-equality principle the extension had not been given.
Same gate family, no new verification surface — check-self-test-wired and its --self-test were already in this card's derived family before the edit.
Proven red-then-green rather than argued. With the boundary removed and the mutation confirmed on disk (anchor 1 to 0, git diff --numstat = 1 1), the production run stays GREEN at exit 0 while --self-test goes RED naming the new pin; restoring gives back the byte-identical blob (db95b1025a4ba4d6a310268161d903b6d44af716, equal to the HEAD blob) and the self-test returns to exit 0. Four cases pin it, control first, battery floor 4 to 8.
Declared surface is therefore: .github/workflows/shard-timings-refresh.yml (new), scripts/ci/select-shard-timings-run.mjs (new), scripts/measure-test-shard-timings.mjs, scripts/check-self-test-wired.mjs. ci.yml is untouched; scripts/test-shard-timings.json is written only by a workflow run, never by hand.
{
"issue": 16464,
"status": "done",
"branch": "claude/issue-16464-shard-timings-refresh-workflow",
"pr": "https://github.com/objectstack-ai/objectstack/pull/16498",
"premise_still_valid": true,
"summary": "Both cards land on one branch, rider first. #16473: buildDataset's slice ledger was keyed (package, slice count, slice index) with no notion of WHICH RUN a summary came from, so every run collapsed into one entry and both merge rules broke at once — a sliced package took whichever run was read last (1000s recorded where the median of three runs is 600s) and a set completed from slices of DIFFERENT runs was summed into a weight no run observed (800s) with skippedIncompleteSlices empty. The ledger is now keyed by run first: slices are summed WITHIN a run, the per-run sums are medianed ACROSS runs exactly as an unsliced package's observations are, and a run that cannot assemble every slice contributes no sample and is named with its run. The grouping key comes from the caller via `--run` because a summary carries no run id; undeclared multi-run input is REFUSED by name with `--run` as the remedy, which is what makes the undeclared single-run default (what the workflow feeds) safe. provenance.mergeRule, which claimed \"median across summaries\" and was only ever true of unsliced packages, now states both composed rules. #16464: .github/workflows/shard-timings-refresh.yml runs weekly (Monday 05:30Z) plus workflow_dispatch, selects a run, downloads its six run-summary artifacts, regenerates, and opens a PR only on a byte difference. It DOWNLOADS the artifacts (ruling 2) rather than generating from the runner's own summaries: a separate lane has no .turbo/runs of its own, and producing a complete set would mean an unsharded full-workspace suite — the \"special measurement run\" this tooling exists to avoid. Downloading is safe here precisely because it is unreachable elsewhere: the blob host is denied to every agent container by egress policy and native to a GitHub runner, so running in-runner removes the channel from the loop rather than working around the denial. Run selection is a tested unit (scripts/ci/select-shard-timings-run.mjs, 30 self-test cases) because \"newest\" is wrong three measured ways — cancelled runs, cache replays, expired artifacts — and it judges the replay case on what was MEASURED (a package the committed dataset measured and the workspace still holds, absent from the refresh, has silently become an estimate) rather than on a duration threshold. The partitioner's balancing pins are RUN but never acted on: they red by design on a refresh that outgrows the bound, and the remedy they name is a decision, so the verdict is quoted verbatim into the PR body and the PR is opened either way. PM-side note: the assignee on both cards was already set (os-steve) and the newest Claim on each names this branch, so nothing was half-stated.",
"tests": "ALL READINGS ON HEAD ea96c274e1 (this branch after merging origin/main at 4500bc5d79), and every exit code captured by redirecting first and reading the status before any pipe.\n\n(1) RIDER, RED THEN GREEN, on the exported buildDataset — flight C1's scratch driver reproduced. RED leg (before the fix): three runs measuring 400/600/1000s recorded `cli = 1000` (last run wins) where the median is 600; the unsliced control fed the same shape correctly recorded 300 (so the median rule was alive and the sliced path alone bypassed it); the cross-run splice recorded `cli = 800` with `skippedIncompleteSlices = []`. GREEN leg (identical driver, after the fix): 600 / 300 / 600, and `skippedIncompleteSlices = [\"cli in run C (missing 2/2)\"]`. NOTE ON THE FIRST ATTEMPT: my first red-leg run exited with MODULE_NOT_FOUND — a relative ESM import resolving from the script file rather than cwd — which is a script that never reached its body, not a reading; it was re-run with an absolute specifier and only that run is reported.\n\n(2) RIDER SELF-TEST: `node scripts/measure-test-shard-timings.mjs --self-test` exit 0, verdict line `measure-test-shard-timings: self-test OK`. Seven new cases, battery floor raised 34 -> 41 (the count was MEASURED by setting the floor absurdly high and reading `registered 41 case(s)` back, not counted by eye).\n\n(3) RIDER THROUGH THE REAL CLI, on synthetic six-shard summaries, which is the path the workflow actually takes: three declared runs (`--run A ... --run B ... --run C ...`) gave `@objectstack/cli = 600` and `@objectstack/spec = 300` with `provenance.runs = [\"A\",\"B\",\"C\"]`; the SAME six files with no `--run` exited 1 naming the slice and the remedy (`@objectstack/cli slice 1/2 was measured twice in this summary set (200s and 300s) ... Group them with --run RUNID`); one run with no `--run` exited 0 and gave 600 with no ceremony. A realistic single-run set (five whole-package shards plus the CLI sliced across shards 5 and 6) summed the CLI to 1250s within the run. Dropping shard 6 from that set left the CLI OUT of `packages` entirely with `skippedIncompleteSlices = [\"@objectstack/cli (missing 2/2)\"]` and a loud stderr warning — and the workflow's own coverage check then rejected that run by name (`COVERAGE SHORTFALL -- 2 package(s) ... are NOT measured by this refresh: @objectstack/cli, @objectstack/console`), so the two guards compose.\n\n(4) THE OTHER CONSUMER: `partition-test-shards.mjs --self-test` exit 0, verdict `partition-test-shards: self-test OK (71 measured packages -> 72 shard items, 6 shards, max/mean 1.00x <= 1.3x, floor 404s, bins 672/672/672/671/672/671s)`; and `--check-drift` driven on a synthetic shard-5 summary still reads the slice correctly (`@objectstack/cli: predicted 229.1s, measured 600.0s (2.62x)` — 229.1 is the dataset weight halved by the slice count), so the rider did not disturb the drift path.\n\n(5) NEW SELECTOR: `node scripts/ci/select-shard-timings-run.mjs --self-test` exit 0, `select-shard-timings-run: self-test OK`, 30 cases (measured the same way), floor 30, one declared battery. Its `--check-coverage` mode was exercised against the REAL committed dataset: a synthetic 31-of-71-package refresh was rejected naming all 40 lost packages; the identical dataset passed with `coverage OK -- 71 package(s) measured, 71 required`.\n\n(6) GATE UNION: all 49 families `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derives for this four-path change set were run — 49 green, 0 red — and reconciled: `✓ dispatch-gates --ran: 49 derived famil(ies) accounted for — 49 run, 0 NOT-MEASURED.` The derivation reported no stale tree. `pnpm check:pm-dispatch-gates` itself: `✓ dispatch-gates self-test: 1534 cases pass.`\n\n(7) WHOLE-REPO LINT (not a narrowing): `pnpm lint` = `eslint . --no-inline-config` through `bash scripts/pm/os-verify-lock.sh` with `OS_VERIFY_LOCK_SLOT=issue-16464`, `LINT-EXIT=0`. The lock printed `held the lock 90s (1m30s) · waited 86s (1m26s)` and its own caveat that these are shared-box seconds. It also warns that a `;`-sequenced batch reports only the last part's exit — here the batch IS the single lint command plus its echo, so `LINT-EXIT=0` is the lint's own status.\n\n(8) GOVERNED-MERGE PREDICATE, on the final file list: `check-governed-merges --test` exit 0, `governed-surface predicate: 0 of 4 path(s) hit the register (5 surfaces, repo-agnostic). ✅ NOT governed`.\n\n(9) GATE-FIX ABLATION, with both legs proved on disk. First mutation attempt was a NO-OP — a `perl -0pi` anchor matched zero times, `git diff --stat` empty — so those readings were discarded and it was redone through a driver that REFUSES unless the anchor matches exactly once. Mutated (right boundary removed, confirmed: boundary-present 1 -> 0, `git diff --numstat` `1 1`): production run GREEN at exit 0, `--self-test` RED at exit 1 naming the new pin verbatim (`scripts/data.json was truncated to scripts/data.js and keyed as a script — a phantom path with no file behind it`) plus the live-corpus battery. That double is exactly the shape check-self-test-wired's own header documents for this gate class. Restored with `git checkout HEAD -- \"$REPO_ROOT/PATH\"` (absolute, HEAD-pinned): `git hash-object` = `db95b1025a4ba4d6a310268161d903b6d44af716`, equal to the HEAD blob, `git diff HEAD` empty, self-test back to exit 0.\n\n(10) SERIALIZATION: merge-tree against #16471's head `05cb57b43c` twice, both exit 0, zero conflict markers, same tree `65319f6c34`. #16471 has since landed and was merged into this branch cleanly. All 33 open PRs were read for their file lists; none touches `scripts/measure-test-shard-timings.mjs`, `scripts/check-self-test-wired.mjs`, `scripts/test-shard-timings.json` or the new workflow.\n\nNOT MEASURED, reported as such rather than as passes: `pnpm check:single-claim-paths` exits 2 = PREREQUISITE NOT MET off a PR context (it needs PR_NUMBER/GITHUB_TOKEN and runs on the PR in CI), and the eight families dispatch-gates itself marks NOT MEASURED because their argv takes a value only a CI run supplies. `workflow_dispatch` on this branch is likewise NOT MEASURED — see open_questions.",
"mcp_calls": "0 — the channel probe (a repo-scoped REST read) answered 200, so every GitHub read AND write went over REST: both cards and their comments, #16173's and #16222's bodies and 31 comments, the open-PR list, per-PR file lists for all 33 open PRs, the file-surface supplement comment plus its PATCH and two read-backs, the report comment, the pointer comment and the PR creation. No MCP GitHub tool was called.",
"open_questions": [
{
"question": "Ruling 4 asked for a `workflow_dispatch` on the branch as reverse verification. This seat cannot perform it: POST /actions/workflows/shard-timings-refresh.yml/dispatches answers 403 `Resource not accessible by integration`, so the session credential carries no Actions write scope (and a workflow absent from the default branch is often not dispatchable at all, which this 403 cannot distinguish from the scope answer). What should stand as this card's reverse verification?",
"options": [
"A. The `pull_request` trigger this PR already carries. It was added for an unrelated reason — dispatch-gates reds on a scheduled-only gate family — and it happens to satisfy ruling 4 better than a dispatch would: opening this PR runs the lane end to end on a real runner against live artifacts (selection, download, regeneration, coverage check, byte comparison, partitioner verdict), with only the write skipped and the would-be PR body rendered to the run's step summary. Costs nothing, and the evidence is on this PR's own checks.",
"B. A maintainer or a seat with Actions write dispatches the workflow once after this lands. Gives the literal ruled artefact including the write path (branch push, PR creation, label write), which A deliberately does not exercise.",
"C. Grant the agent session Actions write. Outside the repo, and it buys one reverse verification at the cost of every agent seat being able to start any workflow."
],
"recommendation": "A now, B as the first scheduled run's natural check — they are complementary, not competing. REAL DEMAND: the property that actually needs proving is that the lane picks a defensible run and regenerates correctly, and A exercises all of that against live data; the write path is three well-trodden house idioms (branch push, `gh pr create`, additive label POST) and is the part a scheduled run proves for free on Monday. LONG-TERM SOUNDNESS: A is contract-first — the lane proves itself on every PR that touches it, forever, rather than once by hand; B is a one-time observation that re-rots. HARDER FOR AN AI TO GET WRONG: A is the option that keeps a future edit to this lane from shipping unexercised, which is the same failure class the card exists to end; C is the opposite, widening a credential so a check can be performed. STARTUP FOCUS: A adds nothing — the trigger is already there and was required independently — while C asks for an infrastructure grant with no pull behind it. The one thing I would not do is weaken the coverage check or the eligibility rule to make a first run land more easily."
},
{
"question": "Ruling 2 asked for 'the pin update by the partitioner's own refresh procedure', reusing flight C1's mechanics. C1 landed no PR and left no script, and the partitioner has no mechanical pin-refresh procedure to reuse: its pin 3c substitutes CLI_MEASURED = 1231.52 and its counterfactual leg says in terms that re-deriving or retiring the slice count is a decision ('Re-derive the slice count (or retire it) instead of leaving a pin that cannot fail'). So the workflow updates NO pin. Is that the intended reading?",
"options": [
"A. As built: the workflow runs `partition-test-shards.mjs --self-test` on the refreshed dataset, quotes the verdict verbatim into the PR body (with a named block when it reds, restating that the remedy is the slice count and never the bound), and opens the PR either way. No pin is ever written by machine.",
"B. Teach the workflow to re-derive FILE_SHARDED_PACKAGES arithmetically and commit the new slice count alongside the dataset. Mechanically possible — the derivation is written out in the partitioner's header — but it makes a machine the author of a balancing decision, and pin 3c's counterfactual leg exists precisely to force a human to look.",
"C. Withhold the PR when the pins red, and fail the workflow instead."
],
"recommendation": "A. REAL DEMAND: the pins are expected to red on the first honest refresh — flight C1 measured shards 5 and 6 at 1.54x-2.38x their predictions on all three green runs — so this is the common case, not a corner. LONG-TERM SOUNDNESS: A is the no-workaround shape; B is a machine quietly re-deriving the number that decides how CI is scheduled, and the partitioner's header calls moving a bound to absorb a gap 'the one move that cannot be right'. HARDER FOR AN AI TO GET WRONG: A puts the arithmetic in front of a person with the measurement attached; B is exactly the automation that would let a bad refresh land quietly, and C is the silent-rot failure this card exists to remove — a refusal to open the PR would discard the measurement and leave the dataset stale with nobody told. C is refused outright for that reason. STARTUP FOCUS: A adds no surface; B adds a code-writing path to a scheduled workflow, which is a large new capability for one number a person can set in a minute."
}
],
"out_of_scope_findings": [
"FIXED IN PLACE rather than filed, under the bounded exemption, and declared as a file-surface supplement on #16464 before the edit (comment 5564911725): scripts/check-self-test-wired.mjs keyed `scripts/test-shard-timings.json` as `scripts/test-shard-timings.js` — `js` is a prefix of `json` and its invocation extractor had a left boundary but none on the extension — minting the exact phantom key its left boundary was added to abolish. Not separable from this card: ruling 2 requires the workflow to name that path, so no version of this deliverable avoids it. Four conditions checked, not asserted; red-then-green ablation in `tests` item 9.",
"FIXED IN PLACE, same reasoning: the refresh lane was the first gate family in the tree reachable ONLY on a schedule, which reds `dispatch-gates` because such a family appears on no card gate list. Closed with the `pull_request` posture half-state-patrol.yml and required-set-patrol.yml already keep, write-skipped.",
"NOT FILED, no defect: `measure-test-shard-timings.mjs --self-test` was run by NO workflow before this PR (`grep -rn measure-test-shard-timings .github/` = 0 hits), so its 34-case battery evaluated never in CI. It was not a `check-self-test-wired` breach because that gate scopes its population to scripts a workflow names, and none did. This PR closes it as a side effect — the new workflow names and self-tests it, and `check-self-test-wired` now reports 184 wired scripts, green. Recording rather than filing, since the condition no longer exists on this branch.",
"OBSERVATION for PM, no card filed and no action taken: the refresh PR the workflow opens carries only `Refs`, never a closing keyword, because a weekly lane cannot know which cards a given run ought to retire. Cards #16173 and #16222 therefore stay open when the first refresh PR merges, contrary to the claim comment's expectation (\"they close when the workflow's first refresh PR merges\"). Whoever lands that first refresh should close them by hand or add the keyword to that one PR. Flagging rather than building it in: a keyword baked into the workflow would try to close already-closed cards on every subsequent Monday."
]
}
Supersedes the earlier os-dev-report on this card (comment 5565145010). That one was written before the lane had ever run; its two live runs then measured a fact that changes the verdict from done to needs_decision. The rider is unaffected and remains fully delivered.
{
"issue": 16464,
"status": "needs_decision",
"branch": "claude/issue-16464-shard-timings-refresh-workflow",
"pr": "https://github.com/objectstack-ai/objectstack/pull/16498",
"premise_still_valid": true,
"summary": "SUPERSEDES my earlier os-dev-report on this card (comment 5565145010, status done): that report was written before the lane had run, and the lane's first two live runs measured a fact that changes the verdict. The rider #16473 is fully repaired and stands on its own. The workflow card is implemented and proven live in every part except its acceptance rule, which now needs a ruling, so the PR binds the closing keyword to the rider only and declares itself a partial delivery of the workflow card. THE RIDER (#16473): buildDataset's slice ledger was keyed (package, slice count, slice index) with no notion of WHICH RUN a summary came from, so every run collapsed into one entry and both merge rules broke at once — a sliced package took whichever run was read last (1000s recorded where the median of three runs is 600s) and a set completed from slices of DIFFERENT runs was summed into a weight no run observed (800s) with skippedIncompleteSlices empty. The ledger is now keyed by run first: slices are summed WITHIN a run, the per-run sums are medianed ACROSS runs exactly as an unsliced package's observations are, and a run that cannot assemble every slice contributes no sample and is named with its run. The grouping key comes from the caller via `--run` because a summary carries no run id; undeclared multi-run input is REFUSED by name with `--run` as the remedy. provenance.mergeRule, which claimed 'median across summaries' and was only ever true of unsliced packages, now states both composed rules. THE WORKFLOW (#16464): .github/workflows/shard-timings-refresh.yml runs weekly (Monday 05:30Z) plus workflow_dispatch plus a lane-scoped pull_request trigger, selects runs, downloads their run-summary artifacts, regenerates, and opens a PR only on a byte difference. It DOWNLOADS artifacts (ruling 2) rather than generating from the runner's own summaries: a separate lane has no .turbo/runs of its own and producing a complete set would mean an unsharded full-workspace suite, the 'special measurement run' this tooling exists to avoid. Downloading is safe here precisely because it is unreachable elsewhere — the blob host is denied to every agent container by egress policy and native to a GitHub runner — so running in-runner removes the channel from the loop rather than working around the denial. Run selection is a tested unit (scripts/ci/select-shard-timings-run.mjs, 30 self-test cases). The partitioner's balancing pins are RUN but never acted on: they red by design on a refresh that outgrows the bound, and the remedy they name is a decision, so the verdict is quoted verbatim into the PR body. WHAT THE LIVE RUNS FOUND, AND WHY THIS IS needs_decision: no single green run measures the workspace, and neither does the union of all of them. Of the seven retained green push runs on main the best measured 52 of 71 packages and the others 2, 3, 13, 18, 22 and 49; accumulating all seven (which the lane now does, each run fenced by its own --run group — a path only the rider made correct) reaches 57 of 71 and converges there, because 14 packages are turbo cache HITs in every retained run and the generator refuses hits. So the ruled acceptance rule — regenerate the dataset from a green run's artifacts — cannot be satisfied as written, and the lane currently refuses loudly rather than writing a dataset in which 14 packages would silently fall back to the test-file-count estimate. Settling that is a decision about what the dataset MEANS (open_questions 1), not a threshold I should pick. Two gate defects were fixed in place under the bounded exemption, both declared on the card before the edit and both unavoidable for this deliverable; they are in out_of_scope_findings. PM-side note: the assignee on both cards was already set (os-steve) and the newest Claim on each names this branch, so nothing was half-stated.",
"tests": "FINAL HEAD 4fe161b41a. Every exit code captured by redirecting first and reading the status before any pipe; no verdict below comes from a pipeline status.\n\n(1) RIDER, RED THEN GREEN, driving the exported buildDataset the way flight C1 did. RED (before the fix): three runs measuring 400/600/1000s recorded cli = 1000 (last run wins) where the median is 600; the unsliced control fed the same shape correctly recorded 300, proving the median rule was alive and the sliced path alone bypassed it; the cross-run splice recorded cli = 800 with skippedIncompleteSlices = []. GREEN (identical driver, after the fix): 600 / 300 / 600, with skippedIncompleteSlices = [\"cli in run C (missing 2/2)\"]. My FIRST red-leg attempt exited MODULE_NOT_FOUND — a relative ESM import resolving from the script file rather than cwd — which is a script that never reached its body and not a reading; it was re-run with an absolute specifier and only that run is reported.\n\n(2) RIDER SELF-TEST: node scripts/measure-test-shard-timings.mjs --self-test exit 0, verdict line \"measure-test-shard-timings: self-test OK\". Seven new cases, battery floor 34 -> 41; the count was MEASURED by setting the floor absurdly high and reading \"registered 41 case(s)\" back, never counted by eye.\n\n(3) RIDER THROUGH THE REAL CLI on synthetic summaries: three declared runs gave @objectstack/cli = 600 and @objectstack/spec = 300 with provenance.runs = [\"A\",\"B\",\"C\"]; the SAME six files with no --run exited 1 naming the slice and the remedy; one run with no --run exited 0 and gave 600. A realistic single-run set (five whole-package shards plus the CLI sliced across shards 5 and 6) summed the CLI to 1250s within the run; dropping shard 6 left the CLI out of packages entirely with skippedIncompleteSlices naming it, and the coverage check then rejected that run by name — the two guards compose.\n\n(4) THE OTHER CONSUMER: partition-test-shards.mjs --self-test exit 0, verdict \"partition-test-shards: self-test OK (71 measured packages -> 72 shard items, 6 shards, max/mean 1.00x <= 1.3x, floor 404s, bins 672/672/672/671/672/671s)\"; and --check-drift on a synthetic shard-5 summary still reads the slice correctly (@objectstack/cli: predicted 229.1s = the dataset weight halved by the slice count, measured 600.0s, 2.62x). The rider did not disturb the drift path.\n\n(5) NEW SELECTOR: node scripts/ci/select-shard-timings-run.mjs --self-test exit 0, 30 cases, floor 30. Its --check-coverage mode exercised against the REAL committed dataset: a synthetic 31-of-71-package refresh rejected naming all 40 lost packages; the identical dataset passed with \"coverage OK -- 71 package(s) measured, 71 required\".\n\n(6) TWO LIVE RUNS OF THE LANE ITSELF, on a real runner against live artifacts, through its own pull_request trigger — this is the reverse verification, and it is what found the blocking fact. Run 34083991141 (before the accumulation fix): steps 1-10 all succeeded, including selection and the artifact download that no agent container can perform, and step 11 refused because every candidate run was a partial cache replay — best 52 of 71 packages, others 2, 3, 13, 18, 22, 49. Run 34085. .. (job 101625792555, after the fix): accumulation works and is monotone — 1 run 2/71, 2 runs 22/71, 4 runs 28/71, 5 runs 35/71, 6 runs 56/71, 7 runs 57/71 — and then refuses, because 14 packages are cache hits in EVERY retained run (driver-memory, driver-sqlite-wasm, formula, metadata-core, metadata-fs, observability, plugin-hono-server, sdui-parser, service-analytics, service-cache, service-cluster, service-cluster-redis, service-package, trigger-api). I predicted that shortfall by intersecting the seven per-run missing lists from the FIRST run log (14 packages) before the second run confirmed it live, so the number is measured twice by two independent routes. Both runs are red on the PR, deliberately: the lane refuses rather than writing a dataset it cannot stand behind.\n\n(7) GATE UNION on 4fe161b41a: all 49 families that node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derives for this four-path change set were run — 49 green, 0 red — and reconciled: \"OK dispatch-gates --ran: 49 derived famil(ies) accounted for — 49 run, 0 NOT-MEASURED.\" The derivation reported no stale tree. pnpm check:pm-dispatch-gates itself: \"dispatch-gates self-test: 1534 cases pass.\"\n\n(8) WHOLE-REPO LINT on 4fe161b41a, not a narrowing: pnpm lint = eslint . --no-inline-config through bash scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-16464, LINT-EXIT=0. The lock printed \"held the lock 99s (1m39s) / waited 164s (2m44s)\" and its own caveat that those are shared-box seconds, not idle-box.\n\n(9) GOVERNED-MERGE PREDICATE on the final file list: check-governed-merges --test exit 0, \"governed-surface predicate: 0 of 4 path(s) hit the register (5 surfaces, repo-agnostic). NOT governed\". Also check-partof-closing-keyword driven with the PR number and the branch commit list: exit 0, \"its 9 commit message(s) carry no card-relation trailer\" — the Part-of/closing-keyword split on this PR is machine-checked, not asserted.\n\n(10) GATE-FIX ABLATION, both legs proved on disk. My FIRST mutation attempt was a NO-OP — a perl -0pi anchor matched zero times and git diff --stat was empty — so those readings were discarded and it was redone through a driver that REFUSES unless the anchor matches exactly once. Mutated (boundary-present 1 -> 0, git diff --numstat \"1 1\"): production run GREEN at exit 0 while --self-test went RED at exit 1 naming the new pin verbatim plus the live-corpus battery — exactly the double that gate's own header documents for this class. Restored with git checkout HEAD -- \"$REPO_ROOT/PATH\" (absolute, HEAD-pinned): git hash-object db95b1025a4ba4d6a310268161d903b6d44af716 equal to the HEAD blob, git diff HEAD empty, self-test back to exit 0.\n\n(11) ACCUMULATION LOGIC simulated offline before it was pushed, using the same array-rebuild shape the workflow uses: three synthetic partial-replay runs whose union covers the dataset went 1/5 -> 3/5 -> 5/5 packages, the CLI summed to 900 WITHIN its own run (not across runs), provenance.runs recorded all three, and skippedIncompleteSlices stayed empty. The live run then reproduced the same monotone behaviour on real data.\n\n(12) SERIALIZATION: merge-tree against #16471's head 05cb57b43c twice, both exit 0, zero conflict markers, same tree 65319f6c34; that PR has since landed and merged into this branch cleanly. All 33 open PRs were read for their file lists; none touches this card's four paths.\n\nNOT MEASURED, reported as such rather than as passes: pnpm check:single-claim-paths exits 2 = PREREQUISITE NOT MET off a PR context; the eight families dispatch-gates itself marks NOT MEASURED because their argv takes a value only a CI run supplies; and workflow_dispatch on the branch, which this seat cannot perform (403 Resource not accessible by integration — no Actions write scope) and which the pull_request trigger replaced.",
"mcp_calls": "2 — both mcp__github__get_job_logs, to read the two live runs of the lane, because GET /actions/jobs/{id}/logs redirects to the same blob host this container is denied (curl 56, CONNECT 403) and the MCP path returns the content directly. Everything else went over repo-scoped REST, which probed 200: both cards and their comments, #16173/#16222 bodies and 31 comments, the open-PR list, per-PR file lists for all 33 open PRs, check-runs, the workflow_dispatch attempt, the file-surface supplement plus its PATCH and read-backs, the report and pointer comments, PR creation, the PR body PATCH and its read-back, and the label POST and read-back.",
"open_questions": [
{
"question": "BLOCKING, and the reason this report is needs_decision. The ruled acceptance rule — regenerate the dataset from a green run's run-summary artifacts — cannot be satisfied as written, because no set of retained runs measures the whole workspace. Measured twice by independent routes: the best single run covers 52 of 71 packages, the accumulation of all seven retained runs converges at 57 of 71, and the last 14 are turbo cache HITs in every one of them (their inputs have not changed, so turbo replays them and the generator refuses replays). What should the lane do about a package it did not re-measure?",
"options": [
"A. MERGE, not replace: a package this refresh did not measure keeps its last measured weight, and the dataset gains a `carriedOver` list naming those packages the way `skippedAsCached` already names its case. The argument is that a cache HIT is not missing data — it is positive evidence that the package has not changed since it was last measured, so the committed weight is still CORRECT rather than stale, and turbo is the witness. Cost: `provenance.measuredAt` stops describing every number in the file, so it has to become per-package or be paired with the carried-over list. Implemented in the generator (a `--merge-into` mode), never in the workflow, so \"every byte came out of the generator\" stays true.",
"B. Force a cold measurement: give the lane its own full-workspace `turbo run test` with the cache disabled, and generate from that. One run, everything measured, `measuredAt` keeps its meaning exactly. Cost: it is precisely the \"special measurement run\" the generator's header says the artifact path exists to avoid — roughly an unsharded full suite of runner time every week, and it measures a machine nobody's PRs run on.",
"C. Lower the coverage bar to a fraction (say 80%) and let the remainder fall back to the test-file-count estimate. Cheapest to write and the only option that invents a number nobody can defend; it also silently converts measured weights into estimates, which is the exact direction this whole file refuses in.",
"D. Ship the lane as it stands — refusing and naming the shortfall every Monday — until A or B is chosen. Costs nothing and hides nothing, but a lane that reds every week is one people learn to ignore, which is the failure mode this card exists to end."
],
"recommendation": "A, with D as the honest interim the PR already implements. Along the four axes. (1) REAL BUSINESS NEED, measured not speculative: the dataset is 14 days stale, shards 5 and 6 ran at 1.5x-2.4x their predictions on all three green runs flight C1 measured, the temporary 45-minute wall (#16445) is being held open until this rebalance lands, and B is the only option that costs real money every week for a number that changes slowly. A refreshes the 57 packages that DID change and leaves the 14 that did not exactly where they are, which is the whole of what anybody wanted. (2) LONG-TERM SOUNDNESS, no workarounds: A is the only option whose semantics are TRUE — every number in the file remains a real measurement of the code as it stands, and the cache is the proof for the ones not re-measured; C is a workaround that launders estimates into a file whose entire purpose is to not contain estimates; B is sound but buys that soundness with a weekly full suite and a measurement environment that is not CI. (3) HARDER FOR AN AI TO GET WRONG: A makes the file monotone — a refresh can only ever improve it, never degrade a package to a guess — and the `carriedOver` list makes what was not re-measured visible in the artefact rather than inferable from the split going strange later; C is the opposite and is the one option I would refuse outright even if instructed to pick cheaply. (4) STARTUP FOCUS, no capability sprawl: A adds one flag to a script that already exists and no new infrastructure; B adds a recurring compute cost and a second measurement environment to keep honest. The one thing I would NOT do under any option is raise MAX_SHARD_OVER_MEAN or MAX_MEASURED_OVER_PREDICTED to absorb the gap; the partitioner's header calls that the one move that cannot be right and its pin 3c is built to red on it."
},
{
"question": "Ruling 2 asked for 'the pin update by the partitioner's own refresh procedure', reusing flight C1's mechanics. C1 landed no PR and left no script, and the partitioner has no mechanical pin-refresh procedure to reuse: its pin 3c substitutes CLI_MEASURED = 1231.52 and its counterfactual leg says in terms that re-deriving or retiring the slice count is a decision. So the workflow updates NO pin. Is that the intended reading?",
"options": [
"A. As built: run partition-test-shards.mjs --self-test on the refreshed dataset, quote the verdict verbatim into the PR body (with a named block when it reds, restating that the remedy is the slice count and never the bound), and open the PR either way. No pin is ever written by machine.",
"B. Teach the workflow to re-derive FILE_SHARDED_PACKAGES arithmetically and commit the new slice count alongside the dataset. Mechanically possible — the derivation is written out in the partitioner's header — but it makes a machine the author of a balancing decision, and pin 3c's counterfactual leg exists precisely to force a human to look.",
"C. Withhold the PR when the pins red, and fail the workflow instead."
],
"recommendation": "A. REAL DEMAND: the pins are expected to red on the first honest refresh — flight C1 measured shards 5 and 6 at 1.54x-2.38x their predictions on all three green runs — so this is the common case, not a corner. LONG-TERM SOUNDNESS: A is the no-workaround shape; B is a machine quietly re-deriving the number that decides how CI is scheduled. HARDER FOR AN AI TO GET WRONG: A puts the arithmetic in front of a person with the measurement attached; B is exactly the automation that would let a bad refresh land quietly, and C is the silent-rot failure this card exists to remove — withholding the PR would discard the measurement and leave the dataset stale with nobody told, so C is refused outright. STARTUP FOCUS: A adds no surface; B adds a code-writing path to a scheduled workflow for one number a person sets in a minute."
}
],
"out_of_scope_findings": [
"FIXED IN PLACE under the bounded exemption, declared as a file-surface supplement on #16464 BEFORE the edit (comment 5564911725): scripts/check-self-test-wired.mjs keyed scripts/test-shard-timings.json as scripts/test-shard-timings.js — js is a prefix of json and its invocation extractor had a left boundary but none on the extension — minting the exact phantom key its left boundary was added to abolish, silent in both directions. Not separable from this card: ruling 2 requires the workflow to name that path, so no version of this deliverable avoids it. Four conditions checked rather than asserted; red-then-green ablation in tests item 10; four cases pin it, floor 4 -> 8.",
"FIXED IN PLACE, same exemption and same unavoidability: this lane was the first gate family in the tree reachable ONLY on a schedule, which reds dispatch-gates because such a family appears on no card gate list and is graded by nobody until the next sweep. Closed with the pull_request posture half-state-patrol.yml and required-set-patrol.yml already keep, write-skipped on a PR run. It also turned out to be the mechanism that delivered this card's reverse verification and found the blocking finding above, so it paid for itself twice.",
"NOT FILED, no defect remains: measure-test-shard-timings.mjs --self-test was run by NO workflow before this PR (grep -rn measure-test-shard-timings .github/ = 0 hits), so its 34-case battery evaluated never in CI. It was not a check-self-test-wired breach because that gate scopes its population to scripts a workflow names, and none did. This PR closes it as a side effect — the new workflow names and self-tests it, and check-self-test-wired now reports 184 wired scripts, green.",
"FOR PM, NOT FILED AS A CARD because it is a fact about the tree rather than a defect, and it belongs with open_question 1: the documented refresh procedure in scripts/measure-test-shard-timings.mjs's own header (\"Download all six from any green queue build and run the generator\") is optimistic in a way nobody had measured. It yields 2-52 of 71 packages depending on how warm that run's cache was. Whatever is decided for the lane, that header sentence should be corrected in the same change, or the next person to refresh by hand will believe they measured the workspace when they measured a seventh of it.",
"OBSERVATION for PM, no action taken: the refresh PR the workflow opens carries only Refs, never a closing keyword, because a weekly lane cannot know which cards a given run ought to retire. Cards #16173 and #16222 therefore stay open when the first refresh PR merges, contrary to the claim comment's expectation. Whoever lands that first refresh should retire them by hand or add the keyword to that one PR; baking it into the workflow would have every subsequent Monday try to re-close cards that are already closed."
]
}
Ruling at the 08:07Z delivery (report 5567426497, superseding 5565145010; PR #16498 at 4fe161b41a): Q1 → A, Q2 → A, and the earlier reverse-verification question → A once the lane's own pull_request run is green. Under the maintainer's direction 「同意你的建议,你负责执行派发所有可行的优化」 (2026-09-07); the card's acceptance line "regenerate from a green run's artifacts" is amended as below, because the live runs measured that no retained run set covers the workspace (best single run 52 of 71, the accumulation of all seven converges at 57 of 71, the last 14 are turbo cache HITs in every run).
Q1 → A, merge not replace, with these conditions:
A package the refresh did not measure keeps its last measured weight and is named in a carriedOver list beside skippedAsCached; provenance.measuredAt becomes per-package (or the file carries the carried-over list next to a single date, whichever the generator's readers already parse — pick the one that needs no change in partition-test-shards.mjs and say which). Implemented in the generator as a --merge-into mode; the workflow never writes a byte the generator did not emit.
Coverage is judged on measured ∪ carriedOver against the workspace: a package in neither (a new package, or one whose committed weight is already an estimate) is named in the refusal, never silently estimated; the lane still refuses when it measured nothing at all.
A carried-over weight is only carried when the cache HIT is the witness (the package's test task was a HIT in the accumulated runs); a package absent from every run for any other reason is a coverage failure, not a carry.
The header sentence in scripts/measure-test-shard-timings.mjs ("Download all six from any green queue build and run the generator") is corrected in the same change to state the measured fact (a run covers 2–52 of 71 depending on cache warmth; the lane accumulates runs and carries cache-hit packages).
Self-test cases for: a carried package (weight unchanged, named), a package in neither set (refused by name), a HIT-witnessed carry versus an absent package (only the first carries), and the monotone accumulation.
Q2 → A, as built: the partitioner's self-test runs on the refreshed dataset and its verdict is quoted verbatim into the PR body (a named block when it reds, restating that the remedy is the slice count and never the bound); the PR opens either way; no pin is ever written by machine.
Landing: the PR's own Regenerate the shard-timings dataset check must be green on the final head (with A it merges 57 measured + 14 carried and renders the would-be PR body to the step summary on a PR run); then pure code ⇒ the seat's in-seat review, ready, SQUASH auto-merge. The first scheduled run's PR retires #16173 and #16222 by hand (the refresh PR carries only Refs); recorded for the next seat. B (a weekly cold full-workspace run) and C (an estimate fallback) are declined on the axes the report states; D is the interim the PR already implements and is not the landing shape.
Supersedes 5567426497 (needs_decision) and 5565145010 before it. The seat ruling at 08:11Z is implemented in full — Q1 to A with all five conditions, Q2 to A as built — and the landing condition it set is met: the lane's own Regenerate the shard-timings dataset check is GREEN on the final head, with all 40 checks on that head complete and none non-green. The PR is still a draft and is not enqueued.
{
"issue": 16464,
"status": "done",
"branch": "claude/issue-16464-shard-timings-refresh-workflow",
"pr": "https://github.com/objectstack-ai/objectstack/pull/16498",
"premise_still_valid": true,
"summary": "Supersedes report 5567426497 (needs_decision) and 5565145010 before it. The seat ruled Q1 -> A and Q2 -> A at 08:11Z; both are implemented, and the landing condition the ruling set is met: the lane's own pull_request check `Regenerate the shard-timings dataset` is GREEN on the final head 94eeaa19b1, with all 40 checks on that head complete, 32 success, 8 skipped, zero non-green. The PR now carries the closing keyword for BOTH cards. Q1 -> A, merge not replace, with all five conditions met. `--merge-into` is a generator mode, so the workflow still writes no byte the generator did not emit. A package the pass did not measure keeps its last measured weight ONLY when a turbo cache HIT witnesses that its inputs are unchanged -- the witness is the whole argument, because a HIT is not missing data but positive evidence the package has not changed since the run whose output was replayed, so the old number still describes it and the file keeps its invariant that every weight is a real measurement. Carried packages are named in a top-level `carriedOver` beside `skippedAsCached`. I picked the single `provenance.measuredAt` paired with that list over per-package dates, and the reader forces it rather than taste: partition-test-shards.mjs reads `packages` as name -> NUMBER (`timings.packages[name] / sliceCount`) and never opens `provenance` at all, so per-package dates would mean changing that shape and every consumer of it, while the list needs no partitioner change. Coverage is judged on measured union carried, and the two ways a package can be missing are reported separately because only one is a regression: one that HAD a weight and has neither refuses by name; a workspace package that never had one is NAMED as partitioner-estimated but is not a refusal, since this refresh did not change its standing -- nothing is silently estimated either way. A carry cannot manufacture a refresh: an all-cached pass still refuses. The generator header sentence \"Download all six from any green queue build\" is corrected in the same change to the measured fact. Q2 -> A as built: the partitioner self-test runs on the refreshed dataset and its verdict is quoted verbatim into the PR body, and the live green run proves it end to end -- `PARTITIONER_EXIT: 1`, so the balancing pins DID red on the honest refresh and the lane reported rather than acted, exactly as designed. No pin is written by machine. The reverse verification resolved to A as ruled: the live lane covered the workspace from ONE run (34087495912 at c383352cb7) plus the carry, composed a 70-line PR body and rendered it to the step summary with no branch pushed, no PR opened and no label written. The rider #16473 is unchanged and remains fully delivered; the live runs strengthened it, since the lane now feeds the generator run-fenced input as a matter of course. Recorded in the PR body as the ruling asked: the refresh PRs this lane opens carry only `Refs`, so cards #16173 and #16222 stay open when the first refresh merges and are retired by hand.",
"tests": "FINAL HEAD 94eeaa19b1. Every exit code captured by redirecting first and reading the status before any pipe.\n\n(1) THE LANDING CONDITION, met: the PR's own `Regenerate the shard-timings dataset` check is completed / SUCCESS on this head (job 101671848579, run 34099951008). All 40 checks on the head are complete — 32 success, 8 skipped, 0 pending, 0 non-green — polled in the foreground to completion. NOTE ON THAT POLL: my first attempt passed the whole API response as an argv and every iteration died with \"Argument list too long\" plus a JSON parse error, so all 40 of its readings were void; it was redone through a file and only that run is reported.\n\n(2) WHAT THE GREEN RUN ACTUALLY DID, from its log: RUN_COUNT 1, RUNS 34087495912, HEAD_SHA c383352cb7 — the merge rule let ONE run plus the cache-hit carry cover the workspace, where before the merge seven accumulated runs reached only 57 of 71 and refused. PARTITIONER_EXIT 1, so the balancing pins red on the honest refresh and the lane quoted the verdict into the body instead of acting on it (Q2 -> A, live). It composed a 70-line PR body and rendered it to the step summary; no branch pushed, no PR opened, no label written, which is the pull_request posture working.\n\n(3) THE MERGE, EXERCISED AGAINST THE REAL COMMITTED DATASET before it was pushed. Two synthetic runs measuring 55 of the 71 real packages, the other 16 fed as cache HITs: `71 package(s) covered (55 measured in these runs, 16 carried on a cache-hit witness), 71 required` — coverage exit 0. THE WITNESS BOUNDARY, the case that separates a merge from \"keep whatever was there\": withholding two packages ENTIRELY (no MISS and no HIT, as a rename, removal or failed suite would) left them uncarried and refused by name — `2 package(s) HAD a measured weight and this refresh neither re-measured them nor found a turbo cache HIT to witness that they are unchanged: @objectstack/cloud-connection, @objectstack/downstream-contract` — exit 1. Both directions measured, not argued.\n\n(4) GENERATOR SELF-TEST: `node scripts/measure-test-shard-timings.mjs --self-test` exit 0, verdict \"measure-test-shard-timings: self-test OK\". Nine new merge cases covering every case the ruling names — a carried package (weight unchanged AND named), a measured package (new weight, NOT reported as carried), HIT-witnessed versus absent (only the first carries, and the absent one is in neither `packages` nor `carriedOver`), an empty `carriedOver` on a plain replace so \"absent key\" and \"nothing carried\" cannot read alike, an all-cached pass still REFUSING so carrying can never manufacture a refresh, and monotone accumulation (one run carries 1, adding a second carries 0 and the package takes its real 480). Battery floor 41 -> 50, the count MEASURED by setting the floor absurdly high and reading \"registered 50 case(s)\" back.\n\n(5) SELECTOR SELF-TEST: `node scripts/ci/select-shard-timings-run.mjs --self-test` exit 0. Five new cases for the union semantics: a carried package counts as covered with the carried/fresh split reported correctly, a package that lost its weight still refuses, a never-measured workspace package is named but is NOT a regression, never-measured is not conflated with carried, and a dataset with no `carriedOver` key at all still reads. Floor 30 -> 35, measured the same way.\n\n(6) THE OTHER CONSUMER, unchanged: `partition-test-shards.mjs --self-test` exit 0, verdict \"partition-test-shards: self-test OK (71 measured packages -> 72 shard items, 6 shards, max/mean 1.00x <= 1.3x, floor 404s, bins 672/672/672/671/672/671s)\". This is also the evidence for the `measuredAt` shape choice: that script reads `packages` as name -> number and never opens `provenance`, so the carriedOver-plus-single-date shape needs no change in it, and none was made.\n\n(7) GATE UNION on 94eeaa19b1: all 49 families dispatch-gates derives for this four-path change set were run — 49 green, 0 red — and reconciled: \"dispatch-gates --ran: 49 derived famil(ies) accounted for — 49 run, 0 NOT-MEASURED.\" No stale tree. `pnpm check:pm-dispatch-gates` itself: \"dispatch-gates self-test: 1534 cases pass.\"\n\n(8) WHOLE-REPO LINT on 94eeaa19b1, not a narrowing: `pnpm lint` = `eslint . --no-inline-config` through `bash scripts/pm/os-verify-lock.sh`, slot issue-16464, LINT-EXIT=0; the lock printed \"held the lock 112s (1m52s), waited 0s\" with its own shared-box caveat.\n\n(9) GOVERNED-MERGE PREDICATE on the final four-path list: exit 0, \"0 of 4 path(s) hit the register (5 surfaces, repo-agnostic). NOT governed\". The PR body's card relations are machine-checked too: `Part-of PR must not also close its card` and both single-writer/issue-claim checks are green on the head.\n\n(10) EARLIER LEGS THAT STILL STAND, unchanged by this round and re-run green in the union above: the #16473 red-then-green on the exported buildDataset (1000 -> 600 sliced median, 300 control, 800 -> 600 splice now named \"cli in run C (missing 2/2)\"); the check-self-test-wired right-boundary ablation (mutated: production GREEN exit 0 while --self-test RED exit 1 naming the new pin; restored: git hash-object db95b1025a4ba4d6a310268161d903b6d44af716 equal to the HEAD blob, git diff HEAD empty); and the merge-tree proof against #16471, which has since landed and merged into this branch cleanly.\n\nNOT MEASURED, reported as such: `pnpm check:single-claim-paths` exits 2 = PREREQUISITE NOT MET off a PR context (it is green on the PR itself); the eight families dispatch-gates marks NOT MEASURED because their argv takes a value only a CI run supplies; and `workflow_dispatch` on the branch, which this seat cannot perform (403 Resource not accessible by integration, no Actions write scope) and which the pull_request trigger replaced by ruling.",
"mcp_calls": "4 — all mcp__github__get_job_logs, to read the three live runs of the lane (two red, one green) because GET /actions/jobs/{id}/logs redirects to the blob host this container is denied (curl 56, CONNECT 403) while the MCP path returns content directly. Everything else went over repo-scoped REST, which probed 200: both cards and their comments including the seat ruling, #16173/#16222 bodies and 31 comments, the open-PR list, per-PR file lists for all 33 open PRs, check-runs and the foreground check poll, the workflow_dispatch attempt, the file-surface supplement plus its PATCH and read-backs, three report comments and two pointer comments, PR creation, four PR body PATCHes with read-backs, and the label POST and read-back.",
"open_questions": [],
"out_of_scope_findings": [
"FIXED IN PLACE under the bounded exemption, declared as a file-surface supplement on the card BEFORE the edit (comment 5564911725): scripts/check-self-test-wired.mjs keyed scripts/test-shard-timings.json as scripts/test-shard-timings.js — js is a prefix of json and its invocation extractor had a left boundary but none on the extension — minting the exact phantom key its left boundary was added to abolish, silent in both directions. Not separable: ruling 2 requires the workflow to name that path. Four conditions checked; red-then-green ablation recorded in tests item 10; four cases pin it, floor 4 -> 8.",
"FIXED IN PLACE, same exemption and same unavoidability: this lane was the first gate family in the tree reachable ONLY on a schedule, which reds dispatch-gates because such a family appears on no card gate list. Closed with the pull_request posture half-state-patrol.yml and required-set-patrol.yml already keep, write-skipped on a PR run. It paid for itself three times over: it is what delivered the reverse verification, what measured the coverage finding that forced the merge rule, and what now proves the merge rule green.",
"NOT FILED, no defect remains: measure-test-shard-timings.mjs --self-test was run by NO workflow before this PR (grep -rn measure-test-shard-timings .github/ = 0 hits), so its battery evaluated never in CI. Not a check-self-test-wired breach, because that gate scopes its population to scripts a workflow names and none did. This PR closes it as a side effect — the new workflow names and self-tests it, and check-self-test-wired now reports 184 wired scripts, green.",
"CORRECTED IN THIS PR rather than filed, per ruling condition 4: the generator header's documented procedure (\"Download all six from any green queue build and re-run the generator\") was optimistic in a way nobody had measured — a single run yields 2-52 of ~71 packages depending only on cache warmth. It now states the measured fact and the accumulate-plus-carry procedure, so the next person refreshing by hand cannot believe they measured the workspace when they measured a seventh of it.",
"RECORDED IN THE PR BODY for whoever lands this, as the ruling directed: the refresh PRs this lane opens carry only `Refs` and never a closing keyword, because a weekly lane cannot know which cards a given run ought to retire and a baked-in keyword would have every subsequent Monday try to re-retire cards already retired. Cards #16173 and #16222 therefore stay open when the first refresh PR merges, and whoever lands that first refresh retires them by hand or adds the keyword to that one PR."
]
}
ACCEPT — flight C2a, PR #16498 at head 94eeaa19b1 (#16464 + rider #16473), on the seat's ruling 5567471502 (Q1 → A, Q2 → A) and the dev's report 5567897750.
Read in a detached compare worktree against the merge base 70e77ec3b5; every reading below is the seat's own, taken on the final head.
Diff: 4 files, +1832 / −49 — .github/workflows/shard-timings-refresh.yml (new), scripts/ci/select-shard-timings-run.mjs (new, the run selector with --candidates / --check-coverage), scripts/measure-test-shard-timings.mjs (the rider's run-fenced slice ledger + the ruled --merge-into mode), scripts/check-self-test-wired.mjs (the .js/.json boundary fix declared on the card before the edit, 5564911725).
Q1 → A as ruled: a package the pass did not measure keeps its weight only on a turbo cache-HIT witness and is named in carriedOver beside skippedAsCached; coverage is judged on measured ∪ carried; a package that had a weight and has neither is refused by name, a never-measured workspace package is named as partitioner-estimated and is not a regression; an all-cached pass still refuses; the generator's header now states the measured fact (a single run yields 2–52 of 71). Single provenance.measuredAt paired with the list, because partition-test-shards.mjs reads packages as name → number and never opens provenance — the reader decides the shape.
Q2 → A as ruled: the partitioner self-test runs on the refreshed dataset and its verdict is quoted into the PR body; the live green run recorded PARTITIONER_EXIT: 1 — the pins redded on the honest refresh and the lane reported instead of acting; no pin is written by machine.
The lane: schedule Monday 05:30Z + workflow_dispatch + a pull_request trigger paths-filtered to its own files; top-level permissions: contents: read, the job holds contents: write and pull-requests: write; the write step (git push + gh pr create) is gated on changed == 'true' && github.event_name != 'pull_request', the dry-run half renders the would-be PR body to the step summary.
Reverse verification (ruled A): the lane's own pull_request run on this head is green — one run (34087495912 at c383352cb7) plus the carry covered 71 of 71, a 70-line body rendered, no branch pushed, no PR opened, no label written.
Seat-run in the compare worktree: measure-test-shard-timings.mjs --self-test OK (floor 50), select-shard-timings-run.mjs --self-test OK (floor 35), partition-test-shards.mjs --self-test OK (71 packages → 72 items, 6 shards, max/mean 1.00×), check-self-test-wired.mjs 185 wired of 197; check-governed-merges --test on the 4 files → 0 of 4 hit the register, NOT governed, exit 0.
CI on 94eeaa19b1: 40 check runs, 32 success + 8 skipped, none red, including Regenerate the shard-timings dataset.
Two gate defects fixed in place under the bounded exemption, both declared on the card before the edit: the check-self-test-wired extension boundary, and the scheduled-only gate family (closed with the pull_request posture the other patrol workflows keep).
measure-test-shard-timings.mjs --self-test was run by no workflow before this PR; the new workflow names it, check-self-test-wired now reports it wired. No card.
The container token cannot workflow_dispatch (REST 403) and job-log downloads redirect to the blob host: on the ledger already.
Landing: pure code ⇒ the seat flips the PR ready and arms SQUASH auto-merge now; on merge strip #16464 and #16473; the first scheduled run is Monday 05:30Z (or a maintainer's workflow_dispatch sooner) and its PR is #16173's deliverable.
Activity
claude commentedon Sep 7, 2026
Claim: PM loop round 9 — flight C2a, maintainer-directed (「同意你的建议,你负责执行派发所有可行的优化」, 2026-09-07): the scheduled shard-timings refresh workflow, with #16473 folded in as its prerequisite — flight C1 (#16173) measured at 03:2xZ that the artifact host (
productionresultssa*.blob.core.windows.net) is denied to every agent container by egress policy, so the documented refresh cannot run from a dev worktree; the refresh therefore runs IN-RUNNER, where.turbo/runs/*.jsonalready sits, and this workflow's firstworkflow_dispatchis the refresh itself — the PR it opens is #16173's deliverable. Rider #16473:buildDatasetinscripts/measure-test-shard-timings.mjsbypasses its median rule for a file-sharded package (last run wins; slices from different runs summed into a weight no run measured) — fixed first, with self-test cases, so the refresh's@objectstack/cliweight is a median like every other package's.Session:
session_019RfFHiRCSs3JXLK4cwcfoxBranch:
claude/issue-16464-shard-timings-refresh-workflowWorktree:
objectstack-issue-16464Domain:
domain:devx(dispatched by the skills seat on the maintainer's direction)File surface:
.github/workflows/shard-timings-refresh.yml(new),scripts/measure-test-shard-timings.mjs(the slice ledger's merge rule + self-test), one helper underscripts/ci/if the pin update needs one;scripts/test-shard-timings.jsonis written only by the workflow run, never by hand. ⛔ci.ymluntouched; notimeout-minutes, matrix or bound change.Container & model: S,
mode:subagent, opus —dispatch-gates.mjs --tier: no path-derived mandate.Clause-②: no
Serial: no open PR touches the measure script or the JSON (C1 produced no PR); flights V and X hold
ci.yml, which this card does not touch. Not governed ⇒ in-seat review, then the seat flips ready and arms SQUASH auto-merge;skip-changeset. The PR carriesFixes #16464andFixes #16473; #16173 and #16222 areRefs(they close when the workflow's first refresh PR merges).Generated by Claude Code
claude commentedon Sep 7, 2026
File-surface supplement to the
Claim:above (not a second claim; branch, session and worktree are unchanged). One file is added to the declared surface, under the bounded in-place exemption, because the card's ruled deliverable cannot exist without it.Added:
scripts/check-self-test-wired.mjs— its invocation extractor had a left boundary but none on its extension alternation, andjsis a prefix ofjson. The first workflow line naming a.jsonunderscripts/therefore matched only as far as the.jsand minted a phantom key with no file behind it:That is exactly the phantom the gate's left boundary was added to abolish, arriving from the other end, and it is silent in both directions — the real file is audited by nothing and the phantom key reconciles against no carrier.
Why this is not scope creep: ruling 2 requires the workflow to invoke the generator with
--out scripts/test-shard-timings.json, and rulings 3 and 4 require it to byte-compare and commit that same path. Writing the path is the card's content, so there is no version of this deliverable that does not trip the gate. It was invisible until now only because no workflow had yet named a.jsonunderscripts/.The four conditions, each checked rather than asserted:
scripts/ci/, and onlyselect-shard-packages.sh/.selftest.sh, which are different files from this card'sselect-shard-timings-run.mjs. Nothing open touches the gate.check-self-test-wiredand its--self-testwere already in this card's derived family before the edit.Proven red-then-green rather than argued. With the boundary removed and the mutation confirmed on disk (anchor 1 to 0,
git diff --numstat= 1 1), the production run stays GREEN at exit 0 while--self-testgoes RED naming the new pin; restoring gives back the byte-identical blob (db95b1025a4ba4d6a310268161d903b6d44af716, equal to the HEAD blob) and the self-test returns to exit 0. Four cases pin it, control first, battery floor 4 to 8.Declared surface is therefore:
.github/workflows/shard-timings-refresh.yml(new),scripts/ci/select-shard-timings-run.mjs(new),scripts/measure-test-shard-timings.mjs,scripts/check-self-test-wired.mjs.ci.ymlis untouched;scripts/test-shard-timings.jsonis written only by a workflow run, never by hand.Generated by Claude Code
claude commentedon Sep 7, 2026
os-dev-report
{ "issue": 16464, "status": "done", "branch": "claude/issue-16464-shard-timings-refresh-workflow", "pr": "https://github.com/objectstack-ai/objectstack/pull/16498", "premise_still_valid": true, "summary": "Both cards land on one branch, rider first. #16473: buildDataset's slice ledger was keyed (package, slice count, slice index) with no notion of WHICH RUN a summary came from, so every run collapsed into one entry and both merge rules broke at once — a sliced package took whichever run was read last (1000s recorded where the median of three runs is 600s) and a set completed from slices of DIFFERENT runs was summed into a weight no run observed (800s) with skippedIncompleteSlices empty. The ledger is now keyed by run first: slices are summed WITHIN a run, the per-run sums are medianed ACROSS runs exactly as an unsliced package's observations are, and a run that cannot assemble every slice contributes no sample and is named with its run. The grouping key comes from the caller via `--run` because a summary carries no run id; undeclared multi-run input is REFUSED by name with `--run` as the remedy, which is what makes the undeclared single-run default (what the workflow feeds) safe. provenance.mergeRule, which claimed \"median across summaries\" and was only ever true of unsliced packages, now states both composed rules. #16464: .github/workflows/shard-timings-refresh.yml runs weekly (Monday 05:30Z) plus workflow_dispatch, selects a run, downloads its six run-summary artifacts, regenerates, and opens a PR only on a byte difference. It DOWNLOADS the artifacts (ruling 2) rather than generating from the runner's own summaries: a separate lane has no .turbo/runs of its own, and producing a complete set would mean an unsharded full-workspace suite — the \"special measurement run\" this tooling exists to avoid. Downloading is safe here precisely because it is unreachable elsewhere: the blob host is denied to every agent container by egress policy and native to a GitHub runner, so running in-runner removes the channel from the loop rather than working around the denial. Run selection is a tested unit (scripts/ci/select-shard-timings-run.mjs, 30 self-test cases) because \"newest\" is wrong three measured ways — cancelled runs, cache replays, expired artifacts — and it judges the replay case on what was MEASURED (a package the committed dataset measured and the workspace still holds, absent from the refresh, has silently become an estimate) rather than on a duration threshold. The partitioner's balancing pins are RUN but never acted on: they red by design on a refresh that outgrows the bound, and the remedy they name is a decision, so the verdict is quoted verbatim into the PR body and the PR is opened either way. PM-side note: the assignee on both cards was already set (os-steve) and the newest Claim on each names this branch, so nothing was half-stated.", "tests": "ALL READINGS ON HEAD ea96c274e1 (this branch after merging origin/main at 4500bc5d79), and every exit code captured by redirecting first and reading the status before any pipe.\n\n(1) RIDER, RED THEN GREEN, on the exported buildDataset — flight C1's scratch driver reproduced. RED leg (before the fix): three runs measuring 400/600/1000s recorded `cli = 1000` (last run wins) where the median is 600; the unsliced control fed the same shape correctly recorded 300 (so the median rule was alive and the sliced path alone bypassed it); the cross-run splice recorded `cli = 800` with `skippedIncompleteSlices = []`. GREEN leg (identical driver, after the fix): 600 / 300 / 600, and `skippedIncompleteSlices = [\"cli in run C (missing 2/2)\"]`. NOTE ON THE FIRST ATTEMPT: my first red-leg run exited with MODULE_NOT_FOUND — a relative ESM import resolving from the script file rather than cwd — which is a script that never reached its body, not a reading; it was re-run with an absolute specifier and only that run is reported.\n\n(2) RIDER SELF-TEST: `node scripts/measure-test-shard-timings.mjs --self-test` exit 0, verdict line `measure-test-shard-timings: self-test OK`. Seven new cases, battery floor raised 34 -> 41 (the count was MEASURED by setting the floor absurdly high and reading `registered 41 case(s)` back, not counted by eye).\n\n(3) RIDER THROUGH THE REAL CLI, on synthetic six-shard summaries, which is the path the workflow actually takes: three declared runs (`--run A ... --run B ... --run C ...`) gave `@objectstack/cli = 600` and `@objectstack/spec = 300` with `provenance.runs = [\"A\",\"B\",\"C\"]`; the SAME six files with no `--run` exited 1 naming the slice and the remedy (`@objectstack/cli slice 1/2 was measured twice in this summary set (200s and 300s) ... Group them with --run RUNID`); one run with no `--run` exited 0 and gave 600 with no ceremony. A realistic single-run set (five whole-package shards plus the CLI sliced across shards 5 and 6) summed the CLI to 1250s within the run. Dropping shard 6 from that set left the CLI OUT of `packages` entirely with `skippedIncompleteSlices = [\"@objectstack/cli (missing 2/2)\"]` and a loud stderr warning — and the workflow's own coverage check then rejected that run by name (`COVERAGE SHORTFALL -- 2 package(s) ... are NOT measured by this refresh: @objectstack/cli, @objectstack/console`), so the two guards compose.\n\n(4) THE OTHER CONSUMER: `partition-test-shards.mjs --self-test` exit 0, verdict `partition-test-shards: self-test OK (71 measured packages -> 72 shard items, 6 shards, max/mean 1.00x <= 1.3x, floor 404s, bins 672/672/672/671/672/671s)`; and `--check-drift` driven on a synthetic shard-5 summary still reads the slice correctly (`@objectstack/cli: predicted 229.1s, measured 600.0s (2.62x)` — 229.1 is the dataset weight halved by the slice count), so the rider did not disturb the drift path.\n\n(5) NEW SELECTOR: `node scripts/ci/select-shard-timings-run.mjs --self-test` exit 0, `select-shard-timings-run: self-test OK`, 30 cases (measured the same way), floor 30, one declared battery. Its `--check-coverage` mode was exercised against the REAL committed dataset: a synthetic 31-of-71-package refresh was rejected naming all 40 lost packages; the identical dataset passed with `coverage OK -- 71 package(s) measured, 71 required`.\n\n(6) GATE UNION: all 49 families `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derives for this four-path change set were run — 49 green, 0 red — and reconciled: `✓ dispatch-gates --ran: 49 derived famil(ies) accounted for — 49 run, 0 NOT-MEASURED.` The derivation reported no stale tree. `pnpm check:pm-dispatch-gates` itself: `✓ dispatch-gates self-test: 1534 cases pass.`\n\n(7) WHOLE-REPO LINT (not a narrowing): `pnpm lint` = `eslint . --no-inline-config` through `bash scripts/pm/os-verify-lock.sh` with `OS_VERIFY_LOCK_SLOT=issue-16464`, `LINT-EXIT=0`. The lock printed `held the lock 90s (1m30s) · waited 86s (1m26s)` and its own caveat that these are shared-box seconds. It also warns that a `;`-sequenced batch reports only the last part's exit — here the batch IS the single lint command plus its echo, so `LINT-EXIT=0` is the lint's own status.\n\n(8) GOVERNED-MERGE PREDICATE, on the final file list: `check-governed-merges --test` exit 0, `governed-surface predicate: 0 of 4 path(s) hit the register (5 surfaces, repo-agnostic). ✅ NOT governed`.\n\n(9) GATE-FIX ABLATION, with both legs proved on disk. First mutation attempt was a NO-OP — a `perl -0pi` anchor matched zero times, `git diff --stat` empty — so those readings were discarded and it was redone through a driver that REFUSES unless the anchor matches exactly once. Mutated (right boundary removed, confirmed: boundary-present 1 -> 0, `git diff --numstat` `1 1`): production run GREEN at exit 0, `--self-test` RED at exit 1 naming the new pin verbatim (`scripts/data.json was truncated to scripts/data.js and keyed as a script — a phantom path with no file behind it`) plus the live-corpus battery. That double is exactly the shape check-self-test-wired's own header documents for this gate class. Restored with `git checkout HEAD -- \"$REPO_ROOT/PATH\"` (absolute, HEAD-pinned): `git hash-object` = `db95b1025a4ba4d6a310268161d903b6d44af716`, equal to the HEAD blob, `git diff HEAD` empty, self-test back to exit 0.\n\n(10) SERIALIZATION: merge-tree against #16471's head `05cb57b43c` twice, both exit 0, zero conflict markers, same tree `65319f6c34`. #16471 has since landed and was merged into this branch cleanly. All 33 open PRs were read for their file lists; none touches `scripts/measure-test-shard-timings.mjs`, `scripts/check-self-test-wired.mjs`, `scripts/test-shard-timings.json` or the new workflow.\n\nNOT MEASURED, reported as such rather than as passes: `pnpm check:single-claim-paths` exits 2 = PREREQUISITE NOT MET off a PR context (it needs PR_NUMBER/GITHUB_TOKEN and runs on the PR in CI), and the eight families dispatch-gates itself marks NOT MEASURED because their argv takes a value only a CI run supplies. `workflow_dispatch` on this branch is likewise NOT MEASURED — see open_questions.", "mcp_calls": "0 — the channel probe (a repo-scoped REST read) answered 200, so every GitHub read AND write went over REST: both cards and their comments, #16173's and #16222's bodies and 31 comments, the open-PR list, per-PR file lists for all 33 open PRs, the file-surface supplement comment plus its PATCH and two read-backs, the report comment, the pointer comment and the PR creation. No MCP GitHub tool was called.", "open_questions": [ { "question": "Ruling 4 asked for a `workflow_dispatch` on the branch as reverse verification. This seat cannot perform it: POST /actions/workflows/shard-timings-refresh.yml/dispatches answers 403 `Resource not accessible by integration`, so the session credential carries no Actions write scope (and a workflow absent from the default branch is often not dispatchable at all, which this 403 cannot distinguish from the scope answer). What should stand as this card's reverse verification?", "options": [ "A. The `pull_request` trigger this PR already carries. It was added for an unrelated reason — dispatch-gates reds on a scheduled-only gate family — and it happens to satisfy ruling 4 better than a dispatch would: opening this PR runs the lane end to end on a real runner against live artifacts (selection, download, regeneration, coverage check, byte comparison, partitioner verdict), with only the write skipped and the would-be PR body rendered to the run's step summary. Costs nothing, and the evidence is on this PR's own checks.", "B. A maintainer or a seat with Actions write dispatches the workflow once after this lands. Gives the literal ruled artefact including the write path (branch push, PR creation, label write), which A deliberately does not exercise.", "C. Grant the agent session Actions write. Outside the repo, and it buys one reverse verification at the cost of every agent seat being able to start any workflow." ], "recommendation": "A now, B as the first scheduled run's natural check — they are complementary, not competing. REAL DEMAND: the property that actually needs proving is that the lane picks a defensible run and regenerates correctly, and A exercises all of that against live data; the write path is three well-trodden house idioms (branch push, `gh pr create`, additive label POST) and is the part a scheduled run proves for free on Monday. LONG-TERM SOUNDNESS: A is contract-first — the lane proves itself on every PR that touches it, forever, rather than once by hand; B is a one-time observation that re-rots. HARDER FOR AN AI TO GET WRONG: A is the option that keeps a future edit to this lane from shipping unexercised, which is the same failure class the card exists to end; C is the opposite, widening a credential so a check can be performed. STARTUP FOCUS: A adds nothing — the trigger is already there and was required independently — while C asks for an infrastructure grant with no pull behind it. The one thing I would not do is weaken the coverage check or the eligibility rule to make a first run land more easily." }, { "question": "Ruling 2 asked for 'the pin update by the partitioner's own refresh procedure', reusing flight C1's mechanics. C1 landed no PR and left no script, and the partitioner has no mechanical pin-refresh procedure to reuse: its pin 3c substitutes CLI_MEASURED = 1231.52 and its counterfactual leg says in terms that re-deriving or retiring the slice count is a decision ('Re-derive the slice count (or retire it) instead of leaving a pin that cannot fail'). So the workflow updates NO pin. Is that the intended reading?", "options": [ "A. As built: the workflow runs `partition-test-shards.mjs --self-test` on the refreshed dataset, quotes the verdict verbatim into the PR body (with a named block when it reds, restating that the remedy is the slice count and never the bound), and opens the PR either way. No pin is ever written by machine.", "B. Teach the workflow to re-derive FILE_SHARDED_PACKAGES arithmetically and commit the new slice count alongside the dataset. Mechanically possible — the derivation is written out in the partitioner's header — but it makes a machine the author of a balancing decision, and pin 3c's counterfactual leg exists precisely to force a human to look.", "C. Withhold the PR when the pins red, and fail the workflow instead." ], "recommendation": "A. REAL DEMAND: the pins are expected to red on the first honest refresh — flight C1 measured shards 5 and 6 at 1.54x-2.38x their predictions on all three green runs — so this is the common case, not a corner. LONG-TERM SOUNDNESS: A is the no-workaround shape; B is a machine quietly re-deriving the number that decides how CI is scheduled, and the partitioner's header calls moving a bound to absorb a gap 'the one move that cannot be right'. HARDER FOR AN AI TO GET WRONG: A puts the arithmetic in front of a person with the measurement attached; B is exactly the automation that would let a bad refresh land quietly, and C is the silent-rot failure this card exists to remove — a refusal to open the PR would discard the measurement and leave the dataset stale with nobody told. C is refused outright for that reason. STARTUP FOCUS: A adds no surface; B adds a code-writing path to a scheduled workflow, which is a large new capability for one number a person can set in a minute." } ], "out_of_scope_findings": [ "FIXED IN PLACE rather than filed, under the bounded exemption, and declared as a file-surface supplement on #16464 before the edit (comment 5564911725): scripts/check-self-test-wired.mjs keyed `scripts/test-shard-timings.json` as `scripts/test-shard-timings.js` — `js` is a prefix of `json` and its invocation extractor had a left boundary but none on the extension — minting the exact phantom key its left boundary was added to abolish. Not separable from this card: ruling 2 requires the workflow to name that path, so no version of this deliverable avoids it. Four conditions checked, not asserted; red-then-green ablation in `tests` item 9.", "FIXED IN PLACE, same reasoning: the refresh lane was the first gate family in the tree reachable ONLY on a schedule, which reds `dispatch-gates` because such a family appears on no card gate list. Closed with the `pull_request` posture half-state-patrol.yml and required-set-patrol.yml already keep, write-skipped.", "NOT FILED, no defect: `measure-test-shard-timings.mjs --self-test` was run by NO workflow before this PR (`grep -rn measure-test-shard-timings .github/` = 0 hits), so its 34-case battery evaluated never in CI. It was not a `check-self-test-wired` breach because that gate scopes its population to scripts a workflow names, and none did. This PR closes it as a side effect — the new workflow names and self-tests it, and `check-self-test-wired` now reports 184 wired scripts, green. Recording rather than filing, since the condition no longer exists on this branch.", "OBSERVATION for PM, no card filed and no action taken: the refresh PR the workflow opens carries only `Refs`, never a closing keyword, because a weekly lane cannot know which cards a given run ought to retire. Cards #16173 and #16222 therefore stay open when the first refresh PR merges, contrary to the claim comment's expectation (\"they close when the workflow's first refresh PR merges\"). Whoever lands that first refresh should close them by hand or add the keyword to that one PR. Flagging rather than building it in: a keyword baked into the workflow would try to close already-closed cards on every subsequent Monday." ] }Generated by Claude Code
claude commentedon Sep 7, 2026
os-dev-report
Supersedes the earlier os-dev-report on this card (comment 5565145010). That one was written before the lane had ever run; its two live runs then measured a fact that changes the verdict from done to needs_decision. The rider is unaffected and remains fully delivered.
{ "issue": 16464, "status": "needs_decision", "branch": "claude/issue-16464-shard-timings-refresh-workflow", "pr": "https://github.com/objectstack-ai/objectstack/pull/16498", "premise_still_valid": true, "summary": "SUPERSEDES my earlier os-dev-report on this card (comment 5565145010, status done): that report was written before the lane had run, and the lane's first two live runs measured a fact that changes the verdict. The rider #16473 is fully repaired and stands on its own. The workflow card is implemented and proven live in every part except its acceptance rule, which now needs a ruling, so the PR binds the closing keyword to the rider only and declares itself a partial delivery of the workflow card. THE RIDER (#16473): buildDataset's slice ledger was keyed (package, slice count, slice index) with no notion of WHICH RUN a summary came from, so every run collapsed into one entry and both merge rules broke at once — a sliced package took whichever run was read last (1000s recorded where the median of three runs is 600s) and a set completed from slices of DIFFERENT runs was summed into a weight no run observed (800s) with skippedIncompleteSlices empty. The ledger is now keyed by run first: slices are summed WITHIN a run, the per-run sums are medianed ACROSS runs exactly as an unsliced package's observations are, and a run that cannot assemble every slice contributes no sample and is named with its run. The grouping key comes from the caller via `--run` because a summary carries no run id; undeclared multi-run input is REFUSED by name with `--run` as the remedy. provenance.mergeRule, which claimed 'median across summaries' and was only ever true of unsliced packages, now states both composed rules. THE WORKFLOW (#16464): .github/workflows/shard-timings-refresh.yml runs weekly (Monday 05:30Z) plus workflow_dispatch plus a lane-scoped pull_request trigger, selects runs, downloads their run-summary artifacts, regenerates, and opens a PR only on a byte difference. It DOWNLOADS artifacts (ruling 2) rather than generating from the runner's own summaries: a separate lane has no .turbo/runs of its own and producing a complete set would mean an unsharded full-workspace suite, the 'special measurement run' this tooling exists to avoid. Downloading is safe here precisely because it is unreachable elsewhere — the blob host is denied to every agent container by egress policy and native to a GitHub runner — so running in-runner removes the channel from the loop rather than working around the denial. Run selection is a tested unit (scripts/ci/select-shard-timings-run.mjs, 30 self-test cases). The partitioner's balancing pins are RUN but never acted on: they red by design on a refresh that outgrows the bound, and the remedy they name is a decision, so the verdict is quoted verbatim into the PR body. WHAT THE LIVE RUNS FOUND, AND WHY THIS IS needs_decision: no single green run measures the workspace, and neither does the union of all of them. Of the seven retained green push runs on main the best measured 52 of 71 packages and the others 2, 3, 13, 18, 22 and 49; accumulating all seven (which the lane now does, each run fenced by its own --run group — a path only the rider made correct) reaches 57 of 71 and converges there, because 14 packages are turbo cache HITs in every retained run and the generator refuses hits. So the ruled acceptance rule — regenerate the dataset from a green run's artifacts — cannot be satisfied as written, and the lane currently refuses loudly rather than writing a dataset in which 14 packages would silently fall back to the test-file-count estimate. Settling that is a decision about what the dataset MEANS (open_questions 1), not a threshold I should pick. Two gate defects were fixed in place under the bounded exemption, both declared on the card before the edit and both unavoidable for this deliverable; they are in out_of_scope_findings. PM-side note: the assignee on both cards was already set (os-steve) and the newest Claim on each names this branch, so nothing was half-stated.", "tests": "FINAL HEAD 4fe161b41a. Every exit code captured by redirecting first and reading the status before any pipe; no verdict below comes from a pipeline status.\n\n(1) RIDER, RED THEN GREEN, driving the exported buildDataset the way flight C1 did. RED (before the fix): three runs measuring 400/600/1000s recorded cli = 1000 (last run wins) where the median is 600; the unsliced control fed the same shape correctly recorded 300, proving the median rule was alive and the sliced path alone bypassed it; the cross-run splice recorded cli = 800 with skippedIncompleteSlices = []. GREEN (identical driver, after the fix): 600 / 300 / 600, with skippedIncompleteSlices = [\"cli in run C (missing 2/2)\"]. My FIRST red-leg attempt exited MODULE_NOT_FOUND — a relative ESM import resolving from the script file rather than cwd — which is a script that never reached its body and not a reading; it was re-run with an absolute specifier and only that run is reported.\n\n(2) RIDER SELF-TEST: node scripts/measure-test-shard-timings.mjs --self-test exit 0, verdict line \"measure-test-shard-timings: self-test OK\". Seven new cases, battery floor 34 -> 41; the count was MEASURED by setting the floor absurdly high and reading \"registered 41 case(s)\" back, never counted by eye.\n\n(3) RIDER THROUGH THE REAL CLI on synthetic summaries: three declared runs gave @objectstack/cli = 600 and @objectstack/spec = 300 with provenance.runs = [\"A\",\"B\",\"C\"]; the SAME six files with no --run exited 1 naming the slice and the remedy; one run with no --run exited 0 and gave 600. A realistic single-run set (five whole-package shards plus the CLI sliced across shards 5 and 6) summed the CLI to 1250s within the run; dropping shard 6 left the CLI out of packages entirely with skippedIncompleteSlices naming it, and the coverage check then rejected that run by name — the two guards compose.\n\n(4) THE OTHER CONSUMER: partition-test-shards.mjs --self-test exit 0, verdict \"partition-test-shards: self-test OK (71 measured packages -> 72 shard items, 6 shards, max/mean 1.00x <= 1.3x, floor 404s, bins 672/672/672/671/672/671s)\"; and --check-drift on a synthetic shard-5 summary still reads the slice correctly (@objectstack/cli: predicted 229.1s = the dataset weight halved by the slice count, measured 600.0s, 2.62x). The rider did not disturb the drift path.\n\n(5) NEW SELECTOR: node scripts/ci/select-shard-timings-run.mjs --self-test exit 0, 30 cases, floor 30. Its --check-coverage mode exercised against the REAL committed dataset: a synthetic 31-of-71-package refresh rejected naming all 40 lost packages; the identical dataset passed with \"coverage OK -- 71 package(s) measured, 71 required\".\n\n(6) TWO LIVE RUNS OF THE LANE ITSELF, on a real runner against live artifacts, through its own pull_request trigger — this is the reverse verification, and it is what found the blocking fact. Run 34083991141 (before the accumulation fix): steps 1-10 all succeeded, including selection and the artifact download that no agent container can perform, and step 11 refused because every candidate run was a partial cache replay — best 52 of 71 packages, others 2, 3, 13, 18, 22, 49. Run 34085. .. (job 101625792555, after the fix): accumulation works and is monotone — 1 run 2/71, 2 runs 22/71, 4 runs 28/71, 5 runs 35/71, 6 runs 56/71, 7 runs 57/71 — and then refuses, because 14 packages are cache hits in EVERY retained run (driver-memory, driver-sqlite-wasm, formula, metadata-core, metadata-fs, observability, plugin-hono-server, sdui-parser, service-analytics, service-cache, service-cluster, service-cluster-redis, service-package, trigger-api). I predicted that shortfall by intersecting the seven per-run missing lists from the FIRST run log (14 packages) before the second run confirmed it live, so the number is measured twice by two independent routes. Both runs are red on the PR, deliberately: the lane refuses rather than writing a dataset it cannot stand behind.\n\n(7) GATE UNION on 4fe161b41a: all 49 families that node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derives for this four-path change set were run — 49 green, 0 red — and reconciled: \"OK dispatch-gates --ran: 49 derived famil(ies) accounted for — 49 run, 0 NOT-MEASURED.\" The derivation reported no stale tree. pnpm check:pm-dispatch-gates itself: \"dispatch-gates self-test: 1534 cases pass.\"\n\n(8) WHOLE-REPO LINT on 4fe161b41a, not a narrowing: pnpm lint = eslint . --no-inline-config through bash scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-16464, LINT-EXIT=0. The lock printed \"held the lock 99s (1m39s) / waited 164s (2m44s)\" and its own caveat that those are shared-box seconds, not idle-box.\n\n(9) GOVERNED-MERGE PREDICATE on the final file list: check-governed-merges --test exit 0, \"governed-surface predicate: 0 of 4 path(s) hit the register (5 surfaces, repo-agnostic). NOT governed\". Also check-partof-closing-keyword driven with the PR number and the branch commit list: exit 0, \"its 9 commit message(s) carry no card-relation trailer\" — the Part-of/closing-keyword split on this PR is machine-checked, not asserted.\n\n(10) GATE-FIX ABLATION, both legs proved on disk. My FIRST mutation attempt was a NO-OP — a perl -0pi anchor matched zero times and git diff --stat was empty — so those readings were discarded and it was redone through a driver that REFUSES unless the anchor matches exactly once. Mutated (boundary-present 1 -> 0, git diff --numstat \"1 1\"): production run GREEN at exit 0 while --self-test went RED at exit 1 naming the new pin verbatim plus the live-corpus battery — exactly the double that gate's own header documents for this class. Restored with git checkout HEAD -- \"$REPO_ROOT/PATH\" (absolute, HEAD-pinned): git hash-object db95b1025a4ba4d6a310268161d903b6d44af716 equal to the HEAD blob, git diff HEAD empty, self-test back to exit 0.\n\n(11) ACCUMULATION LOGIC simulated offline before it was pushed, using the same array-rebuild shape the workflow uses: three synthetic partial-replay runs whose union covers the dataset went 1/5 -> 3/5 -> 5/5 packages, the CLI summed to 900 WITHIN its own run (not across runs), provenance.runs recorded all three, and skippedIncompleteSlices stayed empty. The live run then reproduced the same monotone behaviour on real data.\n\n(12) SERIALIZATION: merge-tree against #16471's head 05cb57b43c twice, both exit 0, zero conflict markers, same tree 65319f6c34; that PR has since landed and merged into this branch cleanly. All 33 open PRs were read for their file lists; none touches this card's four paths.\n\nNOT MEASURED, reported as such rather than as passes: pnpm check:single-claim-paths exits 2 = PREREQUISITE NOT MET off a PR context; the eight families dispatch-gates itself marks NOT MEASURED because their argv takes a value only a CI run supplies; and workflow_dispatch on the branch, which this seat cannot perform (403 Resource not accessible by integration — no Actions write scope) and which the pull_request trigger replaced.", "mcp_calls": "2 — both mcp__github__get_job_logs, to read the two live runs of the lane, because GET /actions/jobs/{id}/logs redirects to the same blob host this container is denied (curl 56, CONNECT 403) and the MCP path returns the content directly. Everything else went over repo-scoped REST, which probed 200: both cards and their comments, #16173/#16222 bodies and 31 comments, the open-PR list, per-PR file lists for all 33 open PRs, check-runs, the workflow_dispatch attempt, the file-surface supplement plus its PATCH and read-backs, the report and pointer comments, PR creation, the PR body PATCH and its read-back, and the label POST and read-back.", "open_questions": [ { "question": "BLOCKING, and the reason this report is needs_decision. The ruled acceptance rule — regenerate the dataset from a green run's run-summary artifacts — cannot be satisfied as written, because no set of retained runs measures the whole workspace. Measured twice by independent routes: the best single run covers 52 of 71 packages, the accumulation of all seven retained runs converges at 57 of 71, and the last 14 are turbo cache HITs in every one of them (their inputs have not changed, so turbo replays them and the generator refuses replays). What should the lane do about a package it did not re-measure?", "options": [ "A. MERGE, not replace: a package this refresh did not measure keeps its last measured weight, and the dataset gains a `carriedOver` list naming those packages the way `skippedAsCached` already names its case. The argument is that a cache HIT is not missing data — it is positive evidence that the package has not changed since it was last measured, so the committed weight is still CORRECT rather than stale, and turbo is the witness. Cost: `provenance.measuredAt` stops describing every number in the file, so it has to become per-package or be paired with the carried-over list. Implemented in the generator (a `--merge-into` mode), never in the workflow, so \"every byte came out of the generator\" stays true.", "B. Force a cold measurement: give the lane its own full-workspace `turbo run test` with the cache disabled, and generate from that. One run, everything measured, `measuredAt` keeps its meaning exactly. Cost: it is precisely the \"special measurement run\" the generator's header says the artifact path exists to avoid — roughly an unsharded full suite of runner time every week, and it measures a machine nobody's PRs run on.", "C. Lower the coverage bar to a fraction (say 80%) and let the remainder fall back to the test-file-count estimate. Cheapest to write and the only option that invents a number nobody can defend; it also silently converts measured weights into estimates, which is the exact direction this whole file refuses in.", "D. Ship the lane as it stands — refusing and naming the shortfall every Monday — until A or B is chosen. Costs nothing and hides nothing, but a lane that reds every week is one people learn to ignore, which is the failure mode this card exists to end." ], "recommendation": "A, with D as the honest interim the PR already implements. Along the four axes. (1) REAL BUSINESS NEED, measured not speculative: the dataset is 14 days stale, shards 5 and 6 ran at 1.5x-2.4x their predictions on all three green runs flight C1 measured, the temporary 45-minute wall (#16445) is being held open until this rebalance lands, and B is the only option that costs real money every week for a number that changes slowly. A refreshes the 57 packages that DID change and leaves the 14 that did not exactly where they are, which is the whole of what anybody wanted. (2) LONG-TERM SOUNDNESS, no workarounds: A is the only option whose semantics are TRUE — every number in the file remains a real measurement of the code as it stands, and the cache is the proof for the ones not re-measured; C is a workaround that launders estimates into a file whose entire purpose is to not contain estimates; B is sound but buys that soundness with a weekly full suite and a measurement environment that is not CI. (3) HARDER FOR AN AI TO GET WRONG: A makes the file monotone — a refresh can only ever improve it, never degrade a package to a guess — and the `carriedOver` list makes what was not re-measured visible in the artefact rather than inferable from the split going strange later; C is the opposite and is the one option I would refuse outright even if instructed to pick cheaply. (4) STARTUP FOCUS, no capability sprawl: A adds one flag to a script that already exists and no new infrastructure; B adds a recurring compute cost and a second measurement environment to keep honest. The one thing I would NOT do under any option is raise MAX_SHARD_OVER_MEAN or MAX_MEASURED_OVER_PREDICTED to absorb the gap; the partitioner's header calls that the one move that cannot be right and its pin 3c is built to red on it." }, { "question": "Ruling 2 asked for 'the pin update by the partitioner's own refresh procedure', reusing flight C1's mechanics. C1 landed no PR and left no script, and the partitioner has no mechanical pin-refresh procedure to reuse: its pin 3c substitutes CLI_MEASURED = 1231.52 and its counterfactual leg says in terms that re-deriving or retiring the slice count is a decision. So the workflow updates NO pin. Is that the intended reading?", "options": [ "A. As built: run partition-test-shards.mjs --self-test on the refreshed dataset, quote the verdict verbatim into the PR body (with a named block when it reds, restating that the remedy is the slice count and never the bound), and open the PR either way. No pin is ever written by machine.", "B. Teach the workflow to re-derive FILE_SHARDED_PACKAGES arithmetically and commit the new slice count alongside the dataset. Mechanically possible — the derivation is written out in the partitioner's header — but it makes a machine the author of a balancing decision, and pin 3c's counterfactual leg exists precisely to force a human to look.", "C. Withhold the PR when the pins red, and fail the workflow instead." ], "recommendation": "A. REAL DEMAND: the pins are expected to red on the first honest refresh — flight C1 measured shards 5 and 6 at 1.54x-2.38x their predictions on all three green runs — so this is the common case, not a corner. LONG-TERM SOUNDNESS: A is the no-workaround shape; B is a machine quietly re-deriving the number that decides how CI is scheduled. HARDER FOR AN AI TO GET WRONG: A puts the arithmetic in front of a person with the measurement attached; B is exactly the automation that would let a bad refresh land quietly, and C is the silent-rot failure this card exists to remove — withholding the PR would discard the measurement and leave the dataset stale with nobody told, so C is refused outright. STARTUP FOCUS: A adds no surface; B adds a code-writing path to a scheduled workflow for one number a person sets in a minute." } ], "out_of_scope_findings": [ "FIXED IN PLACE under the bounded exemption, declared as a file-surface supplement on #16464 BEFORE the edit (comment 5564911725): scripts/check-self-test-wired.mjs keyed scripts/test-shard-timings.json as scripts/test-shard-timings.js — js is a prefix of json and its invocation extractor had a left boundary but none on the extension — minting the exact phantom key its left boundary was added to abolish, silent in both directions. Not separable from this card: ruling 2 requires the workflow to name that path, so no version of this deliverable avoids it. Four conditions checked rather than asserted; red-then-green ablation in tests item 10; four cases pin it, floor 4 -> 8.", "FIXED IN PLACE, same exemption and same unavoidability: this lane was the first gate family in the tree reachable ONLY on a schedule, which reds dispatch-gates because such a family appears on no card gate list and is graded by nobody until the next sweep. Closed with the pull_request posture half-state-patrol.yml and required-set-patrol.yml already keep, write-skipped on a PR run. It also turned out to be the mechanism that delivered this card's reverse verification and found the blocking finding above, so it paid for itself twice.", "NOT FILED, no defect remains: measure-test-shard-timings.mjs --self-test was run by NO workflow before this PR (grep -rn measure-test-shard-timings .github/ = 0 hits), so its 34-case battery evaluated never in CI. It was not a check-self-test-wired breach because that gate scopes its population to scripts a workflow names, and none did. This PR closes it as a side effect — the new workflow names and self-tests it, and check-self-test-wired now reports 184 wired scripts, green.", "FOR PM, NOT FILED AS A CARD because it is a fact about the tree rather than a defect, and it belongs with open_question 1: the documented refresh procedure in scripts/measure-test-shard-timings.mjs's own header (\"Download all six from any green queue build and run the generator\") is optimistic in a way nobody had measured. It yields 2-52 of 71 packages depending on how warm that run's cache was. Whatever is decided for the lane, that header sentence should be corrected in the same change, or the next person to refresh by hand will believe they measured the workspace when they measured a seventh of it.", "OBSERVATION for PM, no action taken: the refresh PR the workflow opens carries only Refs, never a closing keyword, because a weekly lane cannot know which cards a given run ought to retire. Cards #16173 and #16222 therefore stay open when the first refresh PR merges, contrary to the claim comment's expectation. Whoever lands that first refresh should retire them by hand or add the keyword to that one PR; baking it into the workflow would have every subsequent Monday try to re-close cards that are already closed." ] }Generated by Claude Code
claude commentedon Sep 7, 2026
Ruling at the 08:07Z delivery (report 5567426497, superseding 5565145010; PR #16498 at
4fe161b41a): Q1 → A, Q2 → A, and the earlier reverse-verification question → A once the lane's ownpull_requestrun is green. Under the maintainer's direction 「同意你的建议,你负责执行派发所有可行的优化」 (2026-09-07); the card's acceptance line "regenerate from a green run's artifacts" is amended as below, because the live runs measured that no retained run set covers the workspace (best single run 52 of 71, the accumulation of all seven converges at 57 of 71, the last 14 are turbo cache HITs in every run).Q1 → A, merge not replace, with these conditions:
carriedOverlist besideskippedAsCached;provenance.measuredAtbecomes per-package (or the file carries the carried-over list next to a single date, whichever the generator's readers already parse — pick the one that needs no change inpartition-test-shards.mjsand say which). Implemented in the generator as a--merge-intomode; the workflow never writes a byte the generator did not emit.testtask was a HIT in the accumulated runs); a package absent from every run for any other reason is a coverage failure, not a carry.scripts/measure-test-shard-timings.mjs("Download all six from any green queue build and run the generator") is corrected in the same change to state the measured fact (a run covers 2–52 of 71 depending on cache warmth; the lane accumulates runs and carries cache-hit packages).Q2 → A, as built: the partitioner's self-test runs on the refreshed dataset and its verdict is quoted verbatim into the PR body (a named block when it reds, restating that the remedy is the slice count and never the bound); the PR opens either way; no pin is ever written by machine.
Landing: the PR's own
Regenerate the shard-timings datasetcheck must be green on the final head (with A it merges 57 measured + 14 carried and renders the would-be PR body to the step summary on a PR run); then pure code ⇒ the seat's in-seat review, ready, SQUASH auto-merge. The first scheduled run's PR retires #16173 and #16222 by hand (the refresh PR carries onlyRefs); recorded for the next seat. B (a weekly cold full-workspace run) and C (an estimate fallback) are declined on the axes the report states; D is the interim the PR already implements and is not the landing shape.Generated by Claude Code
test:repotask into its sample, or the next refresh under-weighs the six split packages #16550claude commentedon Sep 7, 2026
os-dev-report
Supersedes 5567426497 (needs_decision) and 5565145010 before it. The seat ruling at 08:11Z is implemented in full — Q1 to A with all five conditions, Q2 to A as built — and the landing condition it set is met: the lane's own
Regenerate the shard-timings datasetcheck is GREEN on the final head, with all 40 checks on that head complete and none non-green. The PR is still a draft and is not enqueued.{ "issue": 16464, "status": "done", "branch": "claude/issue-16464-shard-timings-refresh-workflow", "pr": "https://github.com/objectstack-ai/objectstack/pull/16498", "premise_still_valid": true, "summary": "Supersedes report 5567426497 (needs_decision) and 5565145010 before it. The seat ruled Q1 -> A and Q2 -> A at 08:11Z; both are implemented, and the landing condition the ruling set is met: the lane's own pull_request check `Regenerate the shard-timings dataset` is GREEN on the final head 94eeaa19b1, with all 40 checks on that head complete, 32 success, 8 skipped, zero non-green. The PR now carries the closing keyword for BOTH cards. Q1 -> A, merge not replace, with all five conditions met. `--merge-into` is a generator mode, so the workflow still writes no byte the generator did not emit. A package the pass did not measure keeps its last measured weight ONLY when a turbo cache HIT witnesses that its inputs are unchanged -- the witness is the whole argument, because a HIT is not missing data but positive evidence the package has not changed since the run whose output was replayed, so the old number still describes it and the file keeps its invariant that every weight is a real measurement. Carried packages are named in a top-level `carriedOver` beside `skippedAsCached`. I picked the single `provenance.measuredAt` paired with that list over per-package dates, and the reader forces it rather than taste: partition-test-shards.mjs reads `packages` as name -> NUMBER (`timings.packages[name] / sliceCount`) and never opens `provenance` at all, so per-package dates would mean changing that shape and every consumer of it, while the list needs no partitioner change. Coverage is judged on measured union carried, and the two ways a package can be missing are reported separately because only one is a regression: one that HAD a weight and has neither refuses by name; a workspace package that never had one is NAMED as partitioner-estimated but is not a refusal, since this refresh did not change its standing -- nothing is silently estimated either way. A carry cannot manufacture a refresh: an all-cached pass still refuses. The generator header sentence \"Download all six from any green queue build\" is corrected in the same change to the measured fact. Q2 -> A as built: the partitioner self-test runs on the refreshed dataset and its verdict is quoted verbatim into the PR body, and the live green run proves it end to end -- `PARTITIONER_EXIT: 1`, so the balancing pins DID red on the honest refresh and the lane reported rather than acted, exactly as designed. No pin is written by machine. The reverse verification resolved to A as ruled: the live lane covered the workspace from ONE run (34087495912 at c383352cb7) plus the carry, composed a 70-line PR body and rendered it to the step summary with no branch pushed, no PR opened and no label written. The rider #16473 is unchanged and remains fully delivered; the live runs strengthened it, since the lane now feeds the generator run-fenced input as a matter of course. Recorded in the PR body as the ruling asked: the refresh PRs this lane opens carry only `Refs`, so cards #16173 and #16222 stay open when the first refresh merges and are retired by hand.", "tests": "FINAL HEAD 94eeaa19b1. Every exit code captured by redirecting first and reading the status before any pipe.\n\n(1) THE LANDING CONDITION, met: the PR's own `Regenerate the shard-timings dataset` check is completed / SUCCESS on this head (job 101671848579, run 34099951008). All 40 checks on the head are complete — 32 success, 8 skipped, 0 pending, 0 non-green — polled in the foreground to completion. NOTE ON THAT POLL: my first attempt passed the whole API response as an argv and every iteration died with \"Argument list too long\" plus a JSON parse error, so all 40 of its readings were void; it was redone through a file and only that run is reported.\n\n(2) WHAT THE GREEN RUN ACTUALLY DID, from its log: RUN_COUNT 1, RUNS 34087495912, HEAD_SHA c383352cb7 — the merge rule let ONE run plus the cache-hit carry cover the workspace, where before the merge seven accumulated runs reached only 57 of 71 and refused. PARTITIONER_EXIT 1, so the balancing pins red on the honest refresh and the lane quoted the verdict into the body instead of acting on it (Q2 -> A, live). It composed a 70-line PR body and rendered it to the step summary; no branch pushed, no PR opened, no label written, which is the pull_request posture working.\n\n(3) THE MERGE, EXERCISED AGAINST THE REAL COMMITTED DATASET before it was pushed. Two synthetic runs measuring 55 of the 71 real packages, the other 16 fed as cache HITs: `71 package(s) covered (55 measured in these runs, 16 carried on a cache-hit witness), 71 required` — coverage exit 0. THE WITNESS BOUNDARY, the case that separates a merge from \"keep whatever was there\": withholding two packages ENTIRELY (no MISS and no HIT, as a rename, removal or failed suite would) left them uncarried and refused by name — `2 package(s) HAD a measured weight and this refresh neither re-measured them nor found a turbo cache HIT to witness that they are unchanged: @objectstack/cloud-connection, @objectstack/downstream-contract` — exit 1. Both directions measured, not argued.\n\n(4) GENERATOR SELF-TEST: `node scripts/measure-test-shard-timings.mjs --self-test` exit 0, verdict \"measure-test-shard-timings: self-test OK\". Nine new merge cases covering every case the ruling names — a carried package (weight unchanged AND named), a measured package (new weight, NOT reported as carried), HIT-witnessed versus absent (only the first carries, and the absent one is in neither `packages` nor `carriedOver`), an empty `carriedOver` on a plain replace so \"absent key\" and \"nothing carried\" cannot read alike, an all-cached pass still REFUSING so carrying can never manufacture a refresh, and monotone accumulation (one run carries 1, adding a second carries 0 and the package takes its real 480). Battery floor 41 -> 50, the count MEASURED by setting the floor absurdly high and reading \"registered 50 case(s)\" back.\n\n(5) SELECTOR SELF-TEST: `node scripts/ci/select-shard-timings-run.mjs --self-test` exit 0. Five new cases for the union semantics: a carried package counts as covered with the carried/fresh split reported correctly, a package that lost its weight still refuses, a never-measured workspace package is named but is NOT a regression, never-measured is not conflated with carried, and a dataset with no `carriedOver` key at all still reads. Floor 30 -> 35, measured the same way.\n\n(6) THE OTHER CONSUMER, unchanged: `partition-test-shards.mjs --self-test` exit 0, verdict \"partition-test-shards: self-test OK (71 measured packages -> 72 shard items, 6 shards, max/mean 1.00x <= 1.3x, floor 404s, bins 672/672/672/671/672/671s)\". This is also the evidence for the `measuredAt` shape choice: that script reads `packages` as name -> number and never opens `provenance`, so the carriedOver-plus-single-date shape needs no change in it, and none was made.\n\n(7) GATE UNION on 94eeaa19b1: all 49 families dispatch-gates derives for this four-path change set were run — 49 green, 0 red — and reconciled: \"dispatch-gates --ran: 49 derived famil(ies) accounted for — 49 run, 0 NOT-MEASURED.\" No stale tree. `pnpm check:pm-dispatch-gates` itself: \"dispatch-gates self-test: 1534 cases pass.\"\n\n(8) WHOLE-REPO LINT on 94eeaa19b1, not a narrowing: `pnpm lint` = `eslint . --no-inline-config` through `bash scripts/pm/os-verify-lock.sh`, slot issue-16464, LINT-EXIT=0; the lock printed \"held the lock 112s (1m52s), waited 0s\" with its own shared-box caveat.\n\n(9) GOVERNED-MERGE PREDICATE on the final four-path list: exit 0, \"0 of 4 path(s) hit the register (5 surfaces, repo-agnostic). NOT governed\". The PR body's card relations are machine-checked too: `Part-of PR must not also close its card` and both single-writer/issue-claim checks are green on the head.\n\n(10) EARLIER LEGS THAT STILL STAND, unchanged by this round and re-run green in the union above: the #16473 red-then-green on the exported buildDataset (1000 -> 600 sliced median, 300 control, 800 -> 600 splice now named \"cli in run C (missing 2/2)\"); the check-self-test-wired right-boundary ablation (mutated: production GREEN exit 0 while --self-test RED exit 1 naming the new pin; restored: git hash-object db95b1025a4ba4d6a310268161d903b6d44af716 equal to the HEAD blob, git diff HEAD empty); and the merge-tree proof against #16471, which has since landed and merged into this branch cleanly.\n\nNOT MEASURED, reported as such: `pnpm check:single-claim-paths` exits 2 = PREREQUISITE NOT MET off a PR context (it is green on the PR itself); the eight families dispatch-gates marks NOT MEASURED because their argv takes a value only a CI run supplies; and `workflow_dispatch` on the branch, which this seat cannot perform (403 Resource not accessible by integration, no Actions write scope) and which the pull_request trigger replaced by ruling.", "mcp_calls": "4 — all mcp__github__get_job_logs, to read the three live runs of the lane (two red, one green) because GET /actions/jobs/{id}/logs redirects to the blob host this container is denied (curl 56, CONNECT 403) while the MCP path returns content directly. Everything else went over repo-scoped REST, which probed 200: both cards and their comments including the seat ruling, #16173/#16222 bodies and 31 comments, the open-PR list, per-PR file lists for all 33 open PRs, check-runs and the foreground check poll, the workflow_dispatch attempt, the file-surface supplement plus its PATCH and read-backs, three report comments and two pointer comments, PR creation, four PR body PATCHes with read-backs, and the label POST and read-back.", "open_questions": [], "out_of_scope_findings": [ "FIXED IN PLACE under the bounded exemption, declared as a file-surface supplement on the card BEFORE the edit (comment 5564911725): scripts/check-self-test-wired.mjs keyed scripts/test-shard-timings.json as scripts/test-shard-timings.js — js is a prefix of json and its invocation extractor had a left boundary but none on the extension — minting the exact phantom key its left boundary was added to abolish, silent in both directions. Not separable: ruling 2 requires the workflow to name that path. Four conditions checked; red-then-green ablation recorded in tests item 10; four cases pin it, floor 4 -> 8.", "FIXED IN PLACE, same exemption and same unavoidability: this lane was the first gate family in the tree reachable ONLY on a schedule, which reds dispatch-gates because such a family appears on no card gate list. Closed with the pull_request posture half-state-patrol.yml and required-set-patrol.yml already keep, write-skipped on a PR run. It paid for itself three times over: it is what delivered the reverse verification, what measured the coverage finding that forced the merge rule, and what now proves the merge rule green.", "NOT FILED, no defect remains: measure-test-shard-timings.mjs --self-test was run by NO workflow before this PR (grep -rn measure-test-shard-timings .github/ = 0 hits), so its battery evaluated never in CI. Not a check-self-test-wired breach, because that gate scopes its population to scripts a workflow names and none did. This PR closes it as a side effect — the new workflow names and self-tests it, and check-self-test-wired now reports 184 wired scripts, green.", "CORRECTED IN THIS PR rather than filed, per ruling condition 4: the generator header's documented procedure (\"Download all six from any green queue build and re-run the generator\") was optimistic in a way nobody had measured — a single run yields 2-52 of ~71 packages depending only on cache warmth. It now states the measured fact and the accumulate-plus-carry procedure, so the next person refreshing by hand cannot believe they measured the workspace when they measured a seventh of it.", "RECORDED IN THE PR BODY for whoever lands this, as the ruling directed: the refresh PRs this lane opens carry only `Refs` and never a closing keyword, because a weekly lane cannot know which cards a given run ought to retire and a baked-in keyword would have every subsequent Monday try to re-retire cards already retired. Cards #16173 and #16222 therefore stay open when the first refresh PR merges, and whoever lands that first refresh retires them by hand or adds the keyword to that one PR." ] }Generated by Claude Code
claude commentedon Sep 7, 2026
ACCEPT — flight C2a, PR #16498 at head
94eeaa19b1(#16464 + rider #16473), on the seat's ruling 5567471502 (Q1 → A, Q2 → A) and the dev's report 5567897750.Read in a detached compare worktree against the merge base
70e77ec3b5; every reading below is the seat's own, taken on the final head..github/workflows/shard-timings-refresh.yml(new),scripts/ci/select-shard-timings-run.mjs(new, the run selector with--candidates/--check-coverage),scripts/measure-test-shard-timings.mjs(the rider's run-fenced slice ledger + the ruled--merge-intomode),scripts/check-self-test-wired.mjs(the.js/.jsonboundary fix declared on the card before the edit, 5564911725).--runas the remedy. Red-then-green on the exportedbuildDatasetas reported (1000 → 600 sliced median, 300 control, the cross-run splice 800 → named skip).carriedOverbesideskippedAsCached; coverage is judged on measured ∪ carried; a package that had a weight and has neither is refused by name, a never-measured workspace package is named as partitioner-estimated and is not a regression; an all-cached pass still refuses; the generator's header now states the measured fact (a single run yields 2–52 of 71). Singleprovenance.measuredAtpaired with the list, becausepartition-test-shards.mjsreadspackagesas name → number and never opensprovenance— the reader decides the shape.PARTITIONER_EXIT: 1— the pins redded on the honest refresh and the lane reported instead of acting; no pin is written by machine.scheduleMonday 05:30Z +workflow_dispatch+ apull_requesttrigger paths-filtered to its own files; top-levelpermissions: contents: read, the job holdscontents: writeandpull-requests: write; the write step (git push+gh pr create) is gated onchanged == 'true' && github.event_name != 'pull_request', the dry-run half renders the would-be PR body to the step summary.pull_requestrun on this head is green — one run (34087495912 atc383352cb7) plus the carry covered 71 of 71, a 70-line body rendered, no branch pushed, no PR opened, no label written.measure-test-shard-timings.mjs --self-testOK (floor 50),select-shard-timings-run.mjs --self-testOK (floor 35),partition-test-shards.mjs --self-testOK (71 packages → 72 items, 6 shards, max/mean 1.00×),check-self-test-wired.mjs185 wired of 197;check-governed-merges --teston the 4 files → 0 of 4 hit the register, NOT governed, exit 0.94eeaa19b1: 40 check runs, 32 success + 8 skipped, none red, includingRegenerate the shard-timings dataset.Fixes #16464,Fixes #16473,Refs #16173,Refs #16222.验收备注 (noted, not filed)
Refs: CI: the shard-timings file is stale for the CLI package — 672s predicted vs 28m46s measured against a 30-minute timeout, so Test Core shard 1/6 is one slow run from being killed on any PR touching the CLI #16173 and [finding] the generated shard-timings dataset has no scheduled refresh, and its documented refresh path is unreachable from an agent container #16222 are retired by hand by whoever lands the first refresh PR (recorded in the PR body and on the seat post for the next seat).check-self-test-wiredextension boundary, and the scheduled-only gate family (closed with thepull_requestposture the other patrol workflows keep).measure-test-shard-timings.mjs --self-testwas run by no workflow before this PR; the new workflow names it,check-self-test-wirednow reports it wired. No card.workflow_dispatch(REST 403) and job-log downloads redirect to the blob host: on the ledger already.Landing: pure code ⇒ the seat flips the PR ready and arms SQUASH auto-merge now; on merge strip #16464 and #16473; the first scheduled run is Monday 05:30Z (or a maintainer's
workflow_dispatchsooner) and its PR is #16173's deliverable.Implemented-by: flight C2a (os-dev, session
session_019RfFHiRCSs3JXLK4cwcfox), PR #16498.Reviewed-by: the skills seat (os-steve, session
session_019RfFHiRCSs3JXLK4cwcfox).Generated by Claude Code
chore(ci): refresh the Test Core shard-timings dataset (objectstack-a…
chore(ci): refresh the Test Core shard-timings dataset (objectstack-a…
chore(ci): refresh the Test Core shard-timings dataset (objectstack-a…