Repository navigation
docs(perf): freeze the matched application benchmark protocol and record known startup cost - #334
Merged
Merged
Conversation
…ord known startup cost HACK-1169: freeze revision 1 of the v5 matched application benchmark before any trials. docs/reference/application-benchmark-protocol.md and its machine-readable copy fix the exact artifacts (v5.0.0-next.5 at 28de2f8 and stable 4.1.1, 4.2.0, 4.2.1 with release asset SHA-256 digests), the M3 host with Docker CLI 29.4.0, Compose v5.1.2 and OrbStack 2.2.3, Event Agent plus a pinned small fixture, capacities, cache classes, a 10-round Williams rotation, p95 sample floors, observer overhead accounting and the program budgets. scripts/check-application-benchmark-protocol.py validates the frozen file and judges retained summaries offline with closed, value-free codes; anything short of complete, sufficient samples is inconclusive. Offline unittest controls run in CI. docs/performance.md records the user-accepted known v5 limit (HACK-1123): the storage-witness cost of a warm native up after #313, with rationale and revisit trigger. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nings
HACK-1169 rework of the application benchmark protocol checker.
Evaluation: the candidate can never be not_applicable (exit 2,
summary_candidate_not_applicable). A not_applicable record is exactly
{not_applicable: true, reason} and cannot carry samples. A comparative budget
whose baselines are all not_applicable gets an inconclusive_no_baseline
verdict, so it is never accepted. Only warm, single-instance summaries are
judged. Samples must be non-negative, expected a positive count, and flagged
and failed lifecycles are accounted: any flag, unaccounted admission or failed
baseline lifecycle is inconclusive_admission, and a failed candidate
lifecycle fails. Every malformed summary or protocol value maps to a closed
code instead of a traceback.
Protocol validation now pins each budget's statistic, workload scope (only
the watcher budget is limited to the small fixture, as in Gate C; idle CPU
and cached status now apply to both workloads), the observer factor of 2,
p95 method and confidence, rounds_per_workload, the Williams design, workload
shapes, the required baselines in every workload, the Compose lane against
the frozen host, the Gate C phases (adding restore) and the 1/8/32
branch-instance and 1/32 MCP-client scale points.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The protocol validator checked only the format of artifact commits and digests, so a different well-formed candidate commit, a removed asset, a recast lane kind or backend, or a changed candidate capacity still validated. Pin the revision-1 lane artifacts, lane kinds and backends, candidate capacity, host values, workload pins and correctness checks exactly, with closed refusal codes. Bound every protocol and summary number to the interoperable JSON range so an oversized integer is refused as malformed (exit 2) instead of raising OverflowError, and keep an overflowing ratio a failure without printing a non-standard JSON constant. Document why revision 1 freezes the native development profile although the candidate labels it experimental-development-not-benchmark-qualified: graph allocation refuses every other profile and native up always requests it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The validator still accepted a different balanced Williams order, so reordering the lane list and regenerating the schedule validated while the published order table and --schedule disagreed. It also judged sample counts against the program floors (5 runs, 59 tail samples) rather than the frozen revision-1 values (10 and 60), so a protocol edit could lower them without refusal. Pin the revision-1 order exactly (schedule_order_mismatch) and treat the frozen sample counts as floors. Check that the published order table matches the frozen order round for round, and that the page states both frozen sample counts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
roodboi
added a commit
that referenced
this pull request
Oct 23, 2026
Follow-up to #334 (merged as a388ce2) for HACK-1333 / HACK-1169. Two fixes to the application benchmark protocol checker. ## 1. Window means use a correctly rounded sum `window_mean` used the built-in `sum()`, which rounds after every addition. Python 3.12 also changed how `sum()` adds floats. On the macOS system Python 3.9, ten idle samples of `0.01` averaged to `0.009999999999999998`. That mean is exactly at the exclusive 1% idle ceiling, so it passed on 3.9 and failed on 3.12 and later. The checker now uses `math.fsum`, so every interpreter computes the same correctly rounded mean. This is a cherry-pick of 485d46fa; it applied cleanly on `next`. ## 2. The ±(2⁵³ − 1) bound now covers every number The protocol page says every number in a protocol or summary must be within ±(2⁵³ − 1). The checker enforced this only for fields it read with `_number`. Before this change, it accepted these out-of-range values: - `p95_min_samples = 2**53`: the protocol validated. - `expected = 10**400`: the summary was judged `inconclusive_coverage` instead of being refused. - `2**53`, `10**400`, `NaN` or `Infinity` in a field the checker does not read: accepted in both a protocol and a summary. The checker now scans every number in the whole document, iteratively so deep nesting cannot raise `RecursionError`. It adds two closed codes, `protocol_number_out_of_range` and `summary_number_out_of_range`, which are reported alongside any code for the field that holds the number. The page now names both codes and says the bound also covers fields the checker does not read. Three existing assertions that expected exactly one code now expect both codes. They still check that the input is refused. ## Validation - `test_absolute_ceilings_respect_inclusivity` and `test_window_mean_is_correctly_rounded_on_every_interpreter` pass under `/usr/bin/python3` (3.9.6) and the repo's `python3` (3.14.8). - With `fsum` reverted to `sum()`, both tests fail on 3.9.6 (`['pass'] != ['fail']` and `0.009999999999999998 != 0.01`). On 3.14.8 the window-mean test fails. - The full `test_application_benchmark_protocol.py` suite passes on both interpreters (40 tests), and `scripts/check-application-benchmark-protocol.py` reports the frozen protocol as valid. - Mutation checks for item 2 each make at least one test fail: protocol scan removed, summary scan removed, dict values skipped, a recursive walk instead of the iterative one, and the bound changed to `2**53`. - No leaks from gitleaks on `origin/next..HEAD`. `privacy:check` passes. `packages/cli` typecheck and check report 0 errors (no TypeScript changes). - Full `bun run test` on the M3 at the exact head: see the comment below. ## Remaining gaps - Hosted CI on this PR has not been checked yet. - No live benchmark ran. These are checker-only changes. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
HACK-1169 (under WU10 HACK-1123). This freezes revision 1 of the v5 matched application benchmark protocol before any trials, and records the known storage-witness startup cost that the user accepted. No benchmark was run. The PR claims no performance or resource result.
Head
89421c82reworks the first commit (15496fa0) after independent verification. That verification found thatnot_applicabledata could be accepted, that several weakenings of the frozen protocol still validated, that malformed input caused tracebacks, and that some Gate C coverage was missing. Details are in "Rework" below.Final behavior
docs/reference/application-benchmark-protocol.mdand its machine-readable copy,scripts/application-benchmark-protocol.json, freeze:v5.0.0-next.5at28de2f89and stablev4.1.1,v4.2.0andv4.2.1, each with its tag commit, darwin-arm64 asset name and release asset SHA-256 (read from the GitHub release API).oven/bun:1.4.2-slim. Both workloads are compared against all four required baselines.developmentis 4 vCPUs / 6 GiB, and the OrbStack VM is 16 CPUs / 70 GiB. Both are reported side by side and never used to scale results.warmis the only budget class. The phases are the Gate C list, includingrestore: an ordinary start after a retaining shutdown, with data readback. The scale points are 1, 8 and 32 branch instances and 1 and 32 MCP clients. Budgets are judged at the single-instance lifecycle. No separate numerical budget is set for the other scale points; their regressions are reported and block acceptance.scripts/check-application-benchmark-protocol.pyvalidates the frozen file, prints the schedule, and judges a retained summary with--evaluate. It exits 0 only when every verdict ispassornot_applicable, 1 on any fail or inconclusive verdict, and 2 with closed, value-free codes for an invalid protocol or summary. It never runs a runtime, calls Docker or reads host state.docs/performance.mdrecords the known v5 limit the user accepted on 2026-10-19 (HACK-1123). After perf(storage): reuse unchanged storage witness proofs across warm commands #313, a warm nativeuptakes about 17.7 s, of which about 8.7 s is witness time, against a 5 s target. These are hosted estimates. The section gives the rationale and the revisit trigger.Rework
Evaluation.
not_applicable. Such a summary is refused with exit 2 andsummary_candidate_not_applicable.not_applicablerecord must be exactly{not_applicable: true, reason}, so it cannot also carry samples. A record that carries samples is refused withsummary_entry_malformed.not_applicablegets aninconclusive_no_baselineverdict and is never accepted.expectedthat is not a positive integer, wrong-typed fields, unknown entry keys, and more accounted samples than planned.flaggedandfailedcounts are required. A flagged sample, missing accounting, or a failed baseline lifecycle givesinconclusive_admission. A failed candidate lifecycle isfail.workloadexits 2 withsummary_workload_unknown; it no longer exits 1 with a traceback.Validation. Validation now pins:
budget_statistic_mismatch) and workload scope (budget_scope_narrowed; a scope may be broadened but never narrowed);nearest-rankmethod;rounds_per_workloadequal to the round count, and thewilliamsdesign;workload_baseline_missing);compose_lane_host_mismatch);Duplicate budget entries are refused. A wrong-typed
known_limits, workload id or budget list maps to a closed code.Scope corrections.
idle_control_plane_core_fractionandcached_status_1000_objects_msnow apply to both workloads, because Gate C limits neither to one workload. This is stricter than the first commit. Gate C'srestorephase and its scale counts are now part of the frozen protocol.Validation
python3 -m unittest discover -s tests/python -p test_application_benchmark_protocol.py: 34 tests pass. Two of them sweep every protocol node (261) and every summary node (166), each with 13 wrong-typed values and deletion. Every result is a closed code; none raises.python3 -m unittest discover -s tests/python(all Python harness controls): 262 pass, 1 skipped.python3 scripts/check-application-benchmark-protocol.pyreportsvalid.bun test tests/integration-ownership.test.ts tests/cli-spawn-env-isolation.test.ts: 16 pass, 0 fail.bun run --cwd packages/cli typecheckexits 0.bun run --cwd packages/cli checkexits 0 with 0 errors; the 34 warnings were there before this change.bun run privacy:checkon the staged tree: ok.Negative controls
not_applicable), E2 (every baselinenot_applicable), E3 (cold-cache summary), E4 (list-valued workload) and E8 (not_applicableover a 5× regression) are refused with exit 2, or giveinconclusive_no_baselinewith exit 1.expectedvalues and negative candidate timings are refused.rounds_per_workload, workload shape, Compose version drift, a numericknown_limitsand a list-valued workload id.max > ceilingcheck (killed byinfo_latency_msmax 1000), and removing thep95_quantilecheck (killed by quantile 0.9, which a 60-sample floor would otherwise satisfy). Four mutants survived the first pass: unknown scope on an extra budget, a weaker duplicate placed before the frozen entry,expected0 with no samples, and accepting an empty verdict list. Each got a targeted case and is now killed.Remaining host-bound gaps
--evaluatesummary shape, includingcache_class,scale, andflagged/failedaccounting. The small fixture is specified but not yet generated by a script.Rework 2
Commit
ca810049fixes the independent verifier's three open findings from round 1.Oversized integers no longer crash the evaluator. Python parses a JSON integer such as 1 followed by 400 zeros exactly.
math.isfinitethen raisedOverflowError, and the evaluator exited 1, which is the code for "judged, not accepted". Every protocol and summary number must now lie within ±(2⁵³ − 1), the interoperable JSON (I-JSON) range. The range check never converts an int to a float. An oversized sample, baseline sample, observer cost or budget value is refused as malformed with exit 2. A ratio that overflows to infinity, because a baseline is tiny but positive, is afailand is not printed, so the report stays strict JSON. The verifier's repro now exits 2 withsummary_entry_malformed.Frozen identities are pinned. The validator now holds the exact revision-1 values and refuses any change or removal with a closed code:
artifact_identity_mismatch);lane_runtime_mismatch);candidate_capacity_mismatch);host_mismatch);workload_pin_mismatch), and any dropped frozen correctness check (workload_correctness_weakened). A revision may still add checks.Equality is exact JSON equality, so
4.0does not match4andtruedoes not match1. The doc test now reads the validator's pins instead of usingif field in item, so a removed field is still required on the page.Why the
developmentprofile. The protocol page now explains this choice in a new paragraph under "Capacities". At28de2f89, the candidate'sprovider/profile.rscallsdevelopmentan experimental allocation that cannot supply benchmark qualification, and lifecycle records carryexperimental-development-not-benchmark-qualified. That label belongs to the WU02 provider comparison, whose envelope is theresearchprofile. Revision 1 usesdevelopmenton purpose for two reasons:graph_profile(provider/graph/mod.rsrun and restart), soresearchcannot run either workload.hack upalways passes--profile development(src/backends/native-project-start.ts).A candidate record that carries the label is expected and is not a reason to refuse the cohort. Changing the label or the comment is separate runtime work. It cannot change the frozen artifact.
Validation (rework 2)
python3 -m unittest discover -s tests/python -p test_application_benchmark_protocol.py: 37 tests pass.FUZZ_VALUESnow includes ±10⁴⁰⁰, 2⁵³ and −inf in both node sweeps.python3 -m unittest discover -s tests/python: 265 tests pass, 1 skipped, on a serial rerun. An earlier run had 1 error while the fullbun run testran at the same time on a loaded host. I did not identify that error, and it did not recur.python3 scripts/check-application-benchmark-protocol.pyreportsvalid.isfinite_number;==instead of exact JSON equality;gitleaks8.30.1 overorigin/next..HEAD(3 commits): no leaks. The test builds its other commit and digest values at runtime.bun run --cwd packages/cli typecheckexits 0.bun run --cwd packages/cli checkreports 0 errors and the same 34 warnings as before.bun run test: it exited 1 locally on this M5 host at load average 14 to 17, with 7192 pass, 126 skip and 29 fail across 11 native and init files. On a serial rerun with--timeout 60000,native-compose-adoption-probe,project-env-metadata,agent-onboard-commandandinit-assistantpassed. Five native-compose command files and two authored-recovery files still failed. Their source-CLI fixture sets a 1-secondHACK_COMPOSE_STARTUP_TIMEOUT_MS. The same warmupfixture fails withprivate-uncertainat that timeout and exits 0 with a 30-second timeout on this host. So I classify those as host-load failures, not regressions. This PR changes no TypeScript, and hostedtestpassed on the previous head89421c82with the same TypeScript tree. Hosted CI onca810049is the gate for this result.Still open after rework 2
Profilecomment and theexperimental-development-not-benchmark-qualifiedlabel still describedevelopmentas non-qualifying for benchmarks. Whether to relabel them is a product decision for a later candidate.Rework 2: round-2 verification (
b5c2e9f8)Round-1 findings are confirmed fixed on
ca810049. I re-ran the verifier's repros on that head:summary_entry_malformedorsummary_observer_malformed, and no traceback.artifact_identity_mismatch. The test suite also kills that mutant.compose, a removedasset, and a removed capacity or backend.developmentrationale is on the page, and its source claims hold at this head (graph_profileinprovider/graph/mod.rs, and--profile developmentinnative-project-start.ts).New in
b5c2e9f8: the frozen order and sample counts are pinned. Two gaps remained afterca810049:--scheduleand the published order table then disagreed. The validator now pins the revision-1 order exactly (schedule_order_mismatch), and a test checks the page's order table round for round.Validation (round 2)
valid. A random fuzz of 28,000 rounds, each changing 1 to 3 nodes of both a protocol and a summary, raised nothing and produced only closed codes.bun run --cwd packages/cli typecheck: exit 0.bun run --cwd packages/cli check: exit 0, with the same 34 warnings.origin/next..HEAD(4 commits): no leaks.next(099877a0): clean.bun run testexited 1 on the M5 host, with load average 13 to 41 while other agents ran tests. I reran each of the 48 failing files in its own process with--timeout 60000. Forty-one passed outright, andmcpandtest-config-compileralso passed when run again. Five native startup files still fail:native-authored-project-start,native-compose-adoption-generation,native-compose-down-command,native-compose-file-commandandnative-authored-project-recovery. They fail with startup-settlement and fixture-acceptance refusals, which matches the load failures in round 1. This commit changes no TypeScript, and no TypeScript test reads the changed files. Hostedtestpassed onca810049and is the gate for this result.Still open (needs a decision or the M3 host)
These are unchanged from the earlier rework. Revision-1 cohorts, the M3 host re-read and storage-witness measurements need exclusive use of the M3 host. Budgets for the 8- and 32-instance and 32-MCP-client scale points, and the Event Agent scope for cached status and idle CPU, are product decisions. No Linear writes were made.
Hosted status on
b5c2e9f8(resumed check, 2026-10-20)b5c2e9f8(run 39444856646), includingtest,Runtime state models,secret-scanand the fourdocker-e2eshards. This replaces the localbun run testload failures above as the gate for this head.summary_entry_malformed; M25, a candidate or 4.1.1 lane recast ascompose, a removedasset, a changed digest, and a removed or changed candidate capacity or backend each exit 2 with a closed code.next(b431f0e7): clean. Thenextchanges toprovider/graph/mod.rsandnative-project-start.tsdo not affect thedevelopmentrationale, which cites the frozen28de2f89source.🤖 Generated with Claude Code