Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 3 additions & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"outputStyle": "Brief Style"
}
4 changes: 2 additions & 2 deletions .claude/skills/agentic-fuzzing/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ leader-vs-validator comparison does not catch a load failure either: it is
identical in all three modes, so the hashes agree and the case still passes — a
probe whose contract never loaded reports `✓` in 50ms. Before believing a pass, open
`genvm.log.gz` and check the run reached the contract, and decode the `hash`
artifact — `base64 -d < hash` on a failed load reads `invalid_contract
malformed_runner`.
artifact — `base64 -d < hash` on a failed load reads `invalid_contract runner
malformed`.

## Reviewing a PR for a failing test

Expand Down
3 changes: 3 additions & 0 deletions .claude/skills/commit-style/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,6 @@ submodule's linter reformat leak an unrelated file into a commit. Each repo's
per-repo pre-commit hook (git-hooks.nix, in its flake) runs on the repo you
commit in. Do not `--no-verify` (even for gitlink bumps — let the hooks run).
Full workflow (order, pushing, `--force-with-lease` after a rebase): `/submodules`.

Never include text like "bump the v0.3 gitlink". State the underlying change,
not name the file updated
2 changes: 1 addition & 1 deletion .claude/skills/rust-test-style/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: rust-test-style
description: GenVM Rust test conventions and style. Use when writing, adding, or modifying Rust tests — inline `#[cfg(test)] mod tests`, integration tests under a crate's `tests/`, helpers, assertions, and how `genvm-tool test` discovers them.
description: Always read before writing, adding, or modifying Rust tests — where to put and how to run them.
---

# Writing Rust tests in GenVM
Expand Down
8 changes: 5 additions & 3 deletions .claude/skills/spec/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@ generated pages stay the single source of truth:

- Constants: `:ref:`gvm-def-consts-value-<group>-<name>`` /
`:ref:`gvm-def-const-<name>`` from `spec/appendix/constants.rst`
(generated from `executor/codegen/data/public-abi.json`) or
(generated from `executor/codegen/data/public-abi.json`),
`internal-constants.rst` (from `internal-constants.json`) or
`constants-pending.rst` (from `public-abi-pending.json`). **Never edit these
.rst by hand** — edit the JSON and regenerate
([genvm-tool.md](../../../docs/contributing/howto/genvm-tool.md)). New
not-yet-stabilized constants go to the pending JSON.
([genvm-tool.md](../../../docs/contributing/howto/genvm-tool.md)). Constants a
contract cannot read go to the internal JSON, not-yet-stabilized ones to the
pending JSON.
- Error outcomes: `:ref:`gvm-def-str-trie-value-vm-error-...`` — every "traps
with" / "rejected with" must link the exact vm_error entry.
- Glossary terms: `:term:`sub-VM`` etc. on first use in a section.
Expand Down
11 changes: 11 additions & 0 deletions .claude/skills/test/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@ description: Runs tests for the GenVM project. Use after making code changes to

See docs/contributing/howto/testing/README.md.

Invoke the tool as:

```
nix run '.?submodules=1#genvm-tool' -- test run --filter-tag '!fuzz' ...
```

A bare `genvm-tool` from `PATH` is a nix store copy pinned at dev-shell build
time; it goes stale against the working tree and fails with import errors such
as `ModuleNotFoundError: No module named 'genvm_tool.tests.exec.process'`.
`build/genvm_tool.sh` has the same problem — it bakes the store path in.

After fixing a failure, rerun with `--filter-continue <file>` (path printed in
the failure summary) before any full rerun — see the "Fix–rerun loop" section
there.
1 change: 1 addition & 0 deletions .genvm-monorepo-root
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"v0.2"
],
"artifacts_dir": "build/test-artifacts",
"tags_registry": "tests/tags.json",
"extra_python_paths": [
"tests/runner"
]
Expand Down
135 changes: 79 additions & 56 deletions .genvm-tool.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Manager-root genvm-tool project config.
"""
Manager-root genvm-tool project config.

Loaded once by genvm-tool (``common.load_project``) before any subcommand runs;
subcommands ask it for what they need:
Expand All @@ -20,12 +21,14 @@


def tests(ctx):
"""Umbrella test suite (was the top-level .ya-test.py).
"""
Umbrella test suite (was the top-level .ya-test.py).

``ctx`` is the runner's configuration ``Context``. Collectors close over the
imports below (resolved lazily when collection runs).
"""
import json
import platform
import sys
from pathlib import Path

Expand Down Expand Up @@ -56,32 +59,21 @@ def tests(ctx):
+ '\n'
)

ctx.run_parser.add_argument(
'--fuzz-timeout',
type=int,
default=30,
help='Timeout for each fuzzing run in seconds',
)

ctx.run_parser.add_argument(
'--fuzz-update-corpus',
default=False,
action='store_true',
help='Whether to update the fuzzing corpus',
)

import genvm_tool_plugins

