Skip to content

feat(measure): add the middle layer of the measurement core: recipe compile, framework tables, the jit cache and the result archive - #2158

Draft
zoroyihan7 wants to merge 11 commits into
feature/yihan/measure-leavesfrom
feature/yihan/measure-middle
Draft

zoroyihan7 wants to merge 11 commits into
feature/yihan/measure-leavesfrom
feature/yihan/measure-middle

Conversation

@zoroyihan7

@zoroyihan7 zoroyihan7 commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

Review only — merges after the multibranch acceptance run (see AMD-AGI board #121); do not merge before.

Stacked on #2157 (the leaf layer): the base branch of this PR is feature/yihan/measure-leaves (#2157's head, now 533f6beb9), so the diff below is this PR's own. The modules here import the leaf layer (#2157), hyperloom.contracts.measurement and hyperloom.contracts.recipe (#2153), hyperloom.common.outside_facts (#2155) and each other. When the stack below has merged, this PR retargets to main with git rebase --onto main feature/yihan/measure-leaves feature/yihan/measure-middle.

Summary

Adds the middle layer of the measurement core, hyperloom.measure: the modules that import only the leaf layer, the measurement contract, the common helpers and each other. They are what a local test is built from before the socket and the local backend put it together: compiling a recipe into an overlay (now including which of aiter's prebuilt modules a patch or a tuned table makes stale), the rebuilt framework and its tuned tables, the compiled-code cache and the native build store, the result archive, and the reading of a round's marks. All of it is taken from multibranch-dev as it is (refreshed on 2026-10-30 to 629dfc8b1), with the tests whose imports are all in this change or below it, except a comment. Commits:

  1. test(plugs): let the jit store import the result archive's fsync_dir: one line of the architecture guard (below).
  2. feat(measure): add the middle layer of the measurement core: the original 13 modules and their tests (cut from 555e863eb).
  3. fix(measure): store a cached blob readable by its owner only and 4. fix(measure): keep a cached blob world readable, and say why: the mode CodeQL asked for, then restored with the reason in a comment (below).
  4. Merge of feature/yihan/measure-leaves (the refreshed feat(measure): add the leaf modules of the measurement core: instruction sheet, result manifest, signing and what they stand on #2157) into this branch: a normal merge, no conflicts.
  5. chore(measure): bring the measurement core's middle layer up to multibranch-dev 629dfc8b1: every file moved to dev's version, and the 11 modules dev's compile and farm now import added (they belong to this layer).

The modules (lines), and everything each imports from this repository:

Module Lines What it is Imports
compile.py 889 turns a recipe and the change a test request carries into the environment overlay the test runs in; every way the overlay could silently differ from the recipe is a refusal; now also decides which prebuilt aiter modules the change makes stale contracts.measurement, contracts.recipe, common.outside_facts (#2155), common.aiter_layout, framework.rows, framework.tables, framework.table_modules, jit.modules, jit.native.needs
farm.py 528 rebuilds the framework the candidate changed next to the image's and puts it first on the import path, then checks that the server imports it; lays native builds into it spec, jit.key, jit.native.laying, jit.native.record, common.aiter_layout, common.outside_facts, common.benchmark_config and orchestrator.actions.executors.magpie_agentx (both on main), PyYAML
jit/store.py 518 the shared compiled-code cache: append-only, content-addressed, read with verification; a blob a reader finds damaged is set aside archive, docs, fsafe, jit.locations, jit_kinds
jit/session.py 461 one test's use of the cache: seeds a private copy before the server starts, stages what the compilers added afterwards fsafe, jit.builds, jit.locations, jit.notes, jit.store, jit_kinds
jit/modules.py 356 new: which of aiter's prebuilt modules a patch to its sources makes stale common.outside_facts, jit.key
round_marks.py 337 what a round left on disk about when its parts began, and the phases that follow boot_watch, phases, common.outside_facts
jit/key.py 279 the key a cache is stored under: exactly the inputs that decide what the compiler produces common.outside_facts (#2155), docs
framework/table_modules.py 240 new: which of aiter's prebuilt modules a test's tuned tables make stale framework.rows, framework.tables
framework/base.py 225 the framework's tuned tables as the session found them: one snapshot per session, one proof per test common.outside_facts (#2155), framework.tables
jit/native/ (8 files) 874 new: the native build store: key (99) names a build by its inputs, record (113) is its signed manifest, trust (96) signs and checks it, store (218) keeps entries and blobs, laying (148) lays an entry into a framework copy, needs (114) says which builds a recipe needs, config (76), __init__ (10) docs, fsafe, signing, archive, jit.key, jit.store, contracts.recipe, each other
archive.py 194 the write-once archive of verified result bundles; a bundle becomes visible in one step docs, fsafe, manifest
framework/rows.py 162 a few tuned rows laid over the image's default table for one test framework.tables
pins.py 147 the Magpie and InferenceX commits Hyperloom pins, read from the packaged install.sh docs
jit/locations.py 125 cache files that name the directory they were written in: stored without it, given the reader's own again docs, fsafe, jit_kinds
framework/merge.py 88 the directory aiter writes one test's merged tables to, and the hook that makes it write there aiter_merge_hook, framework.base
attachments.py 85 unpacks the sheet's attachments into the working directory spec
common/aiter_layout.py 30 new: where an installed aiter keeps its device sources, found without importing aiter (image_aiter_meta, moved out of jit/modules on dev in 3f7953028) common.outside_facts

Measured: each of the 24 production modules imported alone in a fresh isolated interpreter (python -I) loads, and none of them loads hyperloom.sandbox, hyperloom.measure.runner, socket, local, hosted, failures or hyperloom_multibranch.

Tests (374 collected: 372 pass, 2 skip): jit/tests/test_key (52), test_store (40), test_locations (31), test_modules (27, new), test_session (26); test_compile (32), test_compile_aiter_meta (9, new), test_compile_aiter_tables (3, new) and test_compile_installed_sglang (1, skipped: needs SGLang installed); test_round_marks (34), test_pins (21), test_archive (16), framework/tests/test_table_modules (16, new), test_aiter_merge_hook (13), framework/tests/test_base (11, one skipped: it asserts a permission refusal, which root does not get) and test_rows (9), test_farm (11), test_attachments (6), and plugs/tests/test_architecture (16). Helpers they share: framework_fixtures, aiter_excerpts, _recipes, tunableop_table, jit/tests/triton_cache. test_aiter_merge_hook tests a module of #2157; it waits here because it builds a framework copy with framework_fixtures and framework.merge.

What differs from multibranch-dev

  1. Everything is byte-identical to origin/multibranch-dev at 629dfc8b1 (checked file by file: 24 modules and 25 test and helper files), except the comment in item 3. (629dfc8b1 and e81281d60, the sha feat(measure): add the leaf modules of the measurement core: instruction sheet, result manifest, signing and what they stand on #2157 matches, differ in hyperloom.measure only in runner/native/builder.py, which is not here.)
  2. plugs/tests/test_architecture.py (the first commit): hyperloom.measure.archive leaves _HOSTED, the list of hosted implementations that nothing outside their owners may import. It held while only the hosted path read the archive; jit/store.py (and now jit/native/store.py) take fsync_dir from it. This is the same kind of edit as the _DEV_ONLY line in feat(hosted): add the MCP service core with token scopes and tool registration #2135, feat(sandbox): add the sandbox socket and the E2B provider #2154 and feat(measure): add the runner's cache, start-up script, hardware check, tools, service and AgentX client patch #2156: a guard that must move with the layer that makes it stale. The rest of the file is main's, not dev's. hyperloom.measure.hosted, .sandboxes, .consistency and .runner stay listed.
  3. jit/store.py: the only difference from multibranch-dev is a four-line comment above _BLOB_MODE = 0o444; the mode itself is as on multibranch-dev. CodeQL's py/overly-permissive-file flags it twice (the open of a stored blob, and the chmod of a snapshot); the alert is intended: a blob is content-addressed and read-only by design, and another account reads it. On the hosted path the host writes the store and a sandbox account reads it through a read-only mount, so owner-only would leave that account unable to read a blob and the cache silently unused. The two alerts are left open for the reviewers to decide whether to dismiss; I have not dismissed them.
  4. pyproject.toml is unchanged: no module here adds a dependency.

What is not in this PR, and why

  • jit/native/tests/test_key (it imports measure.runner.native.tests._native), test_compile_aiter_modules (imports measure.runner.native.aiter_modules), test_farm_aiter_meta and test_farm_aiter_meta_root (import measure.runner.import_outcome), framework/tests/test_copies_farm (imports framework.copies and socket): their imports reach the runner core or the upper layer, so they go with those.
  • The rest of jit/native on dev lives under measure/runner/native (builder, harvest, recipes, parity, ...): runner core, not here.
  • failures.py and its tests test_log_causes, test_returncodes_and_failures and the helper eval_refusal_logs: failures imports HIP_RUNTIME_ERROR from hyperloom.common.failure_signature, which fix(failure-signature): a HIP call that failed while running is not a missing kernel #2141 adds. They are drop-in once fix(failure-signature): a HIP call that failed while running is not a missing kernel #2141 has merged (or they go to the layer above).
  • accuracy.py with common/accuracy.py and test_accuracy: kept out for size and for the size of src/hyperloom/common; their only importer is local, so they go with it.
  • The upper layer, which needs these modules: socket.py, framework/copies.py, hosted_spec.py, local.py (with sandbox/node.py, profile_client.py, failures and accuracy), and test_socket, test_boot_watch, test_ports, test_spec_sections.
  • The runner core (run, jit, native, inventory, rewrite, rounds, snapshot, platform_env, __main__) and the hosted path, as in feat(measure): add the leaf modules of the measurement core: instruction sheet, result manifest, signing and what they stand on #2157.
  • .importlinter lines: multibranch-dev has none for measure, so none are added.

Size

5538 production Python lines in 24 files against the 1000-line diff budget (size-exception label, reason here), 4472 lines of tests and helpers. The reason is the layering, as in #2157: these are the modules that import nothing above the leaf layer, so they can be read and merged before the socket and the local backend, which cannot be reviewed without them. Since the first cut dev's compile and farm grew (588 to 889, 218 to 528) and now import the stale-module logic (jit/modules, framework/table_modules) and the native build store (jit/native, 874), which belong to this layer: 2311 of the added lines. The largest file is compile.py (889 lines, module limit 1200). The set splits at one boundary if the reviewers prefer: the compiled-code cache and the archive (jit/key, jit/locations, jit/store, jit/session, archive, jit/native) against the rest; compile and farm now import jit.native and jit.modules, so they would go second.

CI state

At the new head (a31c34971) CI has not been re-checked yet. Known before the refresh: CodeQL red on the two intended py/overly-permissive-file alerts on jit/store.py (item 3; line numbers move with the refresh). The earlier main red (test_multi_node_kept_patch_restore.py collection error) was fixed by #2149, which the base now contains. code-metrics was not re-run locally (no jscpd here); it is expected to report src/hyperloom/common directory size 59 -> 60 for common/aiter_layout.py (threshold 50; the directory is already over it on main).

Review template

  • Linked issue(s): none.
  • Tests: added, taken from multibranch-dev with the code (374 tests, see above). Run locally on Python 3.10 and 3.11 with only base and this PR's files on the path: the PR's 19 test files: 372 passed, 2 skipped on each; and, with -n 4, src/hyperloom/measure src/hyperloom/plugs src/hyperloom/contracts src/hyperloom/sandbox src/hyperloom/orchestrator/trace_analysis scripts/tests plus common/tests/test_dedup_guard.py, common/tests/test_llm_client_architecture.py, inference_optimizer/tests/test_common_import_lint.py, inference_optimizer/tests/test_packaging_lint.py: 2620 passed, 15 skipped on each. Also run: ruff check . and ruff format --check . (2454 files), and lint-imports --no-cache (11 kept, 0 broken). Wheel contents, Gitleaks, bandit and code_metrics.py were not re-run for this refresh; CI decides.
  • Check that stays red: CodeQL, for the two intended py/overly-permissive-file alerts on jit/store.py; the reviewers decide whether to dismiss them.
  • Findings that do not fail a gate: bandit at low severity lists subprocess (B404, B603) in compile.py and farm.py, each as an argv list without a shell. The code-metrics design check A12 (advisory) notes protocols with a single implementation, ResultArchive in archive.py and BlobStore in compile.py; multibranch-dev has no second one either.
  • Size/complexity triggers crossed: the 1000-line diff budget, with the reason under "Size"; src/hyperloom/common directory size (see CI state).
  • If this refactors: not a refactor.
  • Observable effect: none. This is new code: nothing on main imports these modules, no CLI, entry point or flag uses them, and the legacy flow behaves as before. What exists afterwards: 23 new files under hyperloom/measure, hyperloom/common/aiter_layout.py, and one entry fewer in the architecture guard's list of hosted implementations.
  • Breaking changes: no.
  • PR addresses single concern: yes (the middle layer of the measurement core: the modules between the leaves and the socket).
  • Root cause is upstream: no.

🤖 Generated with Claude Code

baseline-raise: why the code-metrics gate is waived here

The only "worse than the base" finding is the entry count of src/hyperloom/common (one more than at the merge base; the directory was already over the 50-entry limit on main). The extra entry is common/aiter_layout.py (30 lines): where an installed aiter keeps its device sources, found without importing aiter (importing it opens every GPU). Measure, the specialist precheck and trace analysis all read it, so it belongs in common by the repo's rule for business-agnostic helpers, and multibranch-dev keeps it there. Moving it elsewhere only for the count would make main and dev disagree on its path. Applied by the author on the decision of the workstream owner (2026-10-31).

zoroyihan7 and others added 2 commits October 23, 2026 22:16
The architecture guard lists hyperloom.measure.archive among the hosted
implementations that nothing outside their owners may import. That held while
only the hosted path read the archive; the jit cache store (measure/jit/store.py,
added in the next commit) also uses its fsync_dir, so the entry leaves the list,
as hyperloom.hosted, hyperloom.sandbox and hyperloom.measure left _DEV_ONLY with
the layers that made them stale. hyperloom.measure.runner, .hosted, .sandboxes and
.consistency stay listed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Modules that import only the leaf layer, the measurement contract, the common
helpers and each other, taken from multibranch-dev as they are:

- compile: turns a recipe and the change a test request carries into the
  environment overlay the test runs in;
- farm: rebuilds the framework a candidate changed next to the image's and puts
  it first on the import path; attachments unpacks the sheet's attachments;
  pins reads the tool commits Hyperloom pins;
- framework/base, rows and merge: the tuned tables as the session found them, a
  few rows laid over the image's default table, and the directory aiter writes a
  test's merged tables to;
- jit/key, locations, store and session: the key a compiled-code cache is stored
  under, the cache files that name their directory, the append-only store and one
  test's use of it;
- archive: the write-once archive of verified result bundles, whose fsync_dir the
  jit store also uses;
- round_marks: what a round left on disk about when its parts began.

Tests: those of the modules above, with the helpers they share (framework_fixtures,
aiter_excerpts, _recipes, tunableop_table, triton_cache), and test_aiter_merge_hook,
which the leaf layer left out because it builds a framework copy. All are taken
from multibranch-dev as they are.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@zoroyihan7 zoroyihan7 added type:feature New capability or improvement size-exception Waives the PR diff budget; added by a maintainer with a reason in the PR description labels Oct 23, 2026
@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Code metrics gate: PASSED

Judged: the 49 files this change touches. Files it does not touch never fail it; anything found there is listed under Outside this change for information.

Dimension Limit New Worse Improved Over the limit vs base
Cyclomatic complexity fails > 20 0 0 0 0 +0
Cognitive complexity fails > 30 0 0 0 34 +0
Function length (lines) fails > 60 0 0 0 249 +0
Nested block depth fails > 4 0 0 0 37 +0
Parameter count fails > 5 0 0 0 0 +0
Module length (lines) warns > 800, fails > 1200 0 0 0 42 +0
Duplicated lines per file fails > 0 0 0 0 76 +0
Dead code (vulture) fails > 0 0 0 0 2 +0
Directory size (entries) fails > 50 0 0 0 1 +0
Check Findings Effect
Comments 0
Broad catch without a reason 0
Broad catch that records nothing 0
English only 0
Production code imports test code 0
Repeated literals 0
A1 Private name of another package 0
A8 Flag argument 0
A9 Tuple return 0
A10 Raw vocabulary comparison 0
A11 Hidden global write 0
A12 Pure forwarder 0
A12 Single-implementation abstraction 2 advisory, never fails

A12 Single-implementation abstraction (advisory, never fails): 2

Where Problem
src/hyperloom/measure/archive.py:42 A12 single implementation: ResultArchive is abstract and only DirResultArchive (src/hyperloom/measure/archive.py) implements it; use the implementation until a second one exists
src/hyperloom/measure/compile.py:148 A12 single implementation: BlobStore is abstract and only LocalBlobStore (src/hyperloom/measure/compile.py) implements it; use the implementation until a second one exists

How to fix

  • New or worse: split the unit (extract a function, a class, a module) until it is back under the limit, or no worse than the base branch has it. There is nothing to record: the base is the reference.
  • A12 Single-implementation abstraction: use the one implementation directly until a second one exists; a lower package declaring an interface for a higher one to implement is exempt.
  • Override: the baseline-raise PR label waives new and worse findings and the failing design checks (they stay listed here). Use it only with the reason in the PR description; it waives nothing else.
  • Tools for a local run: pip install ruff==0.16.2 complexipy==8.0.1 radon==6.0.1 vulture==2.16 and npm install -g jscpd@5.4.1 (or point CODE_METRICS_JSCPD at another jscpd command, such as npx --yes jscpd@<version>).

Module length warning: over 800 lines, within the 1200-line failure limit (not failing): 1

Module Lines Limit
src/hyperloom/measure/compile.py 856 warns > 800

Verdict computed by the gate scripts in /home/runner/work/_temp/code-metrics-gate, not by this tree's copy.

Thresholds and sources: [tool.hyperloom.code_metrics] in pyproject.toml. Tools: complexipy 8.0.1, jscpd 5.4.1, radon 6.0.1, ruff 0.16.2, vulture 2.16.

out.write(snapshot.to_bytes())
out.flush()
os.fsync(out.fileno())
os.chmod(pending, _BLOB_MODE)
def _copy_checked(source: int, target: Path, stored: StoredFile, shown: str, *, mode: int) -> None:
"""Copy ``source`` to a new file ``target`` while hashing it; a file that is not the hash and size listed is removed."""
digest, total = hashlib.sha256(), 0
descriptor = os.open(target, _FETCH_FLAGS, mode)
Comment thread src/hyperloom/measure/archive.py Fixed
Comment thread src/hyperloom/measure/archive.py Fixed
Comment thread src/hyperloom/measure/compile.py Fixed
Comment thread src/hyperloom/measure/compile.py Fixed
zoroyihan7 and others added 3 commits October 23, 2026 22:31
CodeQL flags py/overly-permissive-file twice in jit/store.py: a stored blob and a
snapshot were given mode 0o444, which makes them world readable. The store keeps
no write bit on a blob (it is never rewritten) and creates its incoming
directories 0o700 and a test's private copies 0o600, so 0o400 is the same
owner-only rule with the same refusal to write.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The previous commit narrowed the mode of a stored blob to 0o400 because CodeQL
flags 0o444 (py/overly-permissive-file). The blobs are content-addressed and
read-only by design and are read by another account: on the hosted path the host
writes them and a sandbox account reads them through a read-only mount, so 0o400
would leave that account unable to read a blob and the cache silently unused.
The mode returns to 0o444, as on multibranch-dev, with a comment that gives the
reason. The two CodeQL alerts are intended; a reviewer decides whether to dismiss
them.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
zoroyihan7 and others added 2 commits October 30, 2026 00:49
…branch-dev 629dfc8

Every file this PR carried now matches origin/multibranch-dev 629dfc8, except the
four-line comment above _BLOB_MODE in jit/store.py, kept from the earlier commits.

Moved to dev's version: compile, farm, round_marks, framework/base, jit/key,
jit/session, jit/store, and the tests test_compile, test_farm, test_round_marks,
jit/tests/test_key, test_session, test_store.

Added, because the dev versions of compile and farm now import them and they
belong to this layer (they import only the leaf layer, contracts, common and
each other):
- common/aiter_layout.py (image_aiter_meta, moved there from jit/modules on dev)
- framework/table_modules.py
- jit/modules.py
- jit/native/{__init__,config,key,laying,needs,record,store,trust}.py
and the tests whose imports are all here or below: framework/tests/test_table_modules,
jit/tests/test_modules, tests/test_compile_aiter_meta, tests/test_compile_aiter_tables.

Removed: nothing.

Co-Authored-By: Claude <noreply@anthropic.com>
Comment thread src/hyperloom/measure/compile.py Fixed
Comment thread src/hyperloom/measure/compile.py Fixed
Comment thread src/hyperloom/measure/compile.py Fixed
Comment thread src/hyperloom/measure/compile.py Fixed
Comment thread src/hyperloom/measure/compile.py Fixed
zoroyihan7 and others added 3 commits October 30, 2026 01:11
Co-Authored-By: Claude <noreply@anthropic.com>
The generic-api-key rule of the secret scan reads `key: Ed25519PrivateKey` as
an assignment. Brings trust.py up to multibranch-dev d8755f4.

Co-Authored-By: Claude <noreply@anthropic.com>
…ion alerts

- EnvEntry and EnvRoot move from measure.compile down to
  measure.jit.native.needs, which already used them for NativeStack and
  ModulePlan; compile imports them from there and still exports them.
  needs no longer imports compile, so the cycle is gone rather than
  hidden behind a lazy import.
- Protocol members (BlobStore, NativePlan, ModulePlan, ResultArchive)
  are stubs with a docstring body instead of a bare `...` expression;
  the archive ones already had a docstring, so the `...` was dead.
- The concurrent-publish test records the Exception a publisher raised,
  no longer KeyboardInterrupt or SystemExit.

Co-Authored-By: Claude <noreply@anthropic.com>
@zoroyihan7 zoroyihan7 added the baseline-raise Waives new, worse and baseline-growth code-metrics findings; reason required in the PR description label Oct 31, 2026
Brings main's hyperloom_common layout (#2308) in through the leaves.

Co-Authored-By: Claude <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

baseline-raise Waives new, worse and baseline-growth code-metrics findings; reason required in the PR description size-exception Waives the PR diff budget; added by a maintainer with a reason in the PR description type:feature New capability or improvement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants