Repository navigation
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
Conversation
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>
Code metrics gate: PASSEDJudged: 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.
A12 Single-implementation abstraction (advisory, never fails): 2
How to fix
Module length warning: over 800 lines, within the 1200-line failure limit (not failing): 1
Thresholds and sources: |
| 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) |
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>
…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>
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>
Brings main's hyperloom_common layout (#2308) in through the leaves. Co-Authored-By: Claude <noreply@anthropic.com>
This branch has not been deployed
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.
Stacked on #2157 (the leaf layer): the base branch of this PR is
feature/yihan/measure-leaves(#2157's head, now533f6beb9), so the diff below is this PR's own. The modules here import the leaf layer (#2157),hyperloom.contracts.measurementandhyperloom.contracts.recipe(#2153),hyperloom.common.outside_facts(#2155) and each other. When the stack below has merged, this PR retargets tomainwithgit 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 frommultibranch-devas it is (refreshed on 2026-10-30 to629dfc8b1), with the tests whose imports are all in this change or below it, except a comment. Commits:test(plugs): let the jit store import the result archive's fsync_dir: one line of the architecture guard (below).feat(measure): add the middle layer of the measurement core: the original 13 modules and their tests (cut from555e863eb).fix(measure): store a cached blob readable by its owner onlyand 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).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.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'scompileandfarmnow import added (they belong to this layer).The modules (lines), and everything each imports from this repository:
compile.pycontracts.measurement,contracts.recipe,common.outside_facts(#2155),common.aiter_layout,framework.rows,framework.tables,framework.table_modules,jit.modules,jit.native.needsfarm.pyspec,jit.key,jit.native.laying,jit.native.record,common.aiter_layout,common.outside_facts,common.benchmark_configandorchestrator.actions.executors.magpie_agentx(both onmain), PyYAMLjit/store.pyarchive,docs,fsafe,jit.locations,jit_kindsjit/session.pyfsafe,jit.builds,jit.locations,jit.notes,jit.store,jit_kindsjit/modules.pycommon.outside_facts,jit.keyround_marks.pyboot_watch,phases,common.outside_factsjit/key.pycommon.outside_facts(#2155),docsframework/table_modules.pyframework.rows,framework.tablesframework/base.pycommon.outside_facts(#2155),framework.tablesjit/native/(8 files)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 otherarchive.pydocs,fsafe,manifestframework/rows.pyframework.tablespins.pyinstall.shdocsjit/locations.pydocs,fsafe,jit_kindsframework/merge.pyaiter_merge_hook,framework.baseattachments.pyspeccommon/aiter_layout.pyimage_aiter_meta, moved out ofjit/moduleson dev in3f7953028)common.outside_factsMeasured: each of the 24 production modules imported alone in a fresh isolated interpreter (
python -I) loads, and none of them loadshyperloom.sandbox,hyperloom.measure.runner,socket,local,hosted,failuresorhyperloom_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) andtest_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) andtest_rows(9),test_farm(11),test_attachments(6), andplugs/tests/test_architecture(16). Helpers they share:framework_fixtures,aiter_excerpts,_recipes,tunableop_table,jit/tests/triton_cache.test_aiter_merge_hooktests a module of #2157; it waits here because it builds a framework copy withframework_fixturesandframework.merge.What differs from
multibranch-devorigin/multibranch-devat629dfc8b1(checked file by file: 24 modules and 25 test and helper files), except the comment in item 3. (629dfc8b1ande81281d60, 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 inhyperloom.measureonly inrunner/native/builder.py, which is not here.)plugs/tests/test_architecture.py(the first commit):hyperloom.measure.archiveleaves_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 nowjit/native/store.py) takefsync_dirfrom it. This is the same kind of edit as the_DEV_ONLYline 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 ismain's, not dev's.hyperloom.measure.hosted,.sandboxes,.consistencyand.runnerstay listed.jit/store.py: the only difference frommultibranch-devis a four-line comment above_BLOB_MODE = 0o444; the mode itself is as onmultibranch-dev. CodeQL'spy/overly-permissive-fileflags it twice (the open of a stored blob, and thechmodof 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.pyproject.tomlis unchanged: no module here adds a dependency.What is not in this PR, and why
jit/native/tests/test_key(it importsmeasure.runner.native.tests._native),test_compile_aiter_modules(importsmeasure.runner.native.aiter_modules),test_farm_aiter_metaandtest_farm_aiter_meta_root(importmeasure.runner.import_outcome),framework/tests/test_copies_farm(importsframework.copiesandsocket): their imports reach the runner core or the upper layer, so they go with those.jit/nativeon dev lives undermeasure/runner/native(builder, harvest, recipes, parity, ...): runner core, not here.failures.pyand its teststest_log_causes,test_returncodes_and_failuresand the helpereval_refusal_logs:failuresimportsHIP_RUNTIME_ERRORfromhyperloom.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.pywithcommon/accuracy.pyandtest_accuracy: kept out for size and for the size ofsrc/hyperloom/common; their only importer islocal, so they go with it.socket.py,framework/copies.py,hosted_spec.py,local.py(withsandbox/node.py,profile_client.py,failuresandaccuracy), andtest_socket,test_boot_watch,test_ports,test_spec_sections.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..importlinterlines:multibranch-devhas none formeasure, so none are added.Size
5538 production Python lines in 24 files against the 1000-line diff budget (
size-exceptionlabel, 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'scompileandfarmgrew (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 iscompile.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;compileandfarmnow importjit.nativeandjit.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 intendedpy/overly-permissive-filealerts onjit/store.py(item 3; line numbers move with the refresh). The earliermainred (test_multi_node_kept_patch_restore.pycollection error) was fixed by #2149, which the base now contains.code-metricswas not re-run locally (no jscpd here); it is expected to reportsrc/hyperloom/commondirectory size 59 -> 60 forcommon/aiter_layout.py(threshold 50; the directory is already over it onmain).Review template
multibranch-devwith 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/testspluscommon/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 .andruff format --check .(2454 files), andlint-imports --no-cache(11 kept, 0 broken). Wheel contents, Gitleaks, bandit andcode_metrics.pywere not re-run for this refresh; CI decides.py/overly-permissive-filealerts onjit/store.py; the reviewers decide whether to dismiss them.subprocess(B404, B603) incompile.pyandfarm.py, each as an argv list without a shell. The code-metrics design check A12 (advisory) notes protocols with a single implementation,ResultArchiveinarchive.pyandBlobStoreincompile.py;multibranch-devhas no second one either.src/hyperloom/commondirectory size (see CI state).mainimports these modules, no CLI, entry point or flag uses them, and the legacy flow behaves as before. What exists afterwards: 23 new files underhyperloom/measure,hyperloom/common/aiter_layout.py, and one entry fewer in the architecture guard's list of hosted implementations.🤖 Generated with Claude Code
baseline-raise: why the code-metrics gate is waived hereThe 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 onmain). The extra entry iscommon/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 incommonby the repo's rule for business-agnostic helpers, andmultibranch-devkeeps 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).