ctx.shared.logger.trace(
'import path', path=sys.path, plugins_path=genvm_tool_plugins.__path__
)
from genvm_tool_plugins import (
afl,
cargo,
genvm,
integration,
pytest,
)

afl.register(ctx)

def collect_rust(ctx: genvm_tool.tests.stage.collection.Context):
for t in filter(lambda x: x.name == 'Cargo.toml', ctx.shared.git_files):
ctx.shared.logger.debug('discovered Cargo.toml', path=t)
Expand All @@ -101,54 +93,47 @@ def collect_rust(ctx: genvm_tool.tests.stage.collection.Context):
name = f'{name.parent}/{name.stem}'
cargo.cargo_fuzz(
ctx,
genvm_tool.tests.test.Description(
name,
console_pool=True,
),
genvm_tool.tests.test.Description(name),
rust_root_dir=rust_root_dir,
name=fuzz_file.stem,
)

def collect_pytest(ctx: genvm_tool.tests.stage.collection.Context):
p = ctx.shared.root_dir.joinpath(
'executors', 'v0.3.x', 'runners', 'genlayer-py-std'
)
pytest.pytest(
ctx,
genvm_tool.tests.test.Description(
str(p.relative_to(ctx.shared.root_dir)),
console_pool=True,
tags=frozenset({'unit'}),
),
project_root_dir=p,
)

ci = ctx.shared.root_dir.joinpath('support', 'ci')
pytest.pytest(
ctx,
genvm_tool.tests.test.Description(
str(ci.relative_to(ctx.shared.root_dir)),
console_pool=True,
tags=frozenset({'unit'}),
),
project_root_dir=ci,
)

