chore: post repo split cleanup#4
Conversation
A wrong-platform binary shipped in a tarball was silently accepted and only surfaced as an opaque `Exec format error` at runtime. Sniff the ELF/Mach-O header and error on a clear mismatch with the target OS.
Each repo (manager + every executor submodule) now defines its own git-hooks.nix config in its flake, pinned to that repo's toolchain, replacing the bespoke genvm-tool hook engine and the per-repo support/nix/precommit tool flakes. Expose a formatter output so nix fmt runs the same generated hooks in the working tree. CI runs support/ci/pipelines/commit-hooks.sh, which runs pre-commit in-tree per repo — the executor crates reach the manager's shared crates via relative paths that only resolve in the nested checkout, so a sandboxed nix flake check cannot run their cargo-fmt. Remove the hook package, the hooks() project functions and now-dead helpers; bump the executor gitlinks.
Wrap each CI pipeline stage in GitHub Actions ::group::/::endgroup:: sections for collapsible logs (never wrapping a nix develop that re-emits its own groups, since Actions groups cannot nest). Make the top-level combining derivations (runner trees, executor/manager bundles) symlink trees into the store via cp -rsf instead of copying, keeping their store paths as runtime deps; materialize real files only at the upload tar (find -L plus tar --dereference) and symlink the built runners under build/out/runners in the rust-test flow.
GenVM PR actionsTick a box to run it (the box unticks itself when handled). Actions only run while the PR has the
Full GenVM CI runs only when |
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
CodeRabbit reads .coderabbit.yaml from the PR head branch, and declaring base_branches overrides the org default. The `v\d+\.\d+(-dev)?` pattern did not match `v0.6-dev`, so PRs to the dev branch were auto-skipped (the review on #4 was skipped, while #3 branched before the pattern landed and was reviewed). Spell the suffixes out as separate alternatives instead of an optional group, and do the same for the executors, whose dev branches were unmatched too.
get-src now emits a `changes` output: a JSON object mapping each repo (`.` for the manager, path for each executor submodule) to its base_commit, branch_commit and has_changes. A submodule has no history the manager can see, so its endpoints are the gitlinks recorded on either side of the PR. Outside a pull request base == branch and has_changes is false. Use it to lint commit messages in incl_initial: `check-commit-message` is a commit-msg-stage hook, so the CI pre-commit run (--hook-stage pre-commit) never fired it and no commit message was checked on CI at all. Every commit a PR adds is now checked, in the manager and in each executor that moved, each against its own copy of the script.
…ersal 📦 Split the release along repo lines now that the monorepo is split. The manager release keeps the three platform bundles and gains genvm-universal (the runners, which are platform-independent); it no longer carries the executors. Each active executor line is released into the executor repo instead, at its own executor-version and at the commit the manager pins as its gitlink. A line that has not moved keeps its existing release. Everything is still BUILT here: the executor is deliberately not a build entry point (the umbrella flake feeds it compiled-libs/deps/support), so the built assets are pushed across with EXECUTOR_RELEASE_TOKEN — GITHUB_TOKEN cannot write to another repo. The manager is tagged only after the executor releases land. Platforms are now named as nix names them (amd64-linux, not linux-amd64) everywhere: targets, jobs and published assets. incl_build_trg takes a list of targets rather than a primary/secondary pair, and uploads them as one artifact per job; the targets of a platform share a nix store, so building the manager and every executor line together is no more expensive than separate jobs.
Three bugs a review of the release split turned up: Per-line executor targets prefetched no dependencies at all. The registry keys deps by platform (`executor-amd64-linux`), so `executor-v0.3-amd64-linux` matched nothing and the prefetch was a silent no-op — masked only because the manager target, whose dep set happens to be identical, is built first in the same job. Match the per-line target against the platform key. A line whose release already existed was skipped without checking WHERE that release sits. An executor commit that moves the gitlink without bumping executor-version would leave the manager pinning commit X while the published executor for that version was built from commit Y. Skip only when the existing release is on the pinned commit, and hard-fail otherwise. The legacy line was published as a full release, so GitHub marked v0.2.17 "Latest" ahead of the v0.3 line. Both lines publish identically named assets, so /releases/latest/download/... served the legacy executor. Only the head line claims Latest now. Also drop `actions: write` from the reusable build workflow: a called workflow cannot exceed the caller (`actions: read`), and uploading an artifact does not need it.
…m-post-install 📥 The manager bundle no longer ships the executors, so post-install can no longer assume they are on disk. It now downloads each line the manifest lists from that line`s own release, keyed by its executor-version, and unpacks it at the install root (the tarball is already laid out as executor/<version>/...). The URL template is a manifest field, next to the runner one. A failed download is fatal only when a missing executor is (--error-on-missing-executor). --use-patchelf sets the ELF interpreter with patchelf instead of lief. lief is a heavy binary wheel pulled in for that one job: with the flag it is neither imported nor installed into the venv, since the wrapper drops it from the requirements (the venv is keyed by their hash, so the two variants get separate venvs). Default stays lief. patchelf missing from PATH is a hard error; non-ELF and static binaries are skipped, as with lief. The entry point is now bin/genvm-post-install, matching bin/genvm-manager and bin/genvm-modules.
…tlink 🔀 Merge now leaves ONE commit per merged PR in every repo. Each executor line had its commits fast-forwarded onto its dev branch, so the executor history kept every intermediate commit while the manager was squashed. Both sides are squashed now, and since a squashed line lands under a NEW sha, the manager commit is rewritten to gitlink that commit rather than the one the PR pinned, which never lands. A line that is unchanged, or already a single commit on its dev tip, is left alone and keeps its sha. Squashing collapses several authors into one, so Co-authored-by trailers are added for the other human authors in the range. No tool/AI attribution — the commit-message hook rejects it. The squashed executor commit carries a GenVM-Squashed-From trailer naming the commit whose content it holds. Without it, re-ticking Merge after a rejected manager push (executors are pushed first, so they are already on the dev branch) would find a tip that is neither an ancestor nor a descendant of the pinned sha — squashing keeps only the tree — and would block the PR as diverged, permanently. The trailer is how the second run recognises its own work, reuses that commit and pushes nothing.
The docs pipeline had been broken since the split, in three places.
generate.py read flake-config.json, which no longer exists, and passed
commitToTagStr / build-config-str to runners/docs.nix, which no longer takes
arguments — each runner now carries the version of the line it came from, so
docs.nix groups by that. It also resolved its own directory through
Path("__file__") — the literal string, not the file — so it only ever worked
from one cwd.
docs.nix assumed every runner has a `hash`. Only the forward-rolling lines do;
the legacy v0.2 line exposes just `uid`, so adding that line made the eval
fail. The hash the executor puts in the on-disk path is in `uid` either way.
The runner pages are built from git: tags, tag messages, and the log between
two runner versions. A runner version is an EXECUTOR version, tagged in the
executor repo, with its sources under that repo`s runners/ — but generate.py
asked the manager, whose history contains no executor commits and whose tags
are manager versions. It never matched, so the changelog was silently empty.
Ask the executor checkout instead. It stays empty until the executor repo has
tags, which the reworked release now creates.
Finally, the gen-docs shell needs `?submodules=1` like every other nix
invocation here, since the manager flake imports the executor lines.
A review of the squash-on-merge change surfaced several ways it could corrupt a protected branch: - The re-tick provenance lookup grepped the whole commit message, and the body is the raw PR body. A PR quoting `GenVM-Squashed-From: <sha>` (a revert quoting what it reverts) could make a squash record bogus provenance, so a later PR pinning that sha would push nothing and repoint the manager gitlink at the wrong commit. Strip that trailer from the body and normalize CRLF while there (commit-tree does no cleanup, unlike git commit, so the executor and manager messages for one PR would otherwise differ byte-for-byte). - The executor squash message referenced the manager PR as a bare `#N`, which resolves to an unrelated issue in the executor repo; qualify it as owner/repo#N. - coauthor_trailers claimed to filter AI/tool attribution but did not, so a bot commit in the range could land a Co-authored-by the commit-message hook rejects — and nothing re-validates a generated squash message. Filter it. - Nothing stopped the manager gitlink moving backward: the 0-behind gate is on commits, not the pointer, so a PR current on commits but stale on the gitlink could silently revert another PR`s executor work. Refuse a landed commit that is an ancestor of the base`s current gitlink. - A hand-merged executor PR (right tree, new sha, no trailer) used to wedge the line as diverged; recognise it by tree equality and mirror the tip. Also preserve the author date (was becoming job time), parse author identity NUL-separated (names with `<`/spaces), and correct the now-stale strategy comment in branch_merge_into_dev.yaml.
…y gate ✨ Adds `pr-branches`, a CI tool over a manager PR and every executor line it ships: - pr_branches_info: per-repo base/head sha, ahead/behind, mirror-PR url and rebased/synced/moved flags, all read through the gh API (no checkout) - subcommands show-info, provision (force-push each moved line's mirror branch to its pinned commit and open/reuse its PR) and check - queue gate check-executor-prs (every repo rebased, every executor's pinned commit present so it can land), aggregated by gate-check before the heavy jobs - a "Provision executor PRs" panel checkbox that dispatches branch_provision_executor_prs.yaml Panel and merge now run their scripts from the PR branch under a ci-safe guard, so a PR exercises its own support/ci.
3c9ee9b to
c94b698
Compare
c94b698 to
a087b99
Compare
Replace the fixed module-test-* jobs with plan-queue-matrix (emits the cell matrix, marker-gated) fanned out through a reusable test_cell.yaml; each cell is declared once as DefaultStepInfo (genvm-tool test filters + per-cell runner setup) and executed by test-cell. Supporting granularity: split ninja `all/bin` into `all/manager` + `all/executor/<v>` and add an `all/runners` phony so cells build only what they need; a `minimal` devshell for `genvm-tool configure`; tag the genlayer-py-std suite so `--filter-tag` selects it.
a087b99 to
fa9c458
Compare
ci-changes.py reads gitlink SHAs out of the superproject trees via git ls-tree and never touches a submodule object, but its fetch inherited git's default fetch.recurseSubmodules=on-demand once get-all-git.py had populated the submodules. On-demand recursion then tried to fetch every executor gitlink the PR range touches; a historical commit in the range pins an executor commit that was never pushed to its remote, so the whole fetch aborted with 'upload-pack: not our ref'. Pass --no-recurse-submodules so only the superproject refs are fetched.
check-executor-prs went red because 'gh api repos/.../pulls/4' came back with an empty body, which gh surfaces as 'unexpected end of JSON input'; manager_info cannot tolerate a miss, so that single hiccup failed the whole landability gate (and cascaded into gate-check and validate-end, skipping the test matrix). Every gh call here is an idempotent read, so wrap the gh helper in a bounded retry with linear backoff for transient signals (empty body, 5xx, rate-limit, network). A terminal 4xx — including the 404 that allow_missing callers expect for an absent executor line — is not retried, so that path stays fast, and an exhausted retry still raises exactly as before.
A Build used as an add_dependency/output reference — e.g. all/bin depending on the all/manager and all/executor/<v> phony aggregators — fell through _scalar's type switch to str(build), emitting the object's default repr ('<genvm_tool_plugins.ninja.Build object at 0x...>') as a literal ninja path, so 'ninja all/bin' failed with 'missing and no known rule to make it'. Handle Build in _scalar by referencing its primary output (enough to force a multi-output edge), so passing a Build anywhere a target reference is expected just works.
collect_rust finds Cargo.toml/fuzz targets by scanning Context.git_files, which ran a plain 'git ls-files'. Since the monorepo split each executors/<line>.x is a submodule, so plain ls-files stops at the boundary and only the two manager crates are seen — every executor rust crate (calldata and its fuzz targets included) silently went untested, and executors carry no tests() hook of their own. Recurse into submodules (uninitialized ones are skipped) to restore pre-split discovery: 2 -> 16 crates.
b74c5b0 to
cdb45db
Compare
|
/run-e2e core |
|
/run-e2e core |
655e05c to
0eda352
Compare
|
/run-e2e |
|
/run-e2e |
0eda352 to
e56aa55
Compare
|
/run-e2e |
4 similar comments
|
/run-e2e |
|
/run-e2e |
|
/run-e2e |
|
/run-e2e |
|
❌ Merge blocked: missing the |
* chore: revive release CI
* fix(test): discover executor crates by recursing submodules 🐛
collect_rust finds Cargo.toml/fuzz targets by scanning Context.git_files, which ran a plain 'git ls-files'. Since the monorepo split each executors/<line>.x is a submodule, so plain ls-files stops at the boundary and only the two manager crates are seen — every executor rust crate (calldata and its fuzz targets included) silently went untested, and executors carry no tests() hook of their own. Recurse into submodules (uninitialized ones are skipped) to restore pre-split discovery: 2 -> 16 crates.
* fix(build): resolve a Build passed as a dependency to its output 🐛
A Build used as an add_dependency/output reference — e.g. all/bin depending on the all/manager and all/executor/<v> phony aggregators — fell through _scalar's type switch to str(build), emitting the object's default repr ('<genvm_tool_plugins.ninja.Build object at 0x...>') as a literal ninja path, so 'ninja all/bin' failed with 'missing and no known rule to make it'. Handle Build in _scalar by referencing its primary output (enough to force a multi-output edge), so passing a Build anywhere a target reference is expected just works.
* fix(ci): retry transient gh reads in the executor-PR precondition 🔒
check-executor-prs went red because 'gh api repos/.../pulls/4' came back with an empty body, which gh surfaces as 'unexpected end of JSON input'; manager_info cannot tolerate a miss, so that single hiccup failed the whole landability gate (and cascaded into gate-check and validate-end, skipping the test matrix). Every gh call here is an idempotent read, so wrap the gh helper in a bounded retry with linear backoff for transient signals (empty body, 5xx, rate-limit, network). A terminal 4xx — including the 404 that allow_missing callers expect for an absent executor line — is not retried, so that path stays fast, and an exhausted retry still raises exactly as before.
* fix(ci): stop the PR-diff fetch from recursing submodules 🐛
ci-changes.py reads gitlink SHAs out of the superproject trees via git ls-tree and never touches a submodule object, but its fetch inherited git's default fetch.recurseSubmodules=on-demand once get-all-git.py had populated the submodules. On-demand recursion then tried to fetch every executor gitlink the PR range touches; a historical commit in the range pins an executor commit that was never pushed to its remote, so the whole fetch aborted with 'upload-pack: not our ref'. Pass --no-recurse-submodules so only the superproject refs are fetched.
* chore(ci): run full tests as a planned matrix of reusable cells ♻️
Replace the fixed module-test-* jobs with plan-queue-matrix (emits the cell
matrix, marker-gated) fanned out through a reusable test_cell.yaml; each cell is
declared once as DefaultStepInfo (genvm-tool test filters + per-cell runner
setup) and executed by test-cell.
Supporting granularity: split ninja `all/bin` into `all/manager` +
`all/executor/<v>` and add an `all/runners` phony so cells build only what they
need; a `minimal` devshell for `genvm-tool configure`; tag the genlayer-py-std
suite so `--filter-tag` selects it.
* feat(ci): executor mirror PR provisioning and a manager-PR landability gate ✨
Adds `pr-branches`, a CI tool over a manager PR and every executor line it ships:
- pr_branches_info: per-repo base/head sha, ahead/behind, mirror-PR url and
rebased/synced/moved flags, all read through the gh API (no checkout)
- subcommands show-info, provision (force-push each moved line's mirror branch
to its pinned commit and open/reuse its PR) and check
- queue gate check-executor-prs (every repo rebased, every executor's pinned
commit present so it can land), aggregated by gate-check before the heavy jobs
- a "Provision executor PRs" panel checkbox that dispatches
branch_provision_executor_prs.yaml
Panel and merge now run their scripts from the PR branch under a ci-safe guard,
so a PR exercises its own support/ci.
* chore: split docs, rework ci
* fix(ci): harden the merge squash against review findings 🔒
A review of the squash-on-merge change surfaced several ways it could corrupt a
protected branch:
- The re-tick provenance lookup grepped the whole commit message, and the body
is the raw PR body. A PR quoting `GenVM-Squashed-From: <sha>` (a revert quoting
what it reverts) could make a squash record bogus provenance, so a later PR
pinning that sha would push nothing and repoint the manager gitlink at the
wrong commit. Strip that trailer from the body and normalize CRLF while there
(commit-tree does no cleanup, unlike git commit, so the executor and manager
messages for one PR would otherwise differ byte-for-byte).
- The executor squash message referenced the manager PR as a bare `#N`, which
resolves to an unrelated issue in the executor repo; qualify it as owner/repo#N.
- coauthor_trailers claimed to filter AI/tool attribution but did not, so a bot
commit in the range could land a Co-authored-by the commit-message hook
rejects — and nothing re-validates a generated squash message. Filter it.
- Nothing stopped the manager gitlink moving backward: the 0-behind gate is on
commits, not the pointer, so a PR current on commits but stale on the gitlink
could silently revert another PR`s executor work. Refuse a landed commit that
is an ancestor of the base`s current gitlink.
- A hand-merged executor PR (right tree, new sha, no trailer) used to wedge the
line as diverged; recognise it by tree equality and mirror the tip.
Also preserve the author date (was becoming job time), parse author identity
NUL-separated (names with `<`/spaces), and correct the now-stale strategy
comment in branch_merge_into_dev.yaml.
* fix(docs): build the docs site again after the repo split 📚
The docs pipeline had been broken since the split, in three places.
generate.py read flake-config.json, which no longer exists, and passed
commitToTagStr / build-config-str to runners/docs.nix, which no longer takes
arguments — each runner now carries the version of the line it came from, so
docs.nix groups by that. It also resolved its own directory through
Path("__file__") — the literal string, not the file — so it only ever worked
from one cwd.
docs.nix assumed every runner has a `hash`. Only the forward-rolling lines do;
the legacy v0.2 line exposes just `uid`, so adding that line made the eval
fail. The hash the executor puts in the on-disk path is in `uid` either way.
The runner pages are built from git: tags, tag messages, and the log between
two runner versions. A runner version is an EXECUTOR version, tagged in the
executor repo, with its sources under that repo`s runners/ — but generate.py
asked the manager, whose history contains no executor commits and whose tags
are manager versions. It never matched, so the changelog was silently empty.
Ask the executor checkout instead. It stays empty until the executor repo has
tags, which the reworked release now creates.
Finally, the gen-docs shell needs `?submodules=1` like every other nix
invocation here, since the manager flake imports the executor lines.
* feat(ci): squash executor commits on merge and repoint the manager gitlink 🔀
Merge now leaves ONE commit per merged PR in every repo. Each executor line
had its commits fast-forwarded onto its dev branch, so the executor history
kept every intermediate commit while the manager was squashed. Both sides are
squashed now, and since a squashed line lands under a NEW sha, the manager
commit is rewritten to gitlink that commit rather than the one the PR pinned,
which never lands. A line that is unchanged, or already a single commit on its
dev tip, is left alone and keeps its sha.
Squashing collapses several authors into one, so Co-authored-by trailers are
added for the other human authors in the range. No tool/AI attribution — the
commit-message hook rejects it.
The squashed executor commit carries a GenVM-Squashed-From trailer naming the
commit whose content it holds. Without it, re-ticking Merge after a rejected
manager push (executors are pushed first, so they are already on the dev
branch) would find a tip that is neither an ancestor nor a descendant of the
pinned sha — squashing keeps only the tree — and would block the PR as
diverged, permanently. The trailer is how the second run recognises its own
work, reuses that commit and pushes nothing.
* feat(install): download executors, add --use-patchelf, rename to genvm-post-install 📥
The manager bundle no longer ships the executors, so post-install can no
longer assume they are on disk. It now downloads each line the manifest
lists from that line`s own release, keyed by its executor-version, and
unpacks it at the install root (the tarball is already laid out as
executor/<version>/...). The URL template is a manifest field, next to the
runner one. A failed download is fatal only when a missing executor is
(--error-on-missing-executor).
--use-patchelf sets the ELF interpreter with patchelf instead of lief. lief
is a heavy binary wheel pulled in for that one job: with the flag it is
neither imported nor installed into the venv, since the wrapper drops it from
the requirements (the venv is keyed by their hash, so the two variants get
separate venvs). Default stays lief. patchelf missing from PATH is a hard
error; non-ELF and static binaries are skipped, as with lief.
The entry point is now bin/genvm-post-install, matching bin/genvm-manager and
bin/genvm-modules.
* fix(ci): make the executor release honest about what it publishes 🔒
Three bugs a review of the release split turned up:
Per-line executor targets prefetched no dependencies at all. The registry
keys deps by platform (`executor-amd64-linux`), so `executor-v0.3-amd64-linux`
matched nothing and the prefetch was a silent no-op — masked only because the
manager target, whose dep set happens to be identical, is built first in the
same job. Match the per-line target against the platform key.
A line whose release already existed was skipped without checking WHERE that
release sits. An executor commit that moves the gitlink without bumping
executor-version would leave the manager pinning commit X while the published
executor for that version was built from commit Y. Skip only when the existing
release is on the pinned commit, and hard-fail otherwise.
The legacy line was published as a full release, so GitHub marked v0.2.17
"Latest" ahead of the v0.3 line. Both lines publish identically named assets,
so /releases/latest/download/... served the legacy executor. Only the head
line claims Latest now.
Also drop `actions: write` from the reusable build workflow: a called workflow
cannot exceed the caller (`actions: read`), and uploading an artifact does not
need it.
* ci(release): publish executors to the executor repo, runners as -universal 📦
Split the release along repo lines now that the monorepo is split. The
manager release keeps the three platform bundles and gains genvm-universal
(the runners, which are platform-independent); it no longer carries the
executors. Each active executor line is released into the executor repo
instead, at its own executor-version and at the commit the manager pins as
its gitlink. A line that has not moved keeps its existing release.
Everything is still BUILT here: the executor is deliberately not a build
entry point (the umbrella flake feeds it compiled-libs/deps/support), so the
built assets are pushed across with EXECUTOR_RELEASE_TOKEN — GITHUB_TOKEN
cannot write to another repo. The manager is tagged only after the executor
releases land.
Platforms are now named as nix names them (amd64-linux, not linux-amd64)
everywhere: targets, jobs and published assets.
incl_build_trg takes a list of targets rather than a primary/secondary pair,
and uploads them as one artifact per job; the targets of a platform share a
nix store, so building the manager and every executor line together is no
more expensive than separate jobs.
* ci(hooks): check commit messages across manager and executors 📝
get-src now emits a `changes` output: a JSON object mapping each repo (`.`
for the manager, path for each executor submodule) to its base_commit,
branch_commit and has_changes. A submodule has no history the manager can
see, so its endpoints are the gitlinks recorded on either side of the PR.
Outside a pull request base == branch and has_changes is false.
Use it to lint commit messages in incl_initial: `check-commit-message` is a
commit-msg-stage hook, so the CI pre-commit run (--hook-stage pre-commit)
never fired it and no commit message was checked on CI at all. Every commit a
PR adds is now checked, in the manager and in each executor that moved, each
against its own copy of the script.
* chore(ci): match all dev base branches in coderabbit 🐰
CodeRabbit reads .coderabbit.yaml from the PR head branch, and declaring
base_branches overrides the org default. The `v\d+\.\d+(-dev)?` pattern did
not match `v0.6-dev`, so PRs to the dev branch were auto-skipped (the review
on #4 was skipped, while #3 branched before the pattern landed and was
reviewed).
Spell the suffixes out as separate alternatives instead of an optional group,
and do the same for the executors, whose dev branches were unmatched too.
* ci(tests): emit GitHub Actions error annotations
* chore(ci): keep submodules referenced to cache 🔨
* ci(workflows): use pipeline-local artifacts
* chore(ci): cache submodule checkouts 🔨
* ci: group pipeline logs and make nix artifacts symlink trees 🔗
Wrap each CI pipeline stage in GitHub Actions ::group::/::endgroup:: sections
for collapsible logs (never wrapping a nix develop that re-emits its own
groups, since Actions groups cannot nest). Make the top-level combining
derivations (runner trees, executor/manager bundles) symlink trees into the
store via cp -rsf instead of copying, keeping their store paths as runtime
deps; materialize real files only at the upload tar (find -L plus tar
--dereference) and symlink the built runners under build/out/runners in the
rust-test flow.
* chore: replace in-house commit-hook engine with git-hooks.nix 🪝
Each repo (manager + every executor submodule) now defines its own
git-hooks.nix config in its flake, pinned to that repo's toolchain, replacing
the bespoke genvm-tool hook engine and the per-repo support/nix/precommit tool
flakes. Expose a formatter output so nix fmt runs the same generated hooks in
the working tree. CI runs support/ci/pipelines/commit-hooks.sh, which runs
pre-commit in-tree per repo — the executor crates reach the manager's shared
crates via relative paths that only resolve in the nested checkout, so a
sandboxed nix flake check cannot run their cargo-fmt. Remove the hook package,
the hooks() project functions and now-dead helpers; bump the executor gitlinks.
* fix(web): exempt webdriver connection from SSRF dns filter 🐛
* fix(post-install): reject wrong-OS executor before trusting it 🔒️
A wrong-platform binary shipped in a tarball was silently accepted and only surfaced as an opaque `Exec format error` at runtime. Sniff the ELF/Mach-O header and error on a clear mismatch with the target OS.
Dependencies
e2e fix
Depends-On: https://github.com/genlayerlabs/genlayer-e2e/pull/662
Depends-On: https://github.com/genlayerlabs/genlayer-dev-env/pull/106
Tools Manager Changeset
Depends-On: genlayerlabs/genlayer-studio#1697
Depends-On: genlayerlabs/genlayer-py#96
Depends-On: genlayerlabs/genlayer-js#192
Depends-On: genlayerlabs/genlayer-testing-suite#98