Vendor KernelForge in-tree as Hyperloom's built-in kernel-opt agent - #1338
Merged
Conversation
…yet wired)
Copies KernelForge @ 85264fc6 in as plain files -- no git history, no
subtree. Nothing imports it yet and no build/test/coverage config points at
it; this commit is deliberately inert so the reflow (P3) and the rename (P2)
land as reviewable diffs of their own.
src/{kernel_agents,forge_gemm_tune,forge_llm} packages, verbatim
src/kernel_agents/data/{knowledge_base,local_knowledge,examples,serving_patches}
setuptools has no equivalent
of hatchling force-include,
so the data trees have to sit
physically inside the package
src/kernel_agents/tests was KernelForge's root tests/;
Hyperloom keeps tests per package
docs/kernelforge content pages only; the
duplicate conf.py / Makefile /
sphinx requirements / about /
license / release-notes are
dropped in favour of
Hyperloom's, and the pages are
wired into docs/sphinx/_toc.yml.in
deploy/ GPU runner registration used by
the kernel-bench workflow (P9)
Not copied: src/forge_fusion (empty), KernelForge's pyproject.toml, .github/,
experiments/.gitkeep, and the root metadata files Hyperloom already has.
src/forge_gemm_tune/pyproject.toml is kept on purpose -- forge_gemm_tune stays
separately pip-installable.
.gitignore: anchor the run-artifact `bench.py` rule to `/bench.py`. Unanchored
it also swallowed src/kernel_agents/mcp_server/tools/bench.py, which is real
source.
ruff: the three new packages are excluded for now -- they are still formatted
at KernelForge's line-length 100. P3 reflows them at 120 and removes the
exclusion. mypy needs no change; `files` is scoped to src/hyperloom.
Verified: ruff check/format clean; test_packaging_lint fails only on
test_no_undeclared_assets_under_src (expected, fixed in P5); 1376 files added.
Baselines recorded -- KernelForge 4249 passed / 9 skipped / 8 xfailed.
`kernel_agents` cannot stay: Hyperloom already owns KERNEL_AGENT_* env vars
and src/hyperloom/agents/kernel, and two things called "kernel agent(s)" in
one tree is a reading hazard. `kernelforge` matches the KERNELFORGE_* env
prefix the code already uses. forge_gemm_tune and forge_llm keep their names.
No import shim. 149 submodules cannot be covered by an __init__.py alias, and
a partial shim would turn "missed rename" into "works until it doesn't".
Bulk substitution over src/{kernelforge,forge_gemm_tune,forge_llm},
docs/kernelforge and deploy/, with two carve-outs:
- src/kernelforge/data/** was substituted for `kernel_agents` (4 lines in 2
files: a runnable `python3 -m ...bench` command and two source paths in a
methodology note) but NOT for `kernel-agents`. The 18 remaining files spell
it inside ${KA_WORKSPACE}/kernel-agents-workspace/... paths recording what
past campaigns actually ran. Rewriting those would falsify the record.
- KERNEL_AGENTS_MAX_TURNS and KERNEL_AGENTS_MODEL keep their spelling.
The first exists only so Config.from_env can warn that it is ignored;
renaming it silences the warning. The second is a back-compat alias for
FORGE_AGENT_MODEL, and renaming a back-compat alias defeats its purpose.
(This departs from the plan, which had them renamed.)
Hand-edited, because a string is not an import and fails at call time:
- forge_llm/agent_backends/registry.py now reads both the new
`kernelforge.agent_providers` group and the legacy
`kernel_agents.agent_providers` one, deduped by name, with a
DeprecationWarning. The module's own comment argues that renaming this group
drops every published plugin as a single log line -- so it isn't renamed,
it's superseded.
- kernelforge/cli.py: `version_option(package_name=...)` pointed at a
distribution named `kernel-agents`, which no longer exists. It is now
`hyperloom-inference_optimizer`.
- fellows/constants.py (9 dotted prompt modules), cli_forward_compat._META_KEY,
fusion/campaign.py + rewrite_by_flydsl/optimize.py argv, two MCP SERVER_NAMEs,
the codex cache dir, and deploy/robust/kernelforge.yaml's PATH shim were all
covered by the substitution and verified individually.
New guard: src/kernelforge/tests/test_rename_completeness.py greps every
tracked file and asserts the surviving spellings are exactly the ones listed
above, each with a written reason. It carries two entries marked TEMPORARY for
src/hyperloom/** and docs/** -- P7 rewires those call sites and deletes the
entries, at which point the test proves both halves are done.
Verified: compileall clean; all 158 non-test modules across the three packages
import; all 9 fellow prompt modules import; --help / forge-loop / forge-fuse /
forge-rewrite-by-flydsl all exit 0; ruff clean; rename guard green.
`--version` still raises (the distribution is not declared until P5).
Formatting only, in its own commit. KernelForge formatted at 100, Hyperloom at 120, and folding this into any commit that changes behaviour would bury the behaviour under 335 files of rewrapping. Review with `git show --stat`. - Drops the temporary ruff exclusion P1 put on the three packages. - Keeps src/kernelforge/data excluded permanently: knowledge base, examples and serving patches are content, not source we own the style of, and the example drivers are .py files ruff format would otherwise rewrite. - One new per-file E402 ignore (fusion/llm_failure.py), matching Hyperloom's per-file convention rather than KernelForge's repo-wide ignore. It was the only E402 in ~90k lines; everything else passed E/F/W unchanged. `git diff -w` is not a useful check here -- the formatter joins wrapped lines, which -w cannot see through. Verified instead by parsing every one of the 335 reformatted files before and after and comparing ASTs (with adjacent f-string literal parts folded, since implicit concatenation changes JoinedStr structure without changing the value). 331 are byte-identical trees. The other 4 differ only in docstrings that opened with a quote character (`""""git failed"...`), where ruff inserts a separating space -- a change to docstring text, nothing else. ruff check + ruff format --check clean over the whole repo (1520 files); all three packages still import; rename guard green.
The data trees now live at src/kernelforge/data, so there is no repository
root to fall back to and no "source checkout" branch to take. resources.py
drops _SOURCE_ROOT and is_source_checkout() and gains a writable-state root
that is deliberately *not* the package.
- resource_path(name, project_root=None, *, missing_ok=False) raises
FileNotFoundError instead of returning a path that does not exist. The old
behaviour turned a missing data tree into forge-loop running against an
empty knowledge base, with no error and no log line. The two call sites
that legitimately tolerate absence -- loop/task_preparer.py (has a compact
contract fallback) and fusion/discover.py (has $FORGE_LOCAL_KNOWLEDGE) --
pass missing_ok=True.
- default_project_root() now contracts for a *writable* directory, never
site-packages and never cwd: $KERNELFORGE_PROJECT_ROOT ->
$USER_DATA_PATH/kernelforge -> ~/.cache/hyperloom/kernelforge, mirroring
knowledge/experience_store.py.
- writable_knowledge_root() is the new destination for knowledge the loop
*produces*. AutoEvolver.from_config() targets it instead of
config.knowledge_dir, which resolves to the packaged curated tree.
- TuningDatabase no longer mkdirs in __init__ -- the write flag guarded the
writes but not the directory, so merely constructing one created a tree.
The mkdir moved onto the three write paths.
- assert_sandbox_grant() validates a directory before it is handed to an
agent sandbox allowlist. Claude's add_dirs grant is read *and* write, and
these paths used to be derived from a repository root; a wrong answer is
now an over-broad grant rather than a missing directory.
src/kernelforge/tests/conftest.py is the long-lived guard:
- an autouse fixture that hooks open/os.open/mkdir/makedirs/remove/rename at
the primitive level and fails any test that writes inside the installed
package (__pycache__ excepted -- bytecode is the interpreter's business);
- an autouse fixture pointing $KERNELFORGE_PROJECT_ROOT at a per-test tmp
path, so the suite neither accumulates state in the developer's home
directory nor reads back another test's leftovers;
- REPO_ROOT / requires_repo_root, for the P6 tests that need repository
metadata and must skip under a wheel install rather than guess a depth.
Also pulled forward from P6 because they blocked collection or were direct
P4 fallout: the 8 `from tests.` cross-test imports (the data/ trees were
deliberately left alone -- their `from tests.utils import ...` lines are
aiter/flydsl sample code, not ours), and test_packaged_resources.py's
parents[1] path.
Two P2 gaps closed while here: the fake entry-point registry in
test_provider_registry.py asserted on the group name and so broke against the
dual-read loader, and the deprecated kernel_agents.agent_providers group had
no coverage at all. Both groups are now tested, including precedence.
Verified: the four data trees resolve with 118/758/51/3 files; resource_path
on a missing name raises; chmod a-w on the package then importing
kernelforge.learning.auto_evolve + constructing Config works and resolves
every writer outside the package; `grep -rnw 'is_source_checkout|_PACKAGED_DATA_ROOT' src/`
is empty; ruff check + format clean. src/kernelforge/tests: 3543 passed,
38 failed, 3 errors -- every failure pre-dates this commit (confirmed by
re-running against a stash) and is on P6's list: repo-root-relative fixture
paths, wheel-content assertions, and subprocess PYTHONPATH.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Dependencies. Core stays `[]` -- the bare wheel deliberately carries no
third-party runtime deps. KernelForge's five core deps land as a new `forge`
extra holding only what it uniquely needs (click, PyYAML, anthropic); httpx
and openai were already in `llm`. `runtime` now pulls `[llm,web,forge]`, so
install.sh's existing `pip install -e "${REPO_ROOT}[test]"` gets forge for
free -- which is the whole point of P8.
Three deliberate omissions:
* `pandas` is dropped. Zero imports across all three vendored packages;
it was dead weight in KernelForge's metadata.
* `torch` is not declared, though 40 modules import it. The ROCm build
comes from the container image; a bare `torch` specifier resolves to the
CUDA wheel on PyPI and would quietly replace it.
* KernelForge's `claude`/`codex` extras are gone. Hyperloom's
`claude-agent-sdk>=0.2.110` and `openai-codex>=0.144` already cover them,
and `openai-codex==0.144.4` next to `>=0.144` in one distribution's
metadata is an install-time resolution error, not a runtime surprise.
`test_pure_codex_imports.py` asserted that exact pin verbatim; it now
asserts the floor and forbids any `openai-codex==` from creeping back.
`profiling` is ported as `forge-profiling` (renamed to say whose profiler it
serves). Nothing imports it -- it satisfies rocprof-compute, whose binary
comes from the ROCm image.
Packaging. `packages.find.exclude` gains `kernelforge.data{,.*}`: the data
trees hold 35 .py files, and under implicit-namespace discovery setuptools
would hand out `examples.mori_ep_dispatch_combine.driver` as an importable
top-level module. `package-data` gains `kernelforge = ["data/**/*"]` -- one
type-agnostic glob, because the trees span .md/.json/.py/.txt/.sh/.isa/
.yaml/.jsonl/.patch and a missed extension breaks only wheel installs, which
no dev or CI path exercises.
`test_packaging_lint.py` flagged forge_gemm_tune's README, HYPERLOOM_-
INTEGRATION.md and pyproject.toml as undeclared assets. They describe the
source tree, not the installed package, so they go in `_UNPACKAGED_ASSETS`
with that justification rather than being shipped.
Test + coverage config. `testpaths` widens from `src/hyperloom/**/tests` to
`src/**/tests`, which reaches both new suites in one line. `coverage.source`
gains the three packages; KernelForge's 24 `omit` entries are ported with
their comments intact, plus `src/kernelforge/data/*` (shipped sample kernels,
not library code). `fail_under = 90` is left alone -- the denominator just
grew by ~90k statements and a mis-transcribed omit glob would block the merge
at the worst possible moment. Set the COVERAGE_RELAX_FAIL_UNDER repo variable
on this branch; P10 measures and reopens the gate.
Bandit skips `src/kernelforge/data`: illustrative third-party kernels.
mypy `files` stays `src/hyperloom`. 90k lines of unannotated code in the
advisory job would be noise; recorded as a follow-up.
Console scripts: `kernelforge`, `forge-gemm-tune`, and `kernel-agents` as a
deprecated alias for one release. The rename-completeness allowlist already
covered the script line; widened to cover the comment above it too.
Verification
* `pytest test_packaging_lint.py` -- 7 passed (proves package-data
coverage, script resolvability, and test-package exclusion together)
* clean venv `pip install -e ".[test]"` -> `pip check` clean; resolved
openai-codex 0.147.0, one specifier per package in the built METADATA
* `python -m build --wheel`: data trees ship at exactly the P4 file-count
floors (knowledge_base 118, local_knowledge 758, examples 51,
serving_patches 3); zero `/tests/` entries; top_level.txt is exactly
forge_gemm_tune / forge_llm / hyperloom / kernelforge -- no data
subtree leaked out as a package
* `pytest --collect-only`: 19469 = hyperloom 15147 + scripts 52 +
kernelforge 3601 + forge_gemm_tune 669, i.e. both P0 baselines plus the
4 tests P2/P4 added
* ruff check + format clean over src/ and scripts/ (1520 files)
* forge suites: 41 failed / 4209 passed / 9 skipped / 8 xfailed / 3 errors.
All 41 pre-date P5 (verified by `git stash -u` + re-run) and are on P6's
list: repo-root-relative fixture paths, subprocess PYTHONPATH, and
test_wheel_content.py, which P6 deletes now that packaging.yml can make
the same assertions against a real wheel.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The tree moved from KernelForge's repo root to src/kernelforge/tests, so
every path a test derived from its own __file__ now points somewhere else.
None of those failed loudly: a repo-root path that resolves to an empty
directory makes a scan test pass with nothing to scan.
Resource lookups now go through kernelforge.resources.resource_path()
(test_serving_patches, test_lessons, test_measurement_fidelity,
test_review_regressions, test_mori_kb_injection). Because resource_path
raises FileNotFoundError instead of returning a non-existent path, the
module-level constants in these files are collection-time assertions: a
tree that fails to ship breaks collection rather than passing vacuously.
Repo-root metadata uses conftest's REPO_ROOT + requires_repo_root, which
skips under a wheel install (test_deploy_template_credential_gate,
test_ci_e2e_dispatch). The latter previously only looked green because a
shutil.which() skipif masked its dead path.
Source-tree paths use a new conftest SRC_ROOT (test_pr_stdio_server,
test_tracker, test_rewrite_cli_contract, test_campaign_cross_process),
and a session-scoped autouse fixture exports it on PYTHONPATH. About 250
sites in this tree spawn sys.executable and import kernelforge; pytest's
`pythonpath` ini is in-process only, and upstream CI never noticed because
it always ran against `pip install -e`. Setting it once beats patching 250
call sites.
test_wheel_content.py is deleted: it built its own wheel with `pip wheel .`
and globbed for kernel_agents-*.whl, a distribution that no longer exists.
Its assertions move into scripts/check_wheel_contents.py, already wired to
packaging.yml, as per-tree file-count floors -- a tree that lost all but one
file would otherwise still satisfy the "declaration is not dead" check.
Running that script against the P5 wheel surfaced two real bugs in it:
* _excluded_dir_names() read `kernelforge.data` literally and put
"kernelforge" in the leak vocabulary, reporting all 1091 package entries
as shipped test files. Patterns that fall under a package-data key are
now skipped -- that tree is excluded from *package discovery* (its .py
sample kernels must not be importable modules) while its files do ship.
* `data/**/*` matches directories, which are not wheel entries, producing
225 spurious "declared X is missing" errors. Only files are checked now.
test_mori_kb_injection's force-include parser is gone with hatchling's TOML
table; the behavioural half remains, pointed at resource_path.
test_fellow_prompt_contract's nine sha256 snapshots are re-taken. Rendered
against upstream 85264fc6 and diffed first: each backend differs by exactly
one line, the P2 rename inside a shared knowledge card.
forge_gemm_tune's venv-building packaging test gets a standalone_wheel_e2e
marker so the coverage matrix can deselect it.
Verified: 4250 passed, 9 skipped, 8 xfailed (4267 collected) -- the KernelForge
P0 baseline of 4266 plus 4 tests added in P2/P4 minus the 3 deleted here.
`grep -rnE '^\s*(from|import) tests\b' src/` is empty. check_wheel_contents.py
reports OK on the P5 wheel. ruff check and format are clean.
The 17 failures seen when running from /tmp are upstream cwd sensitivity in
the aiter/vllm discovery tests, not fallout from the move: the same six files
run from /tmp against the 85264fc6 checkout produce the identical 6 + 11.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Point every orchestrator-side dispatch at the vendored package: the argv for
`python -m kernelforge.cli` (forge-loop, forge-rewrite-by-flydsl, the vendor
playbook), the find_spec probe, the codex session import, and the docs.
Three changes here are behavioural, not renames:
* The vendor playbook no longer hard-fails when $FORGE_PATH is unset. That
was a precondition of the two-repo world; the task bundle now resolves
inside the packaged kernelforge. `_vendor_operator_playbooks` likewise
drops its `/nonexistent-forge-path` sentinel, which only dressed "the env
var is unset" up as "the file is missing".
* `_forge_loop_constant()` logs a WARNING instead of silently falling back.
A missed module rename used to mean forge-loop planned against the wrong
wall-clock budget forever, with nothing in the log to say so.
* forge's provenance entry switches from `git rev-parse` on a checkout to
the distribution version, because there is no checkout left to probe.
The producer-supplied root_dir still yields a commit, so both halves of
the provenance survive.
`_resolve_kernelforge_root` becomes `_resolve_serving_patches_root`, imports
kernelforge lazily so a host without forge can still import Hyperloom, and
gains the test coverage it never had. One of those new cases pins a real gap:
a $FORGE_PATH predating serving_patches used to resolve to nothing and
silently drop the patch, rather than falling through to the packaged tree.
Verified against main in the same worktree: collection 15153 vs 15147 (+7 new
cases, -1 replaced), and the 22 failures are the same 22 that fail on main.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
$FORGE_PATH named a KernelForge checkout that Hyperloom grafted onto the child PYTHONPATH. With KernelForge vendored, no value of it is coherent: the module resolver wanted <root>/src while the two data resolvers wanted the pre-inlining <root>/examples and <root>/serving_patches, and those trees now live under src/kernelforge/data/. The only tree that still satisfies the old shape is the archived repository, which the docs actively told operators to point at -- so a stale export would shadow the packaged copy with an archived one, silently and with no log line. Removed from: _flydsl_rewrite.probe_capabilities() (drops its forge_root parameter; the capability cache collapses to a single "<installed>" key), forge_submit (_ensure_forge_on_path() deleted outright, three PYTHONPATH grafts collapse to dict(os.environ), the vendor task bundle resolves via resource_path(..., default_project_root(), missing_ok=True)), _vendor_operator_playbooks, _server_patcher (_KERNELFORGE_ROOT_ENV_VARS deleted), env_safety's allowlist, preflight, request_handlers, and four docs pages. The one need the override actually served -- an air-gapped operator dropping in a newer sglang serving patch ahead of an image rebuild -- is a data-tree substitution, which $KERNELFORGE_PROJECT_ROOT already serves through resource_path(name, project_root=...). Unlike $FORGE_PATH it now logs at WARNING when it wins, because every miss on the serving-patch path is fail-soft and therefore invisible in a green run. $KERNEL_OPT_BACKEND_ORDER is untouched: it selects forge vs geak and is orthogonal to where forge is found. Three integration tests that were skipped purely by the $FORGE_PATH gate now run unconditionally, and test_server_patcher_serving_patches_root.py is rewritten around the new precedence (packaged tree by default, explicit root and $KERNELFORGE_PROJECT_ROOT as logged overrides, neither a veto). The shell sites (install.sh, local_setup.sh and their two tests) are deliberately left alone: P8 deletes ensure_kernel_agents(), _kernel_forge_root() and resolve_forge() wholesale, and touching them here would half-do that. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ge CLI
src/ carried three forge packages after the vendoring snapshot: kernelforge,
forge_gemm_tune (with its own nested pyproject and its own forge-gemm-tune
console script) and forge_llm. Collapse them into one.
forge_gemm_tune -> kernelforge.gemm_tune
forge_llm -> kernelforge.llm
forge_llm.agent_backends -> kernelforge.agent_backends
agent_backends is promoted rather than re-exported: kernelforge.agent_backends
already existed as a compat shim doing `from forge_llm.agent_backends import *`
plus sys.modules aliasing, justified by forge_llm being their home. Once
forge_llm moves inside kernelforge that justification is gone, and promoting the
real modules keeps the published import path (and the
kernelforge.agent_providers entry-point group) byte-identical while deleting the
indirection.
The nested pyproject is deleted, so gemm_tune is no longer separately
installable. Its `main` group becomes `@click.group("gemm-tune")` registered on
kernelforge.cli, which is now the single CLI:
kernelforge gemm-tune {run,plan,evidence}
Callers follow: the kernel-agent tool and the orchestrator handler spawn
`python -m kernelforge.cli gemm-tune run`, and _forge_gemm_tune_available()
drops its shutil.which("forge-gemm-tune") branch -- that console script no
longer exists, and a stale one left by an old standalone install would resolve
to an unrelated tree. install.sh's ensure_forge_gemm_tune() had resolved a
checkout and pip-installed it editable; with no sub-distribution to install it
collapses to an import probe, which still catches a partial install at setup
time rather than mid-run.
Three identities are deliberately NOT renamed, because they key data that
already exists on disk: gemm_tune.__version__ (re-documented as the
tuner-artifact layout stamp that artifact_manifest writes), the manifests'
"tool": "forge-gemm-tune" field, and the ~/.forge_gemm_tune/ blocklist cache.
test_rename_completeness gains a second sweep, mirroring the kernel_agents one,
that forbids \bforge_llm\b / \bforge_gemm_tune\b anywhere in the tree. Word
boundaries keep unrelated identifiers (resolve_forge_llm_model,
_forge_gemm_tune_available) out of it; the home-cache path is the sole allowed
hit. gemm_tune/tests/test_packaging.py is deleted outright -- every test in it
asserted on the nested pyproject's package mapping or built the standalone
wheel; the root pyproject's packages.find plus test_packaging_lint.py cover what
remains.
Collection 19473 -> 19470 (-4 standalone-wheel tests, +1 new guard). Full suite
leaves the same 10 pre-existing environmental failures as HEAD, verified by
running the identical selection against a stashed tree.
forge now ships with this distribution, so install.sh's "find a checkout via $FORGE_PATH, then pip-install a separate distribution out of it" step has nothing left to install. A readiness probe replaces it. install.sh - Remove ensure_kernel_agents() / _kernel_forge_root() and their call sites. - Add _check_kernelforge_ready(): probes kernelforge.cli + kernelforge.fusion (die on absence, downgraded to a warning under --check-only) and openai_codex (warn only). Both the wheel and the editable install branch call it. - The wheel branch now names openai-codex>=0.144 / click>=8.0 / anthropic>=0.40 explicitly. - ensure_forge_gemm_tune() shrinks to an import probe; it no longer runs a sub-install. - ensure_rocprof_compute() used to gate on the KernelForge checkout, so with the checkout gone it would have skipped forever. It is unconditional and fail-soft now, and gains a Step 0 that installs the [forge-profiling] extra -- those profiler dependencies had never been installed on any pod. The old comment claiming the KernelForge root install brought them in was simply wrong: they lived in an extra that install never requested. local_setup.sh - Remove the whole mechanism for cloning the private KernelForge repo (KERNEL_FORGE_REPO / run / ensure_git_available / clone_or_update / resolve_forge). - Keep the script itself: the local-setup.env.sh it generates is still sourced by the Dockerfile. - $FORGE_PATH drops to a dev override, validated and forwarded only when it is already set. quick-start/Dockerfile - Drop the SSH mount instructions that existed for the KernelForge clone. Tests - test_install_kernel_agents_idempotent.py -> test_install_kernelforge_ready.py, rewritten around _check_kernelforge_ready's behaviour and its static guardrails. - test_install_rocprof_compute.py: the gate assertion becomes "unconditional", three cases cover the forge-profiling extra, and the pip probe records call lines so the two pip invocations can be told apart. - test_forge_codex_provider.py: assert the codex runtime rather than an extra that no longer exists. - test_rename_completeness.py: drop the three TEMPORARY allowlist entries P8 leaves behind. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… gates
Ported
- forge-kernel-bench.yml, the one genuinely new workflow (manual 24h GPU
regression), and its driver .github/scripts/forge_ci.py. The prepare job
goes back to ubuntu-latest: upstream pinned a self-hosted runner because
that organisation has an IP allow list and Hyperloom does not. forge-run
still needs project1 network access, so it keeps its self-hosted labels.
forge_ci.py only talks to the Kernel Arena control-plane API and contains
none of the renamed modules, so it moves in verbatim and is then reflowed
by this repository's ruff format.
- pre-merge.yml is not ported wholesale -- its ruff job is a subset of
lint.yml. Two things are worth taking:
(a) the exact ruff pin. lint.yml installed a floating "ruff>=0.8,<1" while
both check and format --check are hard gates, so a single ruff release
can turn main red with no code change. Pinned to 0.16.2, the version
this tree was actually formatted with -- not upstream's 0.15.17, which
disagrees with the P3 reflow that has already landed.
(b) a new compile job running compileall. ruff parses with its own
frontend, so code CPython rejects can lint clean, and kernelforge is
exactly the tree that arrived through a bulk rename.
Not ported: codeql / docs / reuse-lint (REUSE.toml already covers **) /
secret-scan / tests-coverage / ci-e2e -- Hyperloom has an equivalent or
stronger version of each. lint.yml's editable install needs no new entries
either; it is already a single pip install -e ".[test]".
Fix 1: COVERAGE_RELAX_FAIL_UNDER had never done anything
Both scripts in tests-coverage.yml read $COVERAGE_RELAX_FAIL_UNDER, but no
step mapped vars.* into the step env, so the switch had never taken effect
and fail_under was being enforced unconditionally. Both places get the
mapping. Unset means empty, i.e. the strict default.
Fix 2: the test trees were being shipped inside the wheel
setuptools defaults include-package-data = true for pyproject config, so
build_py sweeps every file under a package directory -- including
subdirectories that are not packages. packages.find.exclude kept *.tests
out of the package list while the sweep put the same files back as package
data: 627 test entries in the wheel before vendoring, 833 after (all of
kernelforge/tests/ and kernelforge/gemm_tune/tests/). This is
packaging.yml's wheel-contents job, and it was already red on main. Turning
the sweep off explicitly makes [tool.setuptools.package-data] the sole
source, which is the state both packaging checks already assume.
check_wheel_contents.py passes afterwards, and the four kernelforge/data
trees stay above their file-count floors.
durations cache: the suite roughly doubles while the cached DB predates every
kernelforge test, so the first few shardings guess from the mean and skew.
The first push-to-main after merge heals it via update-durations -- wall clock
only, correctness unaffected, not worth priming by hand. Noted in a comment.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Coverage: the merged tree measures 90.40% (98824 statements, 9487 missing), with src/hyperloom at 90.48% and src/kernelforge at 90.15% -- each side clears 90 on its own. pyproject's fail_under = 90 stands as is, no relaxation needed. The COVERAGE_RELAX_FAIL_UNDER repository variable should not be set: P9 just turned it from a no-op into a live switch, so setting it now really would disable the gate. CHANGELOG: one Unreleased entry covering the vendoring itself, $FORGE_PATH dropping from a prerequisite to a dev override, and the three pre-existing defects found and fixed on the way (the forge-profiling dependencies that had never been installed, the dead COVERAGE_RELAX_FAIL_UNDER, and the test trees shipping in the wheel). Rename completeness test: the CHANGELOG has to name the old package to tell a reader which spelling they are migrating from, so _COLLAPSE_ALLOWED gets a matching allowlist entry. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The category table was written against torch-eager naming (CUDAFunctor_add, at::native::...), where \bmul\b and \badd\b fire. AITER and vLLM fused kernels are snake_case, and "_" is a regex word character, so _act_mul_and_..., _fused_rms_fp8_..., and _..._quant_kernel matched nothing and fell through to "other". On Qwen3-14B-FP8 that buried 11.9% of GPU time and held launch_bound_share at 0.083, below the 0.10 entry floor -- so forge-fuse declared not-a-candidate on the FP8 variant of a model whose BF16 form is a measured +6.2% fusion win (276aacf6). Adding the gemm/rmsnorm/activation/cast alternations moves exactly seven kernels and no others; the share goes 0.083 -> 0.2018 and the model becomes a candidate. Rule order carries the correctness: gemm stays first so ck_tile's QuantGemmKernel is filed as GEMM before any quant rule claims it, and its "gemmkernel" alternation has no underscore so fused_moe_gemm_kernel still reaches moe. rmsnorm and activation precede cast for the same reason. Introduced in d5cbb13c and never modified since -- not a regression. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Discovery ran its read-only agent turn with read_only_resume=True, borrowed in bd3fc3fe for one half of what the flag means: tolerate a worktree the caller already left dirty. When 822b5839 lifted the workspace guard out of the Codex backend it added a read-only fast path that skips the clean-worktree requirement -- and deliberately excluded read_only_resume from it, correctly, because a real resume has state to roll back to. Discovery is not a resume. Claiming the flag opted it out of the fast path, which then ran "git rev-parse --show-toplevel" against cwd. cwd is the framework repo root, and _framework_repo_root is designed to fall back to the package install root -- so against a pip-installed vLLM every LLM discovery died with WorkspaceSafetyError. allow_dirty_baseline gives exactly the half that was wanted: _guards_dirty_ baseline() returns read_only_resume or allow_dirty_baseline, so the dirty-worktree behaviour is unchanged. The real protection was never the flag anyway -- discovery snapshots the tree itself and raises DiscoverySafetyError on any byte the agent changed. Verified against /usr/local/lib/python3.12/dist-packages with no --framework-root: 0 safety errors where all 5 attempts previously failed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
target_files is the caller's per-turn allowlist, but PROTECTED_GLOBS still matched entries by name, and neither the protected-inventory diff nor the protected-untracked check subtracted the targets. So a caller could declare a path and have the guard reject the agent for writing it. That is exactly the harness-author turn. Its sole deliverable is one .forge_fusion/kernel_harness_<digest>.py, declared as its only target by _author_baseline_harness, and the shadow repo keeps the staging directory Git-ignored. The agent wrote the harness, self-verified it in baseline mode, created nothing else -- and the guard killed the run with "protected ignored files changed", rolling the file back out. forge-fuse --author could not produce a harness at all, so no candidate could ever be scored. Explicit protection still wins: protected_paths and the driver are never exempted, so a caller cannot launder a protected path by also naming it a target, and the implementer turn -- whose targets are framework sources -- still may not touch the harness. Rollback is untouched; every exempted path is covered by _restore_target_snapshots. This restores the layering author.py already documents: the backend keeps its built-in measurement protections, the outer transaction owns the exact allowlist. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ten run_example.sh scripts and one config.yaml still invoked the deprecated kernel-agents console alias and told the reader to "pip install -e /path/to/KernelForge" -- a repository that is being archived read-only. When the alias is dropped every example breaks. They survived the rename because the completeness test exempted src/kernelforge/data/* on the grounds that the knowledge base is prose describing historical campaigns, which rewriting would falsify. That is true of the markdown and false of the shell scripts, and the glob did not tell them apart. Narrowing the exemption to *.md puts every runnable file under the guard, which now fails if the old spelling comes back. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Validating the previous commit against a second trace -- GLM-5.2-MXFP4, a MoE MXFP4 model on a different serving framework -- showed it was calibrated on one model and did not generalize. Its "_quant_kernel" alternation claimed _batched_gemm_a8w8_..._quant_kernel, 3.7% of that trace's GPU time, for "cast". cast is launch-bound, so the fix for a share that was too low introduced a share that was too high. The cause is the same word boundary, now on the other side: the pre-existing \bgemm\b matched none of _batched_gemm_a8w8_..., aiter::bf16gemm_..., or _gluon_deepgemm_..., all of which are GEMMs and all of which sat in "other". A bare "gemm" subsumes them along with the hgemm/sgemm/f16_gemm/quantgemm/ gemmkernel alternations it replaces, and reaching the gemm rule first keeps the quant rules from claiming a GEMM. That makes gemm overlap moe, whose kernels are GEMMs the table reports separately, so moe now precedes it -- which preserves every current moe result rather than changing any. Qwen3-14B-FP8 is unchanged (launch_bound_share 0.2018). On GLM the misfiled GEMMs leave the launch-bound numerator, where they never belonged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…nels Revert this commit alone to keep the launch-bound share exactly as it was. On a GLM-5.2-MXFP4 trace 76.7% of GPU time landed in "other", most of it real work the table simply had no rule for: 52.7% MLA attention (aiter::mla_pfl_*, aiter::mla_a16w16_*, kn_mla_reduce_*) and a family of MoE kernels the fused_moe/moe_align/moe_sum alternations miss (mfma_moe1/moe2_*, moe_reduction_*, grouped_topk_*). Adding mla_/_mla to attention and those forms to moe takes "other" to 12.7%. Two of the moved kernels were being counted as fusion headroom, and this is the part with a consequence: mfma_moe1_silu_mul_* 6.1% activation -> moe fuse_qk_rope_concat_and_cache_mla_* 0.7% copy -> attention Both are already-fused kernels -- a matrix-core MoE GEMM that fuses SiLU, and a kernel whose name says it fuses rope, concat and cache. Neither is an unfused launch-bound op, so counting them as fusible overstated the headroom. GLM's launch_bound_share goes 0.1464 -> 0.0751, which crosses the 0.10 floor and turns that model from candidate to non-candidate. That matches the trace: 52.7% MLA plus 9.1% nccl plus MoE GEMMs leaves little launch-bound work to fuse. The moe additions are deliberately narrow rather than a bare "moe": a kernel that merely mentions MoE while doing quantization stays a fusion candidate instead of vanishing into the MoE bucket. Qwen3-14B-FP8 is unchanged (launch_bound_share 0.2018). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… dir
The gate inferred its workspace root from the driver script's parent
directory. That is right only when the driver sits inside the tree being
measured. forge-fuse writes its driver into the run's --output-dir, so
the gate's root became the output dir -- which is not a repository.
Two consequences, both silent:
* ``git diff HEAD -- .`` ran outside any repo, where git falls back to
its implicit ``--no-index`` mode, reads ``HEAD`` as a filename and
exits 1 with ``error: Could not access 'HEAD'``. The stop-time
candidate fingerprint raised GitError on every single session:
``[in-session-gate] gate error (...) -> allow stop, outer loop will
reject candidate``. Fail-soft, so nothing broke -- the gate simply
never ran, and every candidate fell through to outer validation.
* ``protected_path_inventory`` scanned the output dir, which holds none
of the protected files, and agent-reported relative paths resolved
against the output dir rather than the agent's actual cwd. The
in-session tamper check was watching the wrong tree.
The caller already has the authoritative value -- ``config.workspace``,
the same path it makes the agent's cwd -- so pass it in and prefer it.
The driver/kernel inference stays as the fallback, unchanged for every
task that keeps its driver inside the tree.
Pre-existing, not introduced by the vendoring: identical in upstream
KernelForge at 85264fc6.
A fusion is two edits: the fused-kernel module, and the wiring edit that puts it on the framework's forward path. Everything downstream measured only the first. The harness imports the fused entry point and times it against its own eager reference, so a 37x microbench is fully explained by a module nothing calls; the serving smoke boots the framework and sends real decodes, which succeed exactly as they did before, because the unwired kernel never runs. Both reported success for zero end-to-end gain -- the failure this module already names elsewhere as "a PASS reported for a kernel that was never loaded, which is worse than a failure". A real Qwen3-14B-FP8 run delivered exactly that: 37.16x, SNR 52.1 dB, SERVING SMOKE OK, and a framework edit that was a single ``# noqa: F401`` import. fused_symbol_invocation_evidence() is the missing check. It is static on purpose: an import bound by a name that appears nowhere else in the file cannot execute, whatever the runtime does. Everything else fails OPEN -- an unreadable or unparseable source, and equally a source importing no fused module at all, which is what an INLINE fusion legitimately looks like. The gate catches one provable defect, not every KEEP it cannot inspect. It runs in apply_serving_gate after the salvage patch is exported and before the smoke, mirroring the existing blames_kernel demotion, and records a LESSON telling the next author to replace the original call site rather than only add the module.
A fusion is delivered by REPLACING one call site in the source file the
discovery prompt embedded. A proposal claiming ops that file never
performs has no wireable call site there, so the campaign spent
authoring it ends in an orphan module -- the defect the wiring gate
catches at the far end of the pipeline, after the cost is paid.
A real Qwen3-14B-FP8 run proposed four recipes against vLLM's qwen3.py
and two of them crossed a boundary:
qknorm_rope_kvcache folds in the KV-cache write, which vLLM v1
performs inside the attention backend --
key_cache / slot_mapping are not names
Qwen3Attention.forward can reach
reduce_act_mul_fp8_quant fuses the MLP activation chain, which lives
in qwen2.py (qwen3.py only imports Qwen2MLP)
The first won the run: 37.16x microbench, SNR 52.1 dB, SERVING SMOKE OK,
and a framework edit that was one # noqa: F401 import. Their source
anchors were all present in the file, so an anchor-presence check passes
both; what separates them is the ops they claim from the ops the file
performs.
Two layers:
* locate.out_of_scope_terms() judges each declared op and trait against
the shown source. The marker table is deliberately partial -- a term
earns an entry only when it has an unambiguous source-level spelling,
so add / mul / copy / reduce are absent and can never trigger a drop.
Unreadable source and unlisted terms both fail OPEN.
* the discovery prompt now states the scope rule as the hardest
constraint, tells the model to name the call site it would replace and
check every input is in scope there, and requires two fusible modules
to be proposed as two self-contained entries rather than one spanning
both.
Replayed against the real run's proposals, the two cross-boundary
recipes drop and the two whose chains genuinely live in qwen3.py
survive.
"Fellow" was a colleague's coinage for the component that builds a kernel;
it says nothing about what the thing does. The component IS the kernel's
backend -- triton, ck, hipblaslt, aiter -- so name it that.
The rename is pure vocabulary, which is exactly the kind that leaves half
-renamed strings behind: a module path inside a string, a kwargs name, a
JSON key one side of a persistence boundary writes and the other no longer
reads. None of those raise at import. So the sweep is backed by a grep
test (test_no_stray_fellow_references) that asserts every surviving
occurrence is one we chose to keep, with a stated reason.
The canonical value flips from "<backend>-fellow" to the bare backend key,
but the old spelling is still ACCEPTED everywhere it can arrive from
outside this commit -- a stored KB row, a campaign config paused before
the rename, an operator's exported FORGE_FELLOW. Those shims are marked
`# rename: keep-literal` so the grep test lets them through and a future
sweep does not "finish the job" by deleting them. CampaignConfig.from_dict
migrates the old key rather than reading both: its unknown-field check is
strict, so an unmigrated {"fellow": ...} would not have fallen back
quietly, it would have refused to load and stranded the run.
Notable non-mechanical parts:
- src/kernelforge/fellows -> src/kernelforge/kernel_backends (git mv),
plus the four test files named after it.
- The nine backend prompts introduce themselves by name, so the rename
reaches prompt TEXT ("You are the CK kernel backend --"). The sha256
snapshot table is re-generated; the rendered prompts were diffed line
by line and the identity line is the only change.
- FORGE_KERNEL_BACKEND replaces FORGE_FELLOW, which is still read and
warns once that it is deprecated.
- kernel_backends/base.py stripped a "-kernel_backend" suffix after the
machine pass -- a suffix that never exists -- which would have made
every backend name fail to resolve. Rewritten to the bare-key form.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The rename's mechanical pass turned "could not infer kernel fellow" into "could not infer kernel kernel_backend". It surfaced the way these always do -- in a real run's terminal error, where the reader is already trying to work out what went wrong and the message is the only thing they have. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…, 720 -> 213 docs
Replaces the vendored src/kernelforge/data/local_knowledge tree with the
cleaned-up version from KernelForge's docs/local-knowledge-cleanup branch. The
copy here was byte-for-byte the pre-cleanup baseline (720 .md files, exactly
matching KernelForge at its branch point), so this is a straight catch-up rather
than a merge.
What changed upstream, and why
Duplication. The per-operator cards under languages/{triton,ck,hip,asm,flydsl}/
were the same operator-level content copied 3-5 times over -- overview, tuning,
numerics and fusion do not change with the authoring language. 360 files, none
below 0.6 similarity against their siblings.
Staleness. framework/aiter/operators/ went next, on a different argument: which
backend is fastest, what the config knobs are, which env var gates which path --
all of it turns over every aiter/vLLM/SGLang release, and a card one release
behind routes an agent to an entry point that no longer exists. Operator entry
points now come from framework/aiter/overall/operator_catalog.md and the source.
Filename collisions. Cards that shared a name with an upstream knowledge base
were renamed under folder-scoped prefixes (lever_, measure_, mi350_, asm_, ck_,
triton_, hip_, flydsl_, aiter_) so neither set can be confused with, or silently
overwritten by, the other.
Prose. Every renamed card was rewritten rather than reflowed, and corrected from
CDNA3 to gfx950 along the way: 256 CU (not 304), 160 KiB LDS across 64 banks
(not 64 KiB / 32), OCP fp8 (not FNUZ), TF32 removed, the current MFMA shape
family, 128 b/lane direct-to-LDS.
hardware/ was flattened from shared/ + cdna4_mi350/ into nine self-contained
mi350_*.md topic cards, gfx950 only.
Caller updates in this repo
The docs are referenced by name from Python, so the swap does not stand alone:
- orchestrator/analysis.py, orchestrator/agent.py -- PROFILING_METHODOLOGY_FILES
and the prompt text now name measure_{protocol,triage,roofline,rocpc_workflow}.md
- kernel_backends/prompt_utils.py, {ck,hip,triton}/prompts.py -- lever_* and
hip_* card names
- knowledge/local_index.py -- the relative-path examples in the KB preamble cited
`shared/` and `operators/<op>/`, neither of which is a common shape any more;
now `overall/` and `skills/optimize/`
- tests/test_analysis_agent.py, tests/test_kernel_backend_prompt_contract.py --
the card names they assert on
- data/examples/mori_ep_dispatch_combine/driver.py -- comments pointed at the
deleted aiter moe_dispatch_combine cards; repointed at framework/mori/
- scripts/check_wheel_contents.py -- the local_knowledge floor was 700, which the
213-doc tree cannot meet. Lowered to 200 with the reasoning inline; the floor
guards against a wiped tree, and 200 still does that against 251 files.
pyproject.toml needs no change: the kernelforge package-data glob is
`data/**/*`, deliberately type- and shape-agnostic.
Verification
- every card name referenced from Python or config resolves in the new tree
- cross-checked the other direction too: of the 67 card names that existed before
and do not now, none is still referenced from code. The five apparent hits are
substring matches inside their own replacements (cheap_sweeps.md inside
lever_cheap_sweeps.md) or unrelated files (shared_pitfalls.md in the framework
agent's KB)
- languages/gluon/ and languages/fusion/ keep their operators/ trees and are
untouched; framework/mori/operators/ likewise
- relative-link check across the tree: zero broken links
Not verified: pytest was not run. There is no Python interpreter on this machine
(only the Microsoft Store shim), so the test edits above are unexecuted. CI is
the first thing that will actually run them.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…d finish the local_knowledge sync
Three things that only make sense together: the knowledge_base removal, and the
two pieces of fallout from the local_knowledge sync in the parent commit that
the test suite surfaced once it could finally run.
1. Removing data/knowledge_base
An audit for readers came back empty. `Config.knowledge_dir` resolved the tree
through `resource_path("knowledge_base")` and was assigned in `__post_init__`,
but no caller anywhere consumed the result -- the only other mention in the
codebase was a docstring in learning/auto_evolve.py explaining why the writers
deliberately do *not* use it. Nothing granted the tree to an agent sandbox (the
single `assert_sandbox_grant` call site passes `local_knowledge_dir`), no prompt
or knowledge map pointed at it, and CI never referenced it.
The name collides with something that IS live, which is probably how it survived:
`resources.writable_knowledge_root()` returns
`default_project_root() / "knowledge_base"` -- a different directory, next to the
user's experiments, where postmortem lessons and the tuning DB are written. That
one is untouched.
Gone: 118 files, 3.5 MB -- 78 .md cards under aiter/ck/flydsl/hip/hipblaslt/
sglang/shared/triton/vllm, 36 skill JSONs, source_registry.json, and a tuning_db/
holding golden_configs.json, transfer_rules.json and tuning_entries.jsonl. None
had been touched since "P2: rename kernel_agents -> kernelforge" vendored the
tree in; it is recoverable from that history if a reader ever appears.
`Config.knowledge_dir` went with it rather than being left dangling. Deleting the
tree alone broke *every* Config construction, because `resource_path` raises
FileNotFoundError rather than returning a missing path -- deliberately, so an
absent data tree fails loudly instead of silently yielding an empty knowledge
base. The field was dead and nothing constructs Config with `knowledge_dir=`, so
removing it beats papering over the raise with `missing_ok=True`.
Guards and docs that had to move with it: tests/test_packaged_resources.py
dropped its `knowledge_base/shared` assertion, scripts/check_wheel_contents.py
dropped its 100-file floor, and the two docstrings that explained the
packaged-vs-writable distinction (resources.writable_knowledge_root,
learning/auto_evolve.from_config) were rewritten. pyproject.toml needs no change
-- the package-data glob is `data/**/*`.
2. Re-snapshotting the forge-loop prompt hashes
All nine moved after the card renames in the parent commit, not just the three
backends whose prompts.py changed. The two cards every backend is pointed at --
lever_edit_surface.md and lever_cheap_sweeps.md -- are named in the shared
prompt_utils.py preamble, so renaming them reaches every rendered prompt.
Followed the re-snapshot discipline the hash table documents: rendered all nine
prompts at 8541042 (pre-change) and confirmed they reproduced the old hashes
exactly, rendered again at HEAD, then diffed line by line. 23 changed lines
across nine backends, every one a card name:
cheap_sweeps.md -> lever_cheap_sweeps.md (all 9)
edit_surface_reach.md -> lever_edit_surface.md (all 9)
occupancy_and_registers.md -> lever_occupancy.md (ck, hip)
lds_and_bank_conflicts.md -> lever_lds_banks.md (ck)
lds_async.md, patterns.md -> hip_lds_staging.md, hip_templates.md (hip)
Nothing else moved.
3. The package name in two vendored cards
test_rename_completeness caught `kernel_agents`, the pre-rename package, still
named in two cards. Upstream KernelForge still calls the package that, so the
text is correct there and wrong here; copying the tree wholesale reintroduced a
fix the KernelForge -> Hyperloom merge had already applied once.
- common_methodology/optimization/lever_cheap_sweeps.md: the sweep command is
meant to be run verbatim, and `python3 -m kernel_agents.mcp_server.tools.bench`
does not resolve here.
- framework/mori/operators/ep_dispatch_combine/tuning.md: the two files it cites
for knowledge injection are now src/kernelforge/knowledge/local_index.py and
src/kernelforge/kernel_backends/base.py.
Worth flagging for the next sync: this divergence is structural, not a one-off.
Any future wholesale copy of KernelForge's local_knowledge reintroduces it, and
test_rename_completeness is what catches it.
Verified on a GPU host: 3603 passed, 0 failed across src/kernelforge/tests/.
The four failures under src/hyperloom/agents/kernel/tests/ are pre-existing --
they reproduce identically at 8541042.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Syncs the same two index fixes made upstream in KernelForge. An audit of all 14 INDEX / README / LEVERS files against the tree as it now stands, checking both directions: every filename an index names must exist, and every subdirectory on disk must be described by its index. - languages/gluon/INDEX.md cross-linked `.../isa_verify.md` for the AMDGCN_ENABLE_DUMP workflow. That card is `triton_isa_check.md` now. The link checker never caught it because the reference is prose with a `...` prefix rather than a resolvable relative path. - framework/mori/INDEX.md was built around a comparison with `framework/aiter/.../backends/mori.md` -- a section heading, the argument under it, and a folder-tree annotation all referred to a card that no longer exists. Rewritten to stand on its own, and to send "how does aiter call mori" to `aiter/dist/device_communicators/all2all.py`. Clean elsewhere: no stale document counts, no index claiming CDNA3/gfx942 scope, every subdirectory described, zero broken relative links tree-wide. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
_prepare_worktree's docstring promised it returns None when source_file
is not tracked, but it only checked that the repo has a .git and that the
path lives inside it. A git repo that indexes just one subtree of a
framework install therefore passed both checks: `git worktree add`
succeeded and produced a checkout with no copy of the kernel, and the
failure only surfaced downstream as
_WorktreePreparationError: prepared kernel does not exist:
.../worktree/aiter/ops/gemm_op_a8w8.py
which aborts the whole backend attempt instead of falling back. Add the
`git ls-files --error-unmatch` check the docstring already described, so
an untracked anchor returns None and the caller uses the no-git scratch
path that copies the file in.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The fellow rename spells the concept two ways on purpose: "kernel
backend" in prose, kernel_backend in code. Applied by machine, it also
reached twelve sites that were already named kernel_backend and had
nothing to do with fellows, and rewrote them to the prose form -- inside
string literals, where it is not an identifier but a key:
forge_submit.py playbook.get(...) against vendor_operator_playbooks.json
_bypass_trace_reader.py the cpu_op args key, alongside kernel_file
_trace_shape_manifest.py the launch record's backend / library fields
instrument.py a breakdown strategy_group label, whose siblings
are kernel_optimizer / gemm_engine / specialist
None of these raise. The playbook lookup misses and falls back to
"aiter" forever; the trace reader hands every op an empty backend. Three
tests were rewritten in lockstep and kept passing; only the mori
playbook test, which asserts against a stub's captured kwargs, caught it.
Restore the underscore at all twelve, and add a rename-completeness
guard: a quoted two-word spelling is always an identifier that lost it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
P7b removed the last consumer: forge_submit no longer puts $FORGE_PATH on sys.path, and env_safety's allowlist carries KERNELFORGE_* instead, so the variable is not even forwarded to subprocesses any more. What local_setup.sh still did -- validate the directory, die on a stale one, and write it into the generated env file -- therefore protected nothing and propagated a pointer with no reader. Its comment still claimed a stale value would shadow the packaged kernelforge, which stopped being true with that same commit. $KERNELFORGE_PROJECT_ROOT is the dev override that replaced it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The rename to kernel_backend shipped with shims that kept accepting the old spelling: a "-fellow" suffix stripped off backend names, a "fellow" key migrated in campaign configs, a FORGE_FELLOW env fallback. Nothing in-tree produces those any more and no stored artifact this branch can still read carries them, so the shims only widen what the parser accepts. A retired key is now a hard error at config load instead of a silent migration, which is what an operator with a stale config wants to see. The rename-completeness guard loses the corresponding exemptions -- including the blanket "rename: keep-literal" escape hatch -- so the only remaining places allowed to name the old word are records that would be falsified by rewriting them: the KB narratives under data/ and CHANGELOG.md.
…ry test tree A conftest reaches its own directory and below, and this one sat in `tests/`. forge has a second test tree under `gemm_tune/tests/` -- 39 files -- which was therefore running with none of the three guardrails the conftest exists to provide: the fixture that fails any test writing under the installed package, the isolated KERNELFORGE_PROJECT_ROOT, and the child-process PYTHONPATH that lets a subprocess import kernelforge from the source tree. Those are the kind of guarantee that is worthless when partially applied. A forwarding conftest per directory would work today and drift tomorrow; the package root is the one location that covers both trees, and any tree added next to them later, without a copy. REPO_ROOT already walked up looking for a marker rather than counting parents, so the move needs no arithmetic. The six tests that imported it relatively now import `kernelforge.conftest`. It ships in the wheel as a consequence -- a few KB pytest imports during collection and nothing imports at runtime, which the packaging lint treats as an ordinary module.
…ty files REUSE.toml declared `path = "**"` as AMD / MIT with `precedence = "aggregate"`, which *adds* that notice to whatever a file already carries rather than deferring to it. For AMD-authored files that is right. For the third-party content the snapshot brought in it is a copyright claim over someone else's work, and it ships in the wheel via `data/**/*`. Two sets need their own entry, both with `precedence = "override"` so the real licence is the only one REUSE reports: - four FlyDSL reference kernels under `data/`, Apache-2.0 / FlyDSL Project Contributors, carried so the agent can read working examples of the language; - the SGLang serving patches, whose added lines are AMD's but whose diff context and paths are SGLang's, making the patch a derivative work of an Apache-2.0 project -- `Apache-2.0 AND MIT`. LICENSES/Apache-2.0.txt comes with them. `reuse lint` had been failing on "Missing licenses: Apache-2.0" since the snapshot landed, because aggregate was already surfacing Apache-2.0 out of the file headers with no licence text to match; it is compliant now, 2046/2046 files. This states what is true in the tree. Whether shipping these files in the wheel at all is the right call is a compliance question, not a metadata one, and still wants a look before release.
xiaofei-zheng
force-pushed
the
feature/xiaofei/inline-kernelforge
branch
from
August 29, 2026 07:37
54c0770 to
0903721
Compare
There was a problem hiding this comment.
CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
… knowledge layer The intellikit backend was unreachable from Hyperloom. `infer_kernel_backend` has no arm for it, `forge_submit.py` only ever passes triton/flydsl/ck/aiter, and `grep -rn intellikit src/hyperloom/` is empty -- only an explicit `--kernel-backend intellikit` or `FORGE_KERNEL_BACKEND=intellikit` could select it. Its author confirms it is no longer needed. Backends go 9 -> 8. Removed: - `kernel_backends/intellikit/` (3 files) and its registration in `kernel_backends/constants.py`. - `data/local_knowledge/languages/asm/` (117 files). `_BACKEND_LANGUAGE_DIRS` mapped intellikit -- and only intellikit -- to that folder, so it is orphaned by the backend's removal. It held a vendored copy of `ROCm/intellikit-asm-skills` (77 AMDGCN skill docs, no LICENSE shipped, zero SPDX headers) plus 30 CDNA4 ISA text extracts. - `tests/test_intellikit_backend.py` and the intellikit cases in five other test files. Follow-on edits: - `aiter/prompts.py` no longer routes to `languages/asm/`; its prompt hash in `test_kernel_backend_prompt_contract.py` moves accordingly (diffed: one line changed, nothing else). - 14 dangling `languages/asm/` cross-references in the knowledge base rewritten or dropped; the `ck_frontend_tile.md` pointer reroutes to the HIP builtins doc. - `scripts/check_wheel_contents.py` lowers the `local_knowledge/` file floor 200 -> 120 (the tree is now 134 files). The floor is a "did the tree get wiped" guard, not a size assertion. - Backend lists in `docs/kernelforge/` quickstart, run-a-campaign, architecture, and the release note. Unrelated and untouched: `AMDResearch/intellikit`, the Python profiling toolkit Magpie depends on. Same name, different project; its docs under `docs/components/` stay as they are. Verified: `pytest src/kernelforge/tests` 3571 passed, 9 skipped, 8 xfailed; `ruff check` and `ruff format --check` clean.
…ropped Seven `run_example.sh` still pass `--fellow <backend>-fellow`. `cli.py` has declared `--kernel-backend` since the rename, and the `-fellow` suffix is no longer accepted at all -- but `forge-loop` is a `TolerantCommand`, so none of these fail. They run an *inferred* backend instead of the intended one, which is the worst version of this bug: the example appears to work and quietly demonstrates something else. `_FELLOW_ALLOWED` in `test_rename_completeness.py` is why the guard missed them. Its exemption globs `src/kernelforge/data/*`, so it swallowed the runnable scripts along with the knowledge-base prose it was meant to protect. Narrowed to `*.md`, matching its `kernel_agents` sibling -- whose comment warns about exactly this failure and had already been bitten by it once. The narrowed glob immediately caught an eighth site the eye had missed. Also in this sweep, all of it stale rather than merely renamed: - `kernel-agents forge-loop` -> `kernelforge forge-loop` in the mori tuning card, and `pip install -e ".[profiling]"` -> `".[forge-profiling]"` in the rocpc workflow card and `rocpc_profile.py`'s two operator-facing messages. Neither the old console alias's spelling nor the old extra name resolves against this distribution. - Prose fixes where the old vocabulary described current behaviour rather than a historical run: `aiter-fellow` as a live inference result, "that task's fellow", and a `--fellow` comment in the mori example's header.
Three documents still addressed a reader who had cloned
`AMD-AGI/KernelForge`. Vendoring made each of them wrong in a different way.
`quickstart.md` told that reader to clone the retired repo and
`pip install -e ".[dev]"`. `dev` exists in this distribution but is lint
tooling -- pre-commit, ruff, mypy, reuse -- so following the instruction
installs forge's dependencies not at all. Now installs Hyperloom with
`[forge]`, and names `forge-profiling` where it used to name `profiling`.
`run-a-campaign.md` had the same `[dev]` line.
`kernelforge-release.md` was the standalone launch note. Its scale table
("~90 Python source files, ~12,700 lines, 65 unit tests") describes a tree
roughly a quarter the size of the one that landed here, and it was published
in the Sphinx TOC saying so. Deleted, along with seven `assets/*.svg` that
nothing referenced. What was worth keeping is measurement, so the production
results move to `reference/measured-results.md` -- with the per-token-count
MoE breakdown and the Config B/C forward/backward split that only existed
inside `perf-moe.svg` and `perf-sla.svg`, now readable as tables.
`gemm_tune/HYPERLOOM_INTEGRATION.md` was an implementation plan for wiring the
tuner into `kernel_request_handlers.py`. That wiring exists; the plan outlived
it. Its one durable section -- why `--kernel-signature-log` is effectively
required, with the 0.4% dense-lookup and 27-of-27 MoE-skip measurements, and
what `--demand` does instead -- moves into `gemm_tune/README.md`, which is
where a reader of the CLI looks.
Also corrects where `knowledge_base/` lives. The packaged read-only tree was
removed when an audit found nothing read it; the surviving one is writable and
rooted at `$KERNELFORGE_PROJECT_ROOT`. The docs still sent readers to
`less knowledge_base/flydsl/pitfalls.md`, a path that resolves nowhere -- the
curated traps are `*_traps.md` under the shipped `local_knowledge`, and the
`learned/` lessons are outside the package.
`reuse lint` was already green, which is the problem: the blanket `**` annotation claims AMD/MIT over everything not explicitly overridden, so a third-party file that nobody listed passes silently. Three were unlisted. - `04-preshuffle_gemm.py` sits in the same FlyDSL examples directory as `01`/`02`/`03` and imports FlyDSL's own `tests.utils`, but arrived with no SPDX header, so the override list skipped it. Added on the same terms, with a note that the basis is provenance rather than a notice in the file. - `mxfp8_grouped_gemm.py` is SGLang's `mxfp8_moe_amd_gfx95.py`, extracted as the protected Triton oracle for a rewrite example. Its own docstring says so. Now `Apache-2.0` / SGLang, override. - `cute_layout_algebra_guide.md` cites CUTLASS's BSD-3-Clause repeatedly and there is no BSD-3-Clause under `LICENSES/`. Checked: the guide embeds no CUTLASS source, only AMD prose about the algebra, so no licence file is needed. Recorded as such, with the condition that would change it. Adds `THIRD_PARTY.md`, because `REUSE.toml` records what the licence is and cannot record why the file is in the tree at all -- which is the question a release review actually asks. The annotation comment for the SGLang patches is corrected while here: it claimed the patches are derivative works of SGLang as a whole. In the largest, 33 of 37 substantive lines are AMD's. The dual notice is right; that justification for it was not.
`ensure_rocprof_compute()` installs `${REPO_ROOT}[forge-profiling]`
unconditionally -- ~20 wheels, including the exact `kaleido==0.2.1` and
`astunparse==1.6.2` pins ROCm 7.2.x's own rocprofiler-compute
`requirements.txt` carries. That is a real cost to put on every setup, and a
reviewer asked whether it should be gated.
Opt-out rather than opt-in, because opt-in is what the previous arrangement
effectively was: gated on a `$FORGE_PATH` checkout that nothing set, so the
extra never installed and forge profiled on the thin PMC path on every pod
without saying so. That is the failure this function was written to fix; a
flag nobody knows to set would restore it.
`SKIP_FORGE_PROFILING=1` skips that one step, logs that it did, and leaves the
rest of the function alone -- the `pandas<3` pin is about rocprof-compute's CSV
converter, not about the extra. Only the exact string `1` opts out; both
properties are now asserted.
Documented in the installer env-var table.
…polation
The `run:` block built its shell script by interpolating `${{ matrix.name }}`,
`${{ matrix.benchmark_id }}` and four `${{ inputs.* }}` values directly into
the source. GitHub substitutes those before bash ever sees them, so a
benchmark name or a dispatch input containing `$(...)` or a backtick executes
as part of the script -- on a self-hosted project1 runner, which is where this
job has to run to reach the controller network.
Neither source is arbitrary today: matrix values come from the
`KA_BENCHMARK_IDS` secret and inputs need dispatch rights. Both are still the
wrong shape for a runner that is not ephemeral. Every value now reaches the
script as an environment variable and is referenced quoted; the job's display
name keeps its interpolation, which is not a shell.
Fourteen `{doc}` roles in the vendored pages kept the absolute paths they had
in the standalone repository -- `</install/quickstart>`, `</how-to/...>` and so
on. Only `index.rst` was rewritten with the `/kernelforge/` prefix when the tree
moved under `docs/kernelforge/`.
Twelve of those simply do not resolve. The other two are worse: Hyperloom has
its own `docs/conceptual/optimization-loop.md`, so `</conceptual/optimization-
loop>` in `architecture.md` and `what-is-kernelforge.md` resolves cleanly -- to
an unrelated document, with no build warning to notice it by.
Also corrects the example paths. `cd examples/triton-softmax-forge-loop` was
right in a KernelForge checkout; here the tree is `src/kernelforge/data/
examples/`, and from a wheel it is inside the installed package, which is not a
directory to run in. The Example Tasks table now says which root its paths are
relative to and how to find the same tree from an install.
The CHANGELOG said `$FORGE_PATH` "still works when an operator sets it deliberately, and is validated rather than silently ignored". That describes an intermediate revision, not what shipped: no code reads it. Every value it could hold pointed at the pre-inlining repository layout, so honouring it would have shadowed the packaged tree with an archived one, and `_server_patcher` says as much in a comment while the CHANGELOG said the opposite. The hazard is specific enough to be worth naming. `FORGE_` is on env_safety's dotenv prefix allowlist, so a stale setting is still forwarded into the run and then ignored -- an operator following the old entry gets no error, just no effect. Reclassified as BREAKING, with `KERNELFORGE_PROJECT_ROOT` named as the replacement. That variable was itself undocumented: read by `resources.py`, consumed by the serving patcher and the playbook resolver, on both env_safety allowlists, and absent from `docs/`. It now has a row in the environment-variables reference covering the writable-state role and the resource-override precedence, and `kernel-execution-path.md` and `multi-node.md` -- both of which lost their `FORGE_PATH` row and gained nothing -- say forge needs no path variable. Two more breaking changes had no entry at all: `forge-gemm-tune` disappearing as a console script and a distribution, and the `fellow` retirement. The second notes where the failure is *not* loud: `forge-loop` is a `TolerantCommand`, so `--fellow` is dropped with a warning rather than refused, which is why the seven shipped examples ran on an inferred backend and looked fine.
…gnoring it `FORGE_DISABLE_COMPILED_FELLOWS` became `FORGE_DISABLE_COMPILED_KERNEL_BACKENDS`. Deleting the old name is not enough to retire it: `FORGE_` is on env_safety's dotenv prefix allowlist, so an operator's stale value is still forwarded into the run and then read by nothing. The consequence is not a no-op. That variable switched the compiled kernel backends OFF; ignoring it switches them back ON. Someone who had deliberately restricted forge to Triton gets CK, AITER, HIP and FlyDSL again, with no message, on the first run after the upgrade. It is not honoured -- that would keep the retired vocabulary alive -- but it is now detected and warned about, once per process rather than once per kernel. The knob had no test at all before; it has four now. `_FELLOW_ALLOWED` gains two entries, for the detector and its test. Both are scoped to the literal variable name rather than `/fellow/`, so neither can grow to cover other residue in those files.
Three sites treated a missing kernelforge as a supported configuration, from when it was a separate distribution that might not be installed. It ships in this wheel now, so an ImportError is a broken install -- and the same PR left the three handling it three different ways: - `apply_verification._parse` logged at info and returned None. The run loses apply verification and looks identical to one that passed it. - `request_handlers`' MoE routing check returned bare, with no log at all. - Both now warn and name `pip install -e ".[forge]"`. `_server_patcher._resolve_serving_patches_root` had the mirror-image gap. An explicit root that resolves is logged at WARNING, deliberately -- patching SGLang from somewhere other than the shipped tree should not be discovered months later in a diff. An explicit root that does *not* resolve fell through to the packaged tree in silence, so a mistyped override looked exactly like no override, and the patches applied were not the ones asked for.
`install.sh` re-listed the pins from `[project.optional-dependencies]` so a packaged install could reinstall them. A second copy of a version list only stays correct until someone edits one of them, and the test guarding it asserted the literal `openai-codex>=0.144` string -- so it pinned the duplication rather than catching the drift, and would have gone green against a stale copy. Both are replaced by the thing they were approximating: `pip install "hyperloom-inference_optimizer[llm,forge]"`. pip resolves the extras against the already-installed distribution's own metadata, so this needs no index for the top-level package and cannot fall out of sync by construction. The test now follows that chain -- install.sh names the extra, pyproject's `llm` extra provides `openai-codex` -- instead of matching a string. `[forge]` also gains `hyperloom-inference_optimizer[llm]`. That is a hard requirement, not a convenience: forge's agent backends lazy-import `claude_agent_sdk` and `openai_codex` at the first agent turn, so a `[forge]`-only install imports cleanly and then fails minutes into a campaign. And `ensure_forge_gemm_tune` warned when the tuner would not run. It shipped in this wheel as of this PR, so an unrunnable `kernelforge gemm-tune` means the install is broken; it dies now.
Bandit ran on `src/hyperloom` and `scripts` only, which left the newly vendored tree -- the part of the codebase that shells out to rocprofv3, ninja and git -- unscanned. It is in scope now. That surfaces roughly 27 medium-and-above findings, untriaged; the job is `continue-on-error`, so this reports rather than blocks, and triage is follow-up work. `-c pyproject.toml` is required for `[tool.bandit] exclude_dirs` to take effect at all. Pylint is deliberately *not* extended, and the comment records the measurement rather than the opinion: adding kernelforge takes it from 0 findings to ~58, essentially all E1120 on click-decorated commands, with 2 in non-click production code and both the known `__dataclass_fields__` false positive. `check_wheel_contents.py` claimed `serving_patches/` needed 3 entries when the tree holds 1, so the floor was above the truth and would have failed a correct wheel. It is 1, and the file that actually has to be there is now named explicitly in `_REQUIRED_FILES` -- a count is a weak assertion about which files arrived. `_excluded_dir_names` also treated any `pkg/glob` as a shipped subpackage including globbed first segments; it now only exempts literal ones.
Ten top-level names in `src/kernelforge` had no reader anywhere in the repo -- not in production, not in prose, not in a prompt. They came across because a snapshot copy takes everything, including what the source repo had already stopped calling. The largest is `fusion/validate.py`'s coarse decode A/B: `run_ab` plus the two helpers only it used (`_run_arm`, `_bench_one_batch_cmd`) and the regex only those parsed. Its docstring described it as "kept for the legacy single-shot CLI path" -- that path is gone, and no caller replaced it. Its tests went with it; they only ever exercised the dead function, so keeping them would have been coverage of code no one can reach. `_reads_name`/`_defines_name` in the same file are a second, unrelated orphan pair. The rest are single functions: `_parse_nk_and_m_from_csv`, `compute_sglang_cudagraph_m_values` (and the 54-line `_SGLANG_CUDAGRAPH_BS` table it was the only reader of), `ramp_clocks` (and `RAMP_SECONDS`), `unsupported_required`, `_blank_key`. Found by AST-walking every module-level def/assign in `src/kernelforge` and counting identifier occurrences across every .py and .md in the tree, then re-running until the set closed -- the second pass is what surfaced the three names that only the first pass's deletions had kept alive. Also swept two empty directories the intellikit removal left behind (`kernel_backends/intellikit/tools`, the `languages/asm/` stub) and a stray `.pyc` under the deleted asm tree. Untracked, so they never reached git, but they were still on disk in every existing checkout.
Three of the entries in the rename allowlists matched no tracked line:
_ALLOWED / _COLLAPSE_ALLOWED
the two retired-name entries (forge_submit.py's
FORGE_DISABLE_COMPILED_FELLOWS detector and the test that pins it).
Those belong to the /fellow/ grep only; I had copied them into all
three lists. In the other two they can never fire -- neither file
contains kernel_agents or forge_llm/forge_gemm_tune.
_ALLOWED
("src/kernelforge/data/*.md", /kernel-agents/) -- live when it was
written, dead since the run_example.sh fix rewrote the console alias
and the local_knowledge sync dropped the prose that named it.
A dead exemption is not inert. It stays on the list and pre-approves
whatever later lands on that path and matches that regex, which is
exactly the miss these greps exist to catch.
So the removal comes with a guard: test_every_allowlist_entry_still_
exempts_something re-runs each list against its own pattern's tracked
hits and fails on any entry with zero matches. Deleting code that an
exemption covered now fails the suite until the exemption goes too.
Verified: ruff clean; 4291 passed in src/kernelforge; coverage 90.36%
against fail_under = 90.
test_ci_e2e_dispatch.py asserts against KernelForge's own .github/scripts/ci-e2e-dispatch.sh -- ssh staging, a reap step, KF_SOURCE_DIR and KF_USE_GIT. That script was deliberately left behind in P9. Hyperloom has a same-named script that predates this PR (63e8a9b, 8b3594f, 353430a) and is a different shape: HYPERLOOM_SOURCE_DIR, HL_CI_E2E, no staging. Locally the mismatch was invisible: the module-level skipif wants a POSIX toolchain including jq, and this host has no jq, so the file always skipped. CI has jq, so CI ran it against the wrong script. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
pyproject asks for anthropic>=0.40, so CI resolves 1.2.0 while this host sits on 0.120.0. Two things changed under that bound, and both were silent here: - 1.x moved to httpx2 and type-checks http_client against it, so the httpx.Client we were handing it is a TypeError at construction. It surfaced as "llm setup failed" on every discovery call. DefaultHttpxClient is exported by anthropic 0.x and 1.x and by openai, and is always the SDK's own flavour, so both provider legs now ask the SDK for its client class instead of choosing one. The fake SDK modules in the tests do the same, because discover.py no longer consults a bare httpx stub. - 1.x dropped temperature from Messages.create()'s signature, which has no **kwargs. Passing it named raised TypeError, classify_llm_error read that as transient, and discovery spent its entire retry budget on a call that could never succeed. It is still a Messages API field, so it goes in extra_body when the installed SDK will not name it. Verified against anthropic 1.2.0 + httpx2 2.12.0 in a scratch venv: the full src/kernelforge suite passes, 4291 tests. openai 3.6.0 exports DefaultHttpxClient and still names temperature, so its leg needed only the client change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
test_submit_vendor_playbook_runs_from_the_packaged_bundle_without_forge_path is the only test in the file that drives submit() far enough to resolve a gfx target, and _resolve_gpu_target() ends in rocminfo when nothing names one. So it passed on a GPU box and failed on a CI runner. What the test is about is the packaged bundle, not the hardware, so it sets GPU_TARGET and gets the same answer either way. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
script_key="a8w8_bpreshuffle" selects which aiter tuning script to run; the generic-api-key rule sees key="<long token>". Only the pull_request runs flagged it, because that event scans the whole directory while the push event scans the diff range -- so the push runs were green and the PR run was not. The allowlist is scoped to the tuner package and to a snake_case literal, so it cannot cover a real key somewhere else. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…rong shape_key() answered () for anything it could not parse, and that helped no caller: all three unpack the result into three names, so the empty tuple only moved the failure a few frames out and dropped the shape from the message. "16x1536" did not even fail there -- it returned a 2-tuple that blew up the same way. CodeQL flags it as a multiple-assignment mismatch, which it is. It now raises ValueError naming the shape. cli.py already treats that as "tier3 attempt failed; tuning continues", so no tolerance is lost, and the three # type: ignore[arg-type] that papered over the widened return type are gone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… module requires-python is >=3.10 and tomllib is stdlib only from 3.11, so the bare import I added in ec2cca0 turned into a collection ImportError on every py3.10 shard -- all four of them, plus the 3.10 coverage job. py3.11 was green throughout, which is why it read as a test failure rather than a syntax-era mistake. The repo already has the answer at test_packaging_lint.py:45: fall back to tomli, which the ``ci`` extra pins for python_version < '3.11'. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CodeQL's clear-text-storage query classifies any field whose name contains "trusted" as a secret, and this one is serialised into tier3_outcome.json -- so a bool saying "an operator signed this script off" was reported as a credential written in the clear. The taint path in the SARIF starts at ``self.trusted`` and ends at the json.dumps in gemm_tune/cli.py:222. ``operator_signed`` says the same thing without the word the heuristic keys on, and says it more precisely: what the flag records is the signature, not a trust level. ``ledger.is_trusted()`` keeps its name -- a function is not a data node, and the ledger's own vocabulary is fine. Nothing reads the JSON key: tier3_outcome.json has no consumer in this repo, so the rename covers the field, the key, and the one assertion.
CI E2E report — ✅ Succeeded
|
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.
Description: what and why
Vendors KernelForge into this repo so that forge becomes Hyperloom's built-in
kernel-opt agent. Installing Hyperloom now gives you forge — there is no
checkout to clone, no
$FORGE_PATHto export, and no second repo whose versionyou have to keep aligned by hand.
The chain this replaces was fragile.
install.shcarried a comment recording a2026-07-28 incident where
FORGE_PATHwent unexported, forge raisedModuleNotFoundError, and every kernel attempt that day silently reverted.The Dockerfile needed an SSH key mounted just to clone a private repo. That
whole class of failure is now gone:
kernelforgeis an ordinary package insidethe wheel.
What is deliberately unchanged: the orchestrator's kernel-agent dispatch path,
and forge's standalone CLI.
Landed as P0–P10, one phase per commit, each independently verifiable. Two of
them are worth calling out to a reviewer:
812a61642(P3) changes no code — 90k lines reflowed from KernelForge'sline-length 100 to Hyperloom's 120.
git diff -wis not empty here, becausereflowing joins lines rather than just re-indenting them; the check that does
hold is an AST comparison, which reports all 335 Python files semantically
identical across the commit (the 4 that differ byte-wise after
ast.parsediffer only in a docstring gaining a leading space). Skip it.
bcbe1fa74/b401b91bc/885d4376edelete a lot — the unuseddata/knowledge_basetree, alocal_knowledgesync that drops 720 docs to213 matching an upstream cleanup, and then the
intellikitkernel backendwith its
languages/asm/tree (see below). Large deletion count, nobehaviour change for any reachable path.
Notable decisions
kernelforge(top-level, not folded intohyperloom.*) — the oldkernel_agentscollided with this repo's ownhyperloom/agents/kernelkernelforgebinary, 4 subcommands.forge-gemm-tuneis gone as a separate console script;kernel-agentssurvives one release as a deprecated aliasfellow→kernel_backendkernel_backendin codeintellikitdid not come acrossSilent-failure fixes found along the way
The migration surfaced several bugs that produced no error — the reason each
one is fixed here rather than deferred:
resources.pyused to return a non-existent path instead of raising, so amissed data tree meant forge-loop running against an empty knowledge base
forever. It now raises, and the tests assert a file-count floor (
.exists()does not catch an empty directory).
site-packagesunconditionally. An autousefixture now fails any test that writes under the installed package.
_forge_loop_constant()swallowed a lookup miss and substituted a defaultwall-clock budget — a missed module rename would have meant forge running on
the wrong timeout with nobody the wiser. It warns now.
49e444804(thefellowrename) had spaced out 12kernel_backendstringkeys into
"kernel backend". None of them raise: the playbook lookup fellthrough to
"aiter"forever and the trace reader handed every op an emptybackend. Fixed in
d8cacb216, with a grep guard so it cannot recur._prepare_worktree()treated "inside a git repo" as "tracked by it". Aframework tree hosting a repo that indexes only part of itself produced a
worktree without the kernel, surfacing far downstream as "prepared kernel
does not exist". Fixed in
ced24080e.run_example.shstill passed--fellow, a flagcli.pynolonger declares.
forge-loopis aTolerantCommand, so they did not fail —they ran an inferred backend and looked like they worked. The rename guard
missed them because its exemption globbed
data/*instead ofdata/*.md,swallowing runnable scripts along with the prose it meant to protect. Fixed
in
d18bd9f22, and the narrowed glob caught an eighth site immediately.ensure_rocprof_compute()was gated on a$FORGE_PATHcheckout that nothingset, so the
forge-profilingextra never installed and forge profiled on thethin PMC path on every pod, silently. Now unconditional, with
SKIP_FORGE_PROFILING=1as the escape hatch — an opt-out rather than anopt-in, because an opt-in is what the broken gate effectively was.
FORGE_DISABLE_COMPILED_FELLOWSwas renamed, but deleting the old name doesnot retire it:
FORGE_is on env_safety's dotenv prefix allowlist, so a stalevalue is still forwarded and then read by nothing. That variable switched the
compiled kernel backends off; ignoring it switches them back on. It is now
detected and warned about once per process — refused rather than honoured, so
the retired vocabulary does not survive.
except ImportErrorsites treated a missing kernelforge as a supportedconfiguration and handled it three different ways (info log / bare return /
nothing). It ships in this wheel now, so that is a broken install: all three
warn and name the fix.
_resolve_serving_patches_root()logged an override that resolved but fellthrough to the packaged tree in silence when it did not, so a mistyped root
looked exactly like no root and the patches applied were not the ones asked
for.
check_wheel_contents.pyrequired 3 entries underserving_patches/wherethe tree holds 1 — a floor above the truth, which fails a correct wheel. The
file that must be present is now named, not counted.
Dead code the vendoring carried in
A vendored snapshot brings along whatever the upstream repo had stopped
calling. An AST pass over
src/kernelforge— count each top-level name'sidentifier occurrences across the tree, subtract its own definition — found ten
that nothing reached,
run_ab()(the coarse decode A/B, ~160 lines with_bench_one_batch_cmdand_run_arm) being the largest. The scan was re-runafter each deletion, because removing a caller exposes the next layer: three
more names surfaced in round two, none in round three. Their five tests went
with them.
The same idea applies to the rename allowlists. An exemption that matches no
tracked line is not inert — it stays on the list and pre-approves whatever later
lands on that path and matches that regex, which is exactly the miss the greps
exist to catch. Three such entries are deleted, and a new guard fails the suite
on any allowlist entry with zero matches, so deleting exempted code now forces
the exemption out too.
Coverage after both: 90.36% against
fail_under = 90.What the first CI run caught that this host could not
Six red checks, six different reasons the local run was green:
pyprojectasks foranthropic>=0.40, so CI resolves 1.2.0while this host sits on 0.120.0. 1.x moved to httpx2 and type-checks
http_clientagainst it, so thehttpx.Clientwe handed it was aTypeErrorat construction — "llm setup failed" on every discovery call. 1.x also dropped
temperaturefromMessages.create(), whose signature has no**kwargs;classify_llm_errorread the resultingTypeErroras transient, so discoveryspent its whole retry budget on a call that could never succeed. Both legs now
ask the SDK for its own
DefaultHttpxClientrather than picking an httpx, andtemperaturetravels inextra_bodywhen the installed SDK will not name it.Verified in a scratch venv on anthropic 1.2.0 + httpx2 2.12.0: 4291 passed.
openai 3.6.0 (what CI resolves) exports the same class and still names
temperature, so its leg needed only the client change.test_ci_e2e_dispatch.pyis deleted. It is KernelForge's contract test forKernelForge's
ci-e2e-dispatch.sh, which P9 deliberately did not vendor.Hyperloom's same-named script predates this PR and is a different shape. It
never showed up locally because its skipif wants
jq, which this host lacks._resolve_gpu_target(), which ends inrocminfo. Green on a GPU box, red on arunner. It names
GPU_TARGETnow — the test is about the packaged bundle.script_key="a8w8_bpreshuffle"as a generic API key. Only onpull_request, which scans the whole directory; thepushevent scans the diffrange, which is why every push run was green. Allowlisted, scoped to the tuner
package and a snake_case literal.
shape_key()returned()for a shape it could not parse, and allthree callers unpack into three names — so it only moved the failure out a few
frames and dropped the shape from the message.
"16x1536"did not even failthere. It raises now;
cli.pyalready treats that as "tier3 attempt failed;tuning continues".
gemm_tune/cli.py:222, the write oftier3_outcome.json. The SARIF taint pathstarts at
Tier3Outcome.trusted: a bool saying whether an operator hassigned a generated tuner script off. The query's name heuristic reads any field
containing "trusted" as a secret. The field is
operator_signednow, which isboth outside the heuristic and a more accurate name -- what it records is the
signature, not a trust level.
ledger.is_trusted()keeps its name; a functionis not a data node. Nothing reads the JSON key, so the rename is four lines.
test_forge_codex_provider.pyimportedtomllibbare, andrequires-pythonis>=3.10where it is not stdlib -- a collection error thattook out all four py3.10 shards and both coverage jobs. It uses the guard this
repo already has in
test_packaging_lint.py, withtomlifrom theciextra.Linked issue(s)
None.
Tests: added/updated? commands run?
Both — roughly 250 test files move in, plus new guards for each silent-failure
class above (rename completeness, resource file-count floors, the
write-to-site-packages fixture).
Docs were repaired in the same pass: 14 sphinx
{doc}cross-references indocs/kernelforge/**had lost their/kernelforge/prefix in vendoring, andtwo of them resolved silently to the wrong page because Hyperloom has its own
docs/conceptual/optimization-loop.md. Example paths now point atsrc/kernelforge/data/examples/, with a note on reaching the same tree from aninstalled wheel.
CHANGELOG.mdclaimed$FORGE_PATHstill worked — it isremoved — and now carries the three BREAKING entries below;
$KERNELFORGE_PROJECT_ROOTis documented indocs/reference/environment-variables.md.The 18 failures are pre-existing and environment-dependent, not from this
branch. Verified by running the same 7 files in a clean
origin/mainworktree: identical 18 failures. They come from this host's
.envcredentialsleaking into tests, a vllm version mismatch, and no TraceLens patch for the
installed vllm.
End-to-end on MI355X / gfx950, Qwen3-14B-FP8: all three forge paths
(gemm-tuning, fusion, kernel-rewrite) run to completion.
Breaking changes: yes/no
Yes, three — all with a clear failure mode rather than a silent one.
$FORGE_PATHis deleted. Nothing reads it. It is still forwarded —FORGE_is a dotenv prefix on env_safety's allowlist — so a stale valuereaches the child and is then ignored rather than rejected; that is why
CHANGELOG.mdcalls it out explicitly. The dev override that replaces it is$KERNELFORGE_PROJECT_ROOT, which is on the exact allowlist and documentedin
docs/reference/environment-variables.md.forge-gemm-tuneis gone as both a console script and a distribution.The tuner is now the
kernelforge.gemm_tunesubpackage of the same wheel,reached as
kernelforge gemm-tune. There is no subtree left topip installon its own.intellikitkernel backend is gone. Nothing in Hyperloom couldreach it:
infer_kernel_backendhas no arm for it, the dispatch path onlyever passes triton/flydsl/ck/aiter, and
grep -rn intellikit src/hyperloom/is empty — only an explicit
--kernel-backend intellikitselected it. Itsauthor confirms it is no longer needed. Its
languages/asm/knowledge tree(117 files: a vendored
ROCm/intellikit-asm-skillscopy with no LICENSE,plus CDNA4 ISA extracts) went with it, reachable from no other backend.
Unrelated and untouched:
AMDResearch/intellikit, the profiling toolkitMagpie depends on. Same name, different project.
fellowvocabulary is no longer accepted — not as a-fellowbackend suffix, not as a campaign-config key, not asFORGE_FELLOW. A campaign config carrying the retired key now fails loudlyat load instead of silently migrating.
Known follow-ups (deliberate, not oversights)
[tool.mypy] filesstays["src/hyperloom"]. 90k lines of unannotated codein the advisory job is noise. Bandit does now cover
src/kernelforge—it is the shell-out-heavy part of the tree, which is where bandit earns its
keep — and its ~27 medium+ findings (incl. 2 HIGH
B602infusion/validate.py) are untriaged on an advisory job. Pylint ismeasured-and-declined, not skipped: adding the tree takes it 0 → ~58, all but
2 being E1120 against click decorators, and both survivors a known
__dataclass_fields__false positive. The measurement is recorded inlint.ymlso the next person does not have to redo it.they predate this migration and do not belong in a 1047-file PR:
tracelens_skill_runner.py:1526(bogustask_group.source_pathjoin), andwhether
loop.py's early exit should collect multiple KEEPs for multi-patchfusion.
deploy/is not carried over. Every file in the vendored tree targetsthe repository this PR retires — clone URL, runner registration, a cluster
checkout path — and Hyperloom's own dispatcher only knows
kind:"hyperloom".Retargeting it is real work and belongs in its own change; the files are
recoverable from the snapshot commit.
reuse lintwas already green, which was the problem: the**annotationcovers anything nobody listed. Five FlyDSL reference kernels (Apache-2.0),
SGLang's
mxfp8_grouped_gemm.py(Apache-2.0), and the SGLang serving patches(
Apache-2.0 AND MIT) now carry overrides, andTHIRD_PARTY.mdrecords whyeach is in the tree — the question a release review actually asks, which
REUSE.tomlcannot answer. Whether they should ship in the wheel at all isstill a compliance call for a human before release.
fail_under = 90is enforced, and P9 fixed thevars.*mapping that hadsilently disabled the
COVERAGE_RELAX_FAIL_UNDERescape hatch. If coveragedips after merge, the cause is almost certainly a mistranscribed
omitglob(KernelForge had 24), not genuinely uncovered code. Fix the glob; do not set
the variable.
🤖 Generated with Claude Code