fuzz_files = list(p.glob('fuzz/src/*.py'))
fuzz_files.sort()
for fuzz_file in fuzz_files:
name = fuzz_file.relative_to(ctx.shared.root_dir)
name = f'{name.parent}/{name.stem}'
continue # for now let's disable it
pytest.py_fuzz(
dirs = [
ctx.shared.root_dir.joinpath('executors', 'v0.3.x', 'runners', 'genlayer-py-std'),
ctx.shared.root_dir.joinpath('support', 'tools', 'genvm-tool'),
ctx.shared.root_dir.joinpath('support', 'ci'),
]
for p in dirs:
pytest.pytest(
ctx,
genvm_tool.tests.test.Description(
name,
str(p.relative_to(ctx.shared.root_dir)),
console_pool=True,
tags=frozenset({'unit'}),
),
project_root_dir=p,
name=fuzz_file.stem,
)

# A project's flake carries python-afl and AFL++ on x86_64-linux only,
# so elsewhere the case could not start at all
if (platform.system(), platform.machine()) != ('Linux', 'x86_64'):
continue

fuzz_files = list(p.glob('fuzz/src/*.py'))
fuzz_files.sort()
for fuzz_file in fuzz_files:
name = fuzz_file.relative_to(ctx.shared.root_dir)
name = f'{name.parent}/{name.stem}'
pytest.py_fuzz(
ctx,
genvm_tool.tests.test.Description(
name,
),
project_root_dir=p,
name=fuzz_file.stem,
)

ctx.add_collector(collect_rust)
ctx.add_collector(collect_pytest)

Expand Down Expand Up @@ -196,6 +181,7 @@ def collect_integration(ctx: genvm_tool.tests.stage.collection.Context):
ci = ctx.shared.ci

manager_port = genvm.get_manager_port(ctx.configuration)
manager_semaphore = ctx.new_semaphore('manager-listener')

if no_manager:
manager_impl = genvm.ExternalManagerService(
Expand Down Expand Up @@ -225,6 +211,7 @@ def collect_integration(ctx: genvm_tool.tests.stage.collection.Context):
manager_service = ctx.new_service(
name='manager',
manager=manager_impl,
semaphores=[manager_semaphore],
)
manager_service.meta = {
'port': manager_port,
Expand All @@ -250,11 +237,47 @@ def collect_integration(ctx: genvm_tool.tests.stage.collection.Context):
webdriver_service=webdriver_service,
ci=ci,
)
integration.integration_test_directory(
ctx,
cases_dir=ctx.shared.root_dir / 'tests' / 'system' / 'cross-major' / 'cases',
reroute_to=build_info['executor_versions']['v0.3'],
save_hashes=False,
manager_service=manager_service,
modules_service=modules_service,
webdriver_service=webdriver_service,
ci=ci,
)

ctx.collect_dir('tests/system/permits', manager_service=manager_service)
ctx.collect_dir('tests/system/manager-socket')
ctx.collect_dir('tests/system/cross-major')
ctx.collect_dir('tests/system/cross-major-observability')
limited_manager_service = None
if not no_manager:
limited_manager_service = ctx.new_service(
name='manager-cross-major-small-message-cap',
manager=genvm.ManagerService(
bin_path=build_dir / 'out' / 'bin' / 'genvm-modules',
log_path=tests_output_root / 'cross-major-small-message-cap.log',
env=ctx.configuration,
ci=ci,
config={'max_message_bytes': 1024 * 1024},
),
semaphores=[manager_semaphore],
)
ctx.collect_dir(
'tests/system/manager-socket',
manager_semaphore=manager_semaphore,
build_dir=build_dir,
ci=ci,
)
ctx.collect_dir(
'tests/system/cross-major',
manager_service=manager_service,
limited_manager_service=limited_manager_service,
managed_manager=not no_manager,
)
ctx.collect_dir(
'tests/system/cross-major-observability',
manager_service=manager_service,
)

ctx.add_collector(collect_integration)

Expand Down
1 change: 1 addition & 0 deletions .github/workflows/incl_release_build_test_cell_build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ jobs:
with:
token: ${{ secrets.NIX_CACHE_PUSH_TOKEN }}
include: genvm|genlayer|(^lief)
exclude: genvm-cpython-objs

- name: Upload artifacts
uses: actions/upload-artifact@v4
Expand Down
54 changes: 54 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# GenVM manager

Before changing this repository, read the matching page in `docs/contributing/`:
a task has a how-to, a design question has an explanation

<!-- below is generated with `genvm-tool docs` -->

## Tutorial
- [first-contribution.md](docs/contributing/tutorial/first-contribution.md) — patch an executor, branch, commit, push, open the PR

## Howto
- [genvm-tool.md](docs/contributing/howto/genvm-tool.md) — the umbrella tool: man page, test and git subcommands, codegen
- [pr.md](docs/contributing/howto/pr.md) — branch model, PR action panel, `ci-safe` / `run-full-tests`, merge gates
- [setup.md](docs/contributing/howto/setup.md) — first-time clone: submodules, vendored trees, nix/direnv dev shell

## Howto/Building
- [build.md](docs/contributing/howto/building/build.md) — debug build: configure + ninja, targets, outputs, cargo quirks
- [docs.md](docs/contributing/howto/building/docs.md) — building and publishing the website, spec vs impl-spec, ADRs
- [runners.md](docs/contributing/howto/building/runners.md) — where runners come from: build on Linux, download elsewhere

## Howto/Committing
- [git-third-party.md](docs/contributing/howto/committing/git-third-party.md) — how vendored trees (wasmtime, …) are pinned and patched
- [runners.md](docs/contributing/howto/committing/runners.md) — clearing runner dev-mode and refreshing hashes before a commit
- [submodules.md](docs/contributing/howto/committing/submodules.md) — repo topology, gitlink bumps, pre-commit hooks, push order

## Howto/Docs
- [style.md](docs/contributing/howto/docs/style.md) — prose conventions for guides, specs, ADRs and commit bodies

## Howto/Extending
- [add-host-function.md](docs/contributing/howto/extending/add-host-function.md) — new executor↔host protocol method
- [add-llm-provider.md](docs/contributing/howto/extending/add-llm-provider.md) — new LLM backend in the manager
- [add-wasi-function.md](docs/contributing/howto/extending/add-wasi-function.md) — new gl_call method or raw WASI function
- [modify-runner.md](docs/contributing/howto/extending/modify-runner.md) — runner dev-mode and hash refresh
- [modify-wasmtime.md](docs/contributing/howto/extending/modify-wasmtime.md) — patching vendored wasmtime, trap plumbing
- [write-a-script.md](docs/contributing/howto/extending/write-a-script.md) — conventions for helper scripts and pre-commit hooks

## Howto/Releasing
- [release-build.md](docs/contributing/howto/releasing/release-build.md) — nix packages, platforms, release assets
- [versioning.md](docs/contributing/howto/releasing/versioning.md) — release trains, `.genvm-monorepo-root`, version tools

## Howto/Testing
- [README.md](docs/contributing/howto/testing/README.md) — `genvm-tool test`: filters, presets, continue files
- [fuzzing.md](docs/contributing/howto/testing/fuzzing.md) — AFL fuzz targets, host sysctl prep
- [integration.md](docs/contributing/howto/testing/integration.md) — jsonnet cases, tags, golden `.stdout`/`.hash` files, services
- [python.md](docs/contributing/howto/testing/python.md) — Python tests, direct pytest for genlayer-py-std
- [rust.md](docs/contributing/howto/testing/rust.md) — Rust tests: where they go, how to run them, coverage

## Explanation
- [docs-layout.md](docs/contributing/explanation/docs-layout.md) — the 4 kinds of page and where each belongs
- [executor-lines.md](docs/contributing/explanation/executor-lines.md) — why several executor lines ship side by side, and what it costs
- [fuzz.md](docs/contributing/explanation/fuzz.md) — why fuzz targets get fake entropy and no CmpLog
- [merge-model.md](docs/contributing/explanation/merge-model.md) — why merging is a maintainer panel rather than a merge queue
- [shared-submodule-cache.md](docs/contributing/explanation/shared-submodule-cache.md) — why submodules are worktrees of one cache repo, not clones
- [vendored-trees.md](docs/contributing/explanation/vendored-trees.md) — why third-party sources are patch series, not forks
Loading
Loading