{"owner":"vlang","repo":"v","hasSkills":true,"totalSkillsCount":52,"totalTokensCount":36754,"categories":["anthropic-skill","root-instruction","plugin-manifest"],"hasMcp":true,"mcpConfig":{"mcpServers":{"v":{"command":"npx","args":["-y","@modelcontextprotocol/server-v"]}}},"found":["vlib/v/skills/v-memory/SKILL.md","vlib/v/skills/v-project/SKILL.md","vlib/v/skills/v-lang/SKILL.md","vlib/v/skills/v-concurrency/SKILL.md","vlib/v/skills/v-package/SKILL.md","vlib/v/skills/v-mcp/SKILL.md","vlib/v/skills/v-scripts/SKILL.md","vlib/v/skills/v-testing/SKILL.md","vlib/v/skills/v-tools/SKILL.md","vlib/v/skills/v-veb/SKILL.md","vlib/v/skills/v-workflow/SKILL.md","AGENTS.md","cmd/tools/README.md","cmd/tools/fast/README.md","cmd/tools/scripting/README.md","cmd/tools/testing/README.md","cmd/tools/vcreate/README.md","cmd/tools/vgenerate/README.md","cmd/tools/vmcp/README.md","cmd/tools/vpbgen/README.md","cmd/tools/vpm/README.md","cmd/tools/vskills/README.md","cmd/tools/vvet/README.md","examples/cpu_features/SSE_and_MMX_Extensions/README.md","cmd/tools/vpm/test_utils/README.md","vlib/x/async/tools/README.md","vlib/x/executor/tools/README.md","cmd/tools/vdoc/testdata/attributes/main.md","cmd/tools/vdoc/testdata/enum_helpers/main.md","cmd/tools/vdoc/testdata/output_formats/README.md","cmd/tools/vdoc/testdata/output_formats/main.md","cmd/tools/vdoc/testdata/readme_in_project_root/README.md","vlib/v/skills/v-concurrency/references/CHANNELS.md","vlib/v/skills/v-concurrency/references/PATTERNS.md","vlib/v/skills/v-lang/references/COMPTIME.md","vlib/v/skills/v-lang/references/MUTABILITY.md","vlib/v/skills/v-lang/references/OPTION-RESULT.md","vlib/v/skills/v-lang/references/SUMTYPES.md","vlib/v/skills/v-mcp/references/EDITING.md","vlib/v/skills/v-mcp/references/TOOLS.md","vlib/v/skills/v-memory/references/OWNERSHIP.md","vlib/v/skills/v-memory/references/UNSAFE.md","vlib/v/skills/v-package/references/SUBCOMMANDS.md","vlib/v/skills/v-project/references/TEMPLATES.md","vlib/v/skills/v-testing/references/COVERAGE.md","vlib/v/skills/v-testing/references/PATTERNS.md","vlib/v/skills/v-testing/references/TROUBLESHOOTING.md","vlib/v/skills/v-veb/references/MIDDLEWARE.md","vlib/v/skills/v-veb/references/ROUTES.md","vlib/v/skills/v-workflow/references/COMPILER-WORKFLOW.md","vlib/v/skills/v-workflow/references/FLAGS.md","vlib/v/skills/v-workflow/references/V-MOD.md"],"skills":{"vlib/v/skills/v-memory/SKILL.md":"---\nname: v-memory\ndescription: V's memory model - GC modes (-gc boehm, -gc none, -prealloc), the optional -ownership checking that catches use-after-move, what unsafe unlocks, and the C interop rules. Use when choosing a GC mode, when a program leaks or grows without bound, when deciding whether unsafe is worth it, when passing a V pointer to C, or when a move or borrow error appears. Does not cover general language rules (see v-lang), data structure choice (see v-lang's structs and maps section), profiling (see v-workflow), scripting a task in V (see v-scripts), or the wider command surface (see v-tools).\nlicense: MIT\n---\n\n# Memory in V\n\nThe default needs no thought: the Boehm collector traces and frees what is no\nlonger reachable. Every question below only arises when you leave that default.\n\n## Resource Routing\n\n- `references/OWNERSHIP.md` - Read when `-ownership` is in play, or when you want\n  a use-after-move caught at compile time rather than at 3am.\n- `references/UNSAFE.md` - Read before writing any `unsafe` block, and before\n  passing a V pointer into C.\n\n## Quick Reference\n\n| Situation | Mode |\n| --- | --- |\n| An ordinary program | default (Boehm GC) — do nothing |\n| A short-lived batch job, or a self-contained binary | `-gc none` |\n| A compiler or batch tool | `-prealloc` |\n| You want use-after-move caught at compile time | `-ownership` |\n| Talking to a C library | `unsafe`, and keep it in one place |\n| Tuning, before guessing | `-stats run` |\n\n**Default**: leave the GC alone. `-gc none` and `-prealloc` are not performance\nswitches; they remove the collector and move the responsibility to you.\n\n## The three modes\n\n```bash\nv run main.v                    # Boehm tracing GC\nv -gc none -prod main.v         # no collector at all\nv -prealloc -prod main.v        # arena allocation\n```\n\n| Mode | Frees | Cost |\n| --- | --- | --- |\n| Boehm (default) | unreachable memory | tracing pauses; memory is not returned promptly |\n| `-gc none` | nothing, unless you do | you must free; no cycles; no finalisers |\n| `-prealloc` | everything, at exit | only good for one-shot single-threaded work |\n\n`-gc none` means no cycle collection, so a reference cycle leaks. For a program\nthat runs once and exits — a compiler, a code generator, a batch script — that\ntrade is usually worth it.\n\n`-prealloc` is documented as suitable only for short-lived, single-threaded,\nbatch-like programs. Do not reach for it in a server.\n\n**Race builds are the exception to all of it.** `-race` does not work with `-gc\nnone` or `-prealloc`: ThreadSanitizer has to see every allocation and free, which\na collector or an arena hides. So a program you want to race-test is a program you\nmust run under the default GC. It also needs `clang` or `gcc` with the\nThreadSanitizer runtime, and prefers `clang`, because gcc does not instrument\ncopies of whole struct values such as strings and arrays, so it misses races on\nthem.\n\n## Check before you optimise\n\n```bash\nv -stats run main.v\n```\n\nThat reports allocations. `-gc none` on a program that allocates heavily and\nexits quickly can be a large win; on a long-running one it is a leak generator.\nMeasure first.\n\n## Ownership: catching a move at compile time\n\nV has an optional ownership system, off by default, enabled with `-ownership`. It\ntracks owned values and rejects using one after it has been moved:\n\n```v ignore\nfn main() {\n\ts1 := 'hello'.to_owned()\n\ts2 := s1          // s1 is moved into s2\n\tprintln(s1)       // error: use of moved value: `s1`\n}\n```\n\nStrings created with `.to_owned()` or `.clone()`, ordinary string slices, and the\n`Owned` / `Copy` / `Drop` struct markers participate. Literals and primitives are\nunaffected; arbitrary structs, maps and slices are not tracked as owned on their\nown. `doc/ownership.md` is the reference; `references/OWNERSHIP.md` here is the summary.\n\n```bash\nv -ownership file.v        # check and compile\n```\n\nIt also defines a `ownership` compile-time flag, so `$if ownership ? { ... }` and\n`*_d_ownership.v` files select the enabled behaviour.\n\n## unsafe\n\n`unsafe` exists for C interop and for a handful of low-level primitives. It is not\na way to make the compiler stop complaining.\n\nThe rule: one `unsafe` block, as small as possible, with a comment saying why it is\nsound. If a function needs two, split it so each has one.\n\n```v ignore\nimport unsafe\n\n// Fixed-size stack buffer; the size must be a compile-time constant.\npub fn (b &Buf) fill(c byte) {\n\tunsafe {\n\t\tb.data = char(c)\n\t\tb.len = b.size\n\t}\n}\n```\n\nSee `references/UNSAFE.md` for what it does not check and what that costs.\n\n## C interop\n\nV's own rule is the one to follow: avoid `C.` and `JS.` in plain `.v` files and put\nthe interop in `.c.v` or `.js.v` files. Then the compiler can tell which code is\nplatform-specific, and `-Wimpure-v` catches an interop call that escaped.\n\n```v ignore\n// buffer_windows.c.v\nmodule main\n\nfn windows_only() int {\n\tunsafe {\n\t\treturn C.GetLastError()\n\t}\n}\n```\n\nA V pointer handed to C must not be freed or moved by the caller, and the C side\nmust not retain it after the V object goes out of scope. See `references/UNSAFE.md`.\n\n## Validation\n\n```bash\nv -ownership file.v            # catch a move at compile time\nv -stats run main.v            # measure before changing the mode\nv -check main.v                # plain, portable V\n```\n\nFor library code add `-shared`. See [v-workflow](../v-workflow/SKILL.md).\n\n## Related Skills\n\n- **The language rules**: see [v-lang](../v-lang/SKILL.md) for `mut` and for when\n  to reach for a struct rather than a map, which changes how much is allocated.\n- **The build loop**: see [v-workflow](../v-workflow/SKILL.md) for `-prod`,\n  `-stats` and the other flags this skill uses.\n- **Concurrency**: see [v-concurrency](../v-concurrency/SKILL.md) for the shared\n  state whose lifetime a lock has to protect.\n","vlib/v/skills/v-project/SKILL.md":"---\nname: v-project\ndescription: Create V projects with v new or v init and choose a binary, library or web template.\nlicense: MIT\n---\n\n# Creating a V project\n\nUse this when starting a project, scaffolding a module, or choosing a template.\nFor building and testing, see `v-workflow`; for language rules, see `v-lang`; for\nother commands, see `v-tools`.\n\nTwo commands, one decision: `v new` makes a directory, `v init` works in the one\nyou are already in.\n\n## The decision\n\n- `v new <name>` — creates `<name>/` and puts the project in it.\n- `v init` — puts the project in the current directory. Use this when the\n  directory already exists.\n\nBoth take the same three template flags, and both create a `v.mod`.\n\n## Put template flags before the project name\n\nTemplate flags belong to the `new` or `init` subcommand. For `new`, put them\nafter `new` and before the project name:\n\n```bash\nv new myapp           # executable, the default\nv new --lib mylib     # library\nv new --web myapp     # veb web app\n```\n\n`v new mylib --lib` fails with *\"too many arguments\"*: flag parsing stops at the\nproject name, so the later flag is read as another argument.\n\n## What each template makes\n\n| Flag | Entry point | Module | Extra |\n| --- | --- | --- | --- |\n| `--bin` (default) | `main.v` | `main` | — |\n| `--lib` | `<name>.v` | `<name>` | `tests/<fn>_test.v` |\n| `--web` | `main.v` | `main` | `assets/main.css`, `templates/index.html` |\n\nAll three also create `v.mod`, `.editorconfig`, `.gitattributes` and\n`.gitignore`.\n\n## v.mod\n\n```\nModule {\n\tname: 'myapp'\n\tdescription: ''\n\tversion: '0.0.0'\n\tlicense: 'MIT'\n\tdependencies: []\n}\n```\n\nCreating one starts a prompt for the description, version and license. **The\nprompt only runs when stdin is a terminal.** With piped or redirected stdin,\nthe defaults are used. A non-interactive\n`v new` requires the project name; `v init` derives it from the current directory.\nFor example:\n\n```bash\nv new myapp < /dev/null\n```\n\nIf git is installed and the directory is not already a git project, `git init`\nruns as part of setup.\n\n## After it exists\n\n```bash\ncd myapp\nv run .          # build and run an executable or web project\nv -check .       # type-check only\nv test .         # run the tests\n```\n\nThe module name is `main` for executable and web templates. For a library, the\nmodule name is the project name.\n\n## Resource Routing\n\n- `references/TEMPLATES.md` - Read when choosing between `--bin`, `--lib` and\n  `--web`, or when a template's contents need checking against the current\n  compiler.\n\n## Quick Reference\n\n```bash\nv new <name>          # executable project in <name>/\nv new --lib <name>    # library project\nv new --web <name>    # veb project\nv init                # project in the current directory\nv init --lib          # library in the current directory\n```\n","vlib/v/skills/v-lang/SKILL.md":"---\nname: v-lang\ndescription: The V language rules that make code which looks right fail to compile or behave wrongly - ?T versus !T, sum types and match exhaustiveness, mut receivers, module names matching directories, comptime $ forms, and explicit type conversion. Use when writing or reviewing any V code that uses an Option, a Result, a sum type, a match, generics, or a custom flag, and when a V compiler error names an option, a result, exhaustiveness, mutability, or a module. Does not cover the build and test loop (see v-workflow), writing tests (see v-testing), reading a V project through the MCP server (see v-mcp), scripting in V (see v-scripts), or the wider command surface (see v-tools).\nlicense: MIT\n---\n\n# The V rules that bite\n\nMost mistakes an agent makes in V are not algorithmic. They are these rules, and\neach one fails in a way that does not look like the mistake.\n\n## Resource Routing\n\n- `references/OPTION-RESULT.md` - Read when deciding whether a function returns\n  `?T` or `!T`, or when an `or {}` block is not doing what you expected.\n- `references/SUMTYPES.md` - Read when modelling a closed set of states, or when\n  `match` reports a non-exhaustive match.\n- `references/COMPTIME.md` - Read when a symbol only exists on some platforms, or\n  when reaching for reflection or `$embed_file`.\n- `references/MUTABILITY.md` - Read when an assignment is rejected, or when a\n  mutation to a field does not reach the caller.\n\n## Quick Reference\n\n| You want | Write | Not |\n| --- | --- | --- |\n| A value that may be absent | `fn f() ?int` | `return 0` as a sentinel |\n| An operation that can fail | `fn f() !int` | `return -1` |\n| Unwrap, no default | `x or { return error('...') }` | `x or { 0 }` on a result |\n| Unwrap, with a default | `x or { 0 }` on an option | `x or { return }` on an option |\n| Branch on a closed set | `enum` + exhaustive `match` | a string constant |\n| Mutate an argument | `fn f(mut x T)` | `x = ...` silently failing |\n| Mutate the caller's struct | `fn (mut t &T)` | `fn (mut t T)` (a copy) |\n| Run code at compile time | `$if`, `$for` | a runtime `if` |\n\n## `?T` is not `!T`\n\n`?T` holds a value or `none`. `!T` holds a value or an error. They unwrap\ndifferently, and using one where the other belongs is a type error rather than a\nlogic error.\n\n```v ignore\n// An option: absence is part of the answer.\nfn find_port(cfg Config) ?int {\n    if !cfg.has_port {\n        return none\n    }\n    return cfg.port\n}\n\n// A result: the operation can fail, and the failure carries why.\nfn read_port(path string) !int {\n    lines := os.read_lines(path) or {\n        return error('cannot read ${path}: ${err.msg()}')\n    }\n    ...\n}\n```\n\nThe rule that catches people: inside `or { ... }` on a **result**, the block must\nproduce that result's error value, so it is `return error(...)` and not a bare\n`return`. On an **option**, `or { default }` supplies a value.\n\n**Default**: return `!T` for anything that touches the outside world, and `?T` for\na lookup that is legitimately absent. Full detail, including how to propagate and\nwhen a default is honest, in `references/OPTION-RESULT.md`.\n\n## Sum types and match\n\nV enums plus `match` are the way to model a closed set. `match` over an enum is\nchecked for exhaustiveness, which is the point: adding a variant turns every\nincomplete `match` into a compile error instead of a silent fallthrough.\n\nThe diagnostic is exact: `non-exhaustive match expression without `else``. Two\nways out — handle every variant, or add an `else` branch and accept that a new\nvariant changes nothing.\n\n```v ignore\nenum Status {\n    pending\n    running\n    done\n    failed\n}\n\nfn label(s Status) string {\n    return match s {\n        .pending { 'queued' }\n        .running { 'in progress' }\n        .done { 'finished' }\n        .failed { 'could not finish' }\n    }\n}\n```\n\nVariants are shared: `type Job = Status | string` is a sum type, and a `match`\nover it must handle both sides. See `references/SUMTYPES.md`.\n\n## mut is not optional\n\nFunction arguments are immutable. A receiver is immutable unless it is declared\n`mut`. This is the single most common V compile error, and it always has the same\ncause: the author assumed mutation was the default.\n\n```v ignore\n// Rejected: `app` is immutable.\n// fn serve(app App) { app.port = 8080 }\n\n// Accepted: the argument is mutable.\nfn serve(mut app App) { app.port = 8080 }\n\n// Accepted, and it reaches the caller: the receiver is a pointer.\nfn serve(app &App) { app.port = 8080 }\n\n// Accepted: the field itself is declared mutable.\nstruct Server {\nmut:\n    port int\n}\n```\n\n`mut` on a **value** receiver mutates a copy, so the caller's struct is\nunchanged. `mut` on a **pointer** receiver mutates the caller's. That difference\nis silent at the call site, which is why `references/MUTABILITY.md` exists.\n\n## Module names must match directories\n\n`module foo` must live in a directory named `foo`. A mismatch imports cleanly and\nthen resolves to nothing, with no error pointing at the name. The `module` line\ncarries no hierarchy — the directory nesting supplies it.\n\nThis is why a project built by `v new` has `src/main.v` with `module main` rather\nthan `module src`.\n\n## Comptime is `$`, and it excludes branches\n\nAnything starting with `$` runs while the compiler runs. The branches of an\n`$if` are **excluded entirely** on a platform that does not match, which is the\nonly reason a platform-specific import inside one is safe.\n\n```v ignore\n$if windows {\n    import sys.windows as wsys\n}\n$if linux {\n    import sys.linux\n}\n```\n\nA runtime `if` that mentions a platform-specific symbol will not compile on the\nother platforms, because there is no exclusion. Available in `$if`: `windows`,\n`linux`, `macos`, `js`, `freebsd`, `android`, `debug`, `prod`, and your own\n`$d custom_flag ? { ... }` — note the `?`.\n\n## No implicit conversions\n\n`int` is 32 bit and `i64` is 64. `f64` and `int` do not mix. Nothing converts\nimplicitly, which is deliberate: a silent narrowing is a bug you find later.\n\nBe explicit at the boundary, and prefer `strconv` over hand-rolled parsing.\n\n## Validation\n\nAfter changing V code, prove it rather than reading it again:\n\n```bash\nv -check path/to/file.v        # type-check only, no binary\nv fmt -verify path/to/file.v  # would the formatter change it?\n```\n\nFor library code rather than a `main` module, `-check` alone fails with *\"project\nmust include a `main` module\"*; use `v -check -shared path/to/file.v`.\n\nRun both before you report the change done. See `v-workflow` for the full loop.\n\n## Related Skills\n\n- **The build and test loop**: see [v-workflow](../v-workflow/SKILL.md) for\n  `v.mod`, dependencies, when `./v self` is required, and the flags that go\n  before the subcommand.\n- **Writing tests**: see [v-testing](../v-testing/SKILL.md) when the change needs\n  a `_test.v` file, or when `?T` and `!T` need asserting.\n- **Asking the compiler**: see [v-mcp](../v-mcp/SKILL.md) when you want the\n  declarations, references or diagnostics of a file without reading it, or when\n  you want to edit through an AST-aware rename.\n- **Memory and GC**: see [v-memory](../v-memory/SKILL.md) for GC modes, ownership\n  checking and `unsafe`.","vlib/v/skills/v-concurrency/SKILL.md":"---\nname: v-concurrency\ndescription: V's concurrency primitives - spawn, the sync module (channels, Mutex, RwMutex, WaitGroup, Once, Pool, select), and parallel.amap for parallel map and run. Use when writing code with goroutines, threads, channels, locks, shared mutable state, a WaitGroup, or a race, and when reviewing V code that touches them. Covers how to wait for spawned work and why a detached spawn is a bug. Does not cover the mutability rules a lock protects (see v-lang), the build and test loop (see v-workflow), web servers (see v-veb), scripting a task in V (see v-scripts), or the wider command surface (see v-tools).\nlicense: MIT\n---\n\n# Concurrency in V\n\nV has three levels: `parallel.amap` when you have a collection to process, `spawn`\nplus a `WaitGroup` when you have a fixed set of tasks, and `sync` channels and\nlocks when the tasks have to talk to each other.\n\n## Resource Routing\n\n- `references/CHANNELS.md` - Read when passing work or results between threads, or\n  when a producer must not block.\n- `references/PATTERNS.md` - Read for worker pools, fan-out, a single-writer\n  cache, and the shapes that deadlock.\n\n## Reach for these first\n\n| Situation | Use |\n| --- | --- |\n| Process a slice, collect results | `parallel.amap(items, worker)` |\n| Process a slice, discard results | `parallel.run(items, worker)` |\n| A fixed number of independent tasks | `spawn f()` plus `sync.WaitGroup` |\n| Hand work to a pool | a channel plus a fixed set of consumers |\n| Share mutable state | `sync.Mutex`, and keep the critical section small |\n| Initialise once, lazily | `sync.Once` |\n| Reuse objects | `sync.Pool` |\n| Wait for any of several channels | `select` |\n\nReaching for `spawn` when `amap` would do is the most common mistake. `spawn`\nmakes you wait for the work; `amap` does it for you.\n\n## parallel: do not hand-roll this\n\n```v ignore\nimport arrays.parallel\n\nfn test_it_doubles_every_number() {\n\titems := [1, 2, 3, 4]\n\tgot := parallel.amap(items, fn (n int) int {\n\t\treturn n * 2\n\t})\n\tassert got == [2, 4, 6, 8]\n}\n```\n\n`amap` preserves order and returns a slice; `run` discards results. Both take an\noptional `Params` whose `workers` defaults to `runtime.nr_jobs()`, which reads\n`VJOBS`.\n\nThis is worth preferring over a loop with a `WaitGroup` even when the loop is\nshort: there is no way to forget to wait.\n\n## spawn needs a WaitGroup\n\n`spawn` starts a thread and returns immediately. Nothing waits for it, so a\nprogram that ends, or a function that returns, can leave it mid-write.\n\n```v ignore\nimport sync\n\nfn fetch_all(urls []string) []string {\n\tmut wg := sync.new_waitgroup()\n\tmut results := []string{len: urls.len}\n\tfor url, i in urls {\n\t\twg.add(1)\n\t\tspawn fn () {\n\t\t\tresults[i] = fetch(url)\n\t\t\twg.done()\n\t\t}\n\t}\n\twg.wait()\n\treturn results\n}\n```\n\nEvery rule that matters here is easy to get wrong:\n\n- **`wg.add(1)` before the spawn, `wg.done()` inside it.** A `done()` without a\n  matching `add()` panics.\n- **All `wg.go()` / `spawn` before `wg.wait()`.** The doc comment says so\n  explicitly: adding after waiting is a race.\n- **`wait()` or nothing.** A detached spawn is a bug unless it is genuinely\n  fire-and-forget, and V is not a language where that is the default answer.\n- **Never let a spawned function panic.** It takes the whole process down.\n\n`sync.WaitGroup.go(f)` bundles the `add` and the thread start, which removes two\nof the four mistakes:\n\n```v ignore\nmut wg := sync.new_waitgroup()\nwg.go(fn () { work() })\nwg.wait()\n```\n\n## Sharing state needs a lock\n\nA struct shared between threads needs a `Mutex`, and the rule is to hold it for\nthe shortest possible span:\n\n```v ignore\nimport sync\n\nstruct Counter {\nmut:\n\tmu    sync.Mutex\n\ttotal int\n}\n\nfn (mut c Counter) add(n int) {\n\tlock c.mu {\n\t\tc.total += n\n\t}\n}\n```\n\nDo not hold a lock across a channel operation, a `spawn`, or anything that can\nblock. Two locks taken in different orders by two threads deadlock, so pick one\norder and hold to it.\n\n`RwMutex` is for a structure read far more often than it is written. If the write\nrate is not clearly lower, `Mutex` is simpler and fast enough.\n\n## Channels are the typed default\n\nA channel moves one value type between threads and gives you a buffer:\n\n```v ignore\nmut jobs := chan Job{cap: 16}\nmut results := chan Result{cap: 16}\n\nspawn fn () {\n\tfor job in jobs {\n\t\tresults <- run(job)\n\t}\n\tclose(results)\n}\n\nfor r in results {\n\tprintln(r)\n}\n```\n\nNote `chan Job{cap: 16}` — the capacity is part of the type, and a buffered channel\nlets a producer get ahead instead of blocking on every send.\n\nSee `references/CHANNELS.md` for closing, `select` and timeouts.\n\n## Atomics for counters\n\nFor a single value — a counter, a flag, a pointer — a lock is more than needed:\n\n```v ignore\nimport sync.stdatomic\n\nmut hits := stdatomic.new_atomic(0u64)\nhits.add(1u64)\ntotal := hits.load()\n```\n\nThe type is fixed when the atomic is created, and `load`, `store` and `add` need\n`mut`. There are also plain functions taking a pointer (`stdatomic.add_u64(&x, 1)`)\nfor the cases where an `AtomicVal` field is awkward.\n\nUse atomics for one or two values. The moment you need two of them to change\ntogether there is no transaction, and you want a `Mutex`:\n\n```v ignore\n// Two values that must stay consistent with each other.\nlock c.mu {\n    c.total += n\n    c.checks += 1\n}\n```\n\n## Validation\n\nConcurrency bugs are timing bugs, and reasoning about them is unreliable. Run\nunder the race detector and read what it reports:\n\n```bash\nv -race run main.v\nv -race test dir/\n```\n\nA clean race run does not prove the absence of races, but a reported one is\nreal. Never disable a report without explaining it. See `v-workflow` for the rest\nof the loop.\n\n## Related Skills\n\n- **Mutability**: see [v-lang](../v-lang/SKILL.md) for `mut` receivers and the\n  value-versus-pointer difference a lock protects.\n- **The build loop**: see [v-workflow](../v-workflow/SKILL.md) for `-race`,\n  `-prod` and the other flags.\n- **Web servers**: see [v-veb](../v-veb/SKILL.md) when the shared state is a\n  request counter and `veb` already gives you the locking idiom.","vlib/v/skills/v-package/SKILL.md":"---\nname: v-package\ndescription: \"Manage V packages: install, update, search, remove and link local modules.\"\nlicense: MIT\n---\n\n# V packages with vpm\n\nUse this when adding dependencies, resolving a missing package, or working with\n`v install`, `v update`, `v search` or `v link`. For building and testing, see\n`v-workflow`; for language rules, see `v-lang`.\n\nInvoke package commands as `v <subcommand>`, such as `v install <module>`.\nThe frontend dispatches these commands to the `vpm` tool; `v pm` is not a package\nsubcommand.\n\n```bash\nv install <module>   # install a package\nv update             # update the installed packages\nv outdated           # which installed packages have a newer version\nv list               # what is installed\nv remove <module>    # remove a package\nv show <module>      # information about a package\nv search <keyword>   # search vpm.vlang.io\nv why <module>       # why a module is in the dependency graph\nv link               # symlink the current project into the modules directory\nv unlink             # remove that symlink\nv upgrade            # upgrade all outdated modules\n```\n\n## Where modules go\n\n`~/.vmodules`, or `$VMODULES` when it is set. A module is stored under its name\nwith `.` replaced by the path separator, so `prantlf.json` becomes\n`~/.vmodules/prantlf/json`.\n\n## Installing\n\n`v install <module>` installs a package into the modules directory. It does not\nadd the package to the project's `v.mod`; add the dependency there explicitly.\nRun `v install` without package arguments from the project directory to install\nthe dependencies already declared in `v.mod`.\n\nGit packages include their file contents in the initial clone, so checkout can\ncomplete without a second network request for missing file blobs. Submodules are\ninstalled recursively.\n\n## Removing\n\n`v remove <module>` refuses two cases rather than guessing: a module outside the\nmodules directory, and one that was not installed by vpm. For a linked module the\nanswer is `v unlink` first.\n\n## Linking a local module\n\n`v link` symlinks the current project into the modules directory, so a change\nthere is seen without reinstalling. `v unlink` removes the symlink.\n\n## Resource Routing\n\n- `references/SUBCOMMANDS.md` - Read when a subcommand is missing, ignored, or\n  behaves differently from this summary.\n\n## Quick Reference\n\n```bash\nv install <module>\nv update\nv outdated\nv list\nv remove <module>\nv show <module>\nv search <keyword>\nv why <module>\nv link\nv unlink\nv upgrade\n```\n","vlib/v/skills/v-mcp/SKILL.md":"---\nname: v-mcp\ndescription: Using the V MCP server, `v mcp serve`, to read and change V code through the compiler itself - the AST, declarations, references, diagnostics, stdlib docs, an AST-aware rename, a guarded edit and the formatter. Use when an agent needs to know what a V file declares, whether V code compiles, where a symbol is used, or what a stdlib function takes, and to make a change safely across files. Does not cover the language rules themselves (see v-lang), the build and test loop (see v-workflow), writing test cases (see v-testing), scripting a task in V (see v-scripts), or the command surface outside the MCP server (see v-tools).\nlicense: MIT\n---\n\n# The V MCP server\n\n`v mcp serve` speaks MCP over stdio, or over HTTP with `--http`. It is the V\ncompiler exposed as tools: it calls the parser, the checker and the formatter **in\nprocess**, so it answers correctly about code that does not compile yet.\n\n```json\n{\n  \"mcpServers\": {\n    \"v\": { \"command\": \"v\", \"args\": [\"mcp\", \"serve\"] }\n  }\n}\n```\n\n## Resource Routing\n\n- `references/TOOLS.md` - Read for the arguments of a specific tool, or when you\n  need the tool that answers a particular question.\n- `references/EDITING.md` - Read before changing anything through the server. It\n  covers the guards and why each one exists.\n\n## Quick Reference\n\n| Question | Tool |\n| --- | --- |\n| What project is this? | `v_project_info` |\n| What modules can I import? | `v_modules` |\n| What files are here? | `v_files` |\n| What does this file declare? | `v_symbols` |\n| What is at the cursor? | `v_symbol_at` |\n| Where is this used? | `v_references` |\n| What does this stdlib call take? | `v_stdlib_doc` |\n| Does it compile? | `v_check` |\n| Do the tests pass? | `v_test_run` |\n| What are this app's routes? | `v_veb_routes` |\n| Rename a symbol | `v_rename_symbol` |\n| Change a known range | `v_edit_replace` |\n| Format a file | `v_format` |\n\n## A working order\n\n1. **`v_project_info` once per session.** It names the module, the compiler, the\n   root every path is relative to, and whether this is the V checkout itself.\n2. **`v_symbols`** to learn what a file declares. It carries the doc comments, so\n   it usually answers \"what is this file for\" without reading the body.\n3. **`v_check` after every change.** It is the only authoritative answer to \"does\n   it compile\", and it returns records with file, line and column.\n4. **`v_test_run`** for behaviour. `v_check` cannot catch a wrong result.\n\n## Prefer asking over reading\n\nThe whole point of the server is that it knows more than a file read does. Reach\nfor it before guessing:\n\n- a signature you are unsure of: `v_stdlib_doc`, not an invented one\n- whether an edit is safe: `v_references`, then `v_rename_symbol`\n- what a file contains: `v_symbols`, not a full read\n- what broke: `v_check`, not a re-read\n\nAn invented stdlib signature costs a compile cycle at best. A hand-written rename\nmisses the comment that mentions the name and the call in a file you never opened.\n\n## Editing is guarded by default\n\nThree tools write. None of them writes unless asked twice:\n\n- `v_rename_symbol` defaults to `dry_run: true` and lists every position it would\n  change.\n- `v_format` defaults to `write: false` and reports `before` and `after`.\n- `v_edit_replace` requires `expected_old`: you read the range and pass it back\n  verbatim, so it refuses to write over a change you did not see.\n\nRead the plan, then pass the flag that applies it. See `references/EDITING.md`.\n\n`--read-only` does not register these three at all, so a session started that way\ncannot write by accident.\n\n## Before guessing, ask\n\n- **A language rule**: the `v-lang` skill covers the parts agents get wrong, most\n  of all `?T` versus `!T` and comptime code.\n- **A build or module question**: the `v-workflow` skill, and `v_modules` for what\n  is importable right now.\n- **The code's shape**: `v_ast`, in the same JSON `v ast -p` prints.\n\n## When the tools cannot answer\n\n`v_run`, `v_test_run`, `v_doctor` and `v_eval` start the compiler. They are\nslower than the in-process tools and unavailable when the compiler cannot be\nstarted. A tool that could not start the compiler says so explicitly — it reports\n`started: false` with an error rather than an exit code, so a sandbox that cannot\nspawn processes is never mistaken for a failed check.\n\nIf you see that, the answer is about the environment, not about the code. Do not\nstart changing the code to satisfy a tool that never ran.\n\n## Validation\n\nThe server is the checker. Do not finish a change without it:\n\n```json\n{\"name\": \"v_check\", \"arguments\": {\"path\": \"src/main.v\"}}\n```\n\nThen format what you touched with `v_format`, and run the tests with\n`v_test_run`. See [v-workflow](../v-workflow/SKILL.md) for the equivalent shell\ncommands.\n\n## Related Skills\n\n- **The language rules**: see [v-lang](../v-lang/SKILL.md) for `?T` versus `!T`,\n  sum types, `mut` and comptime code.\n- **The build loop**: see [v-workflow](../v-workflow/SKILL.md) for `v.mod`,\n  dependencies, flags and when `./v self` is required.\n- **Tests**: see [v-testing](../v-testing/SKILL.md) for what to assert.\n- **Web apps**: see [v-veb](../v-veb/SKILL.md) for veb, and use `v_veb_routes` to\n  read the routes of an app you have not opened.","vlib/v/skills/v-scripts/SKILL.md":"---\nname: v-scripts\ndescription: Writing V as a scripting language - `.vsh` script mode, its implicit `import os` and unqualified `os` calls, running a script directly, the executable-cache contract that governs rebuilds, compiling a script without running it, and shebang scripts with no file extension. Use when a task is a build, release, maintenance or one-off automation job that would otherwise be bash, when deciding between a script and a small program, or when a `.vsh` file behaves differently from a `.v` file. Does not cover the language rules (see v-lang), the build and test loop (see v-workflow), writing test cases (see v-testing), or the wider command surface (see v-tools).\nlicense: MIT\n---\n\n# V scripts\n\nA file ending in `.vsh` is parsed in **script mode**. It is still V — the same\ntypes, the same modules, the same compiler — but two rules make it read like a\nshell script, and both are worth knowing before writing one, because they are\nwhy a script can fail in a way a program would not.\n\n## What script mode changes\n\n**`os` is imported for you.** A `.vsh` file gets an implicit `import os` when it\ndoes not already import `os` itself. If it does import `os` explicitly, that\nimport wins and keeps its own alias and selected symbols; the implicit one is\nnever added on top.\n\n**`os` functions are called unqualified.** In script mode an unqualified\n`read_file` resolves to `os.read_file`, so the usual `os.` prefix is optional:\n\n```v ignore\nprintln('hello')\ncontent := read_file('data.txt') or { panic(err) }\n```\n\nThat resolution reaches public `os` functions and constants (for example\n`args`). A local or parameter of the same name shadows it, so naming a variable\n`args` or `exists` changes what the bare name means — and that shadowing is\nscoped to the script, not to a `.v` file compiled beside it.\n\nNeither rule applies to a plain `.v` file. If a name resolves in a `.vsh` but not\nin a `.v` file, this is why.\n\n## Running and building scripts\n\nRunning the file runs the script; there is no separate interpreter to install.\n\n```sh\nv script.vsh                 # build if needed, then run\nv run script.vsh             # rebuild and run, keep no executable\nv build script.vsh           # compile to an executable, do not run it\nv -skip-running script.vsh   # the same thing\n```\n\nOn Unix-like systems a script that starts with this shebang runs by its path,\nonce it is executable (`chmod +x script.vsh`, then `./script.vsh`):\n\n```sh ignore\n#!/usr/bin/env -S v\n```\n\nFor `.vsh` scripts on systems whose `/usr/bin/env` has no `-S` (BusyBox,\nOpenBSD), put `cmd/tools/vrun` on the PATH and start the script with\n`#!/usr/bin/env vrun` instead; it runs the script with `v run`.\n\n`v script.vsh` follows **V's executable-cache contract**: it builds the script\nas `script` (the name without `.vsh`) next to the source and reuses that binary\non the next run, which is what makes repeated invocation cheap. It rebuilds when\nthe script is newer than the binary, or when an imported module changed. The\n`#!/usr/bin/env -S v` shebang goes through the same cache.\n\n`v run script.vsh` skips the cache: it rebuilds on every run and leaves no\nexecutable behind, and so do the `#!/usr/bin/env -S v run` and `vrun` shebangs.\nThe cached binary has to be deleted by hand: `v clean` handles `.v` files and\ndirectories, not `.vsh` scripts.\n\n## Scripts with no extension\n\nA file with a fully custom name and a shebang runs as a script when it starts\nwith:\n\n```sh ignore\n#!/usr/bin/env -S v -raw-vsh-tmp-prefix tmp\n```\n\n`tmp` is the prefix for the built executable, which is kept as\n`tmp.<filename>` beside the script. **Caution:** that name is overwritten if it\nalready exists. To rebuild every time instead of caching, use\n`#!/usr/bin/env -S v -raw-vsh-tmp-prefix tmp run`.\n\nThis runs in `crun` mode. V's own documentation recommends it for scripts that go\non the PATH and advises against it for build or deploy scripts.\n\n## Choosing between a script and a program\n\nReach for a script when the job is sequencing external tools, moving files,\nwatching a directory, or wrapping a build step. Reach for a program when there is\ndomain logic to test, when it needs a data structure, or when it will be imported.\n\nThe test suite exists to keep that boundary honest: if a `.vsh` has grown logic\nworth asserting on, move it into a `_test.v` alongside a real program. Script\nmode does not make a script untestable — it compiles like anything else.\n\n## Worked examples in the tree\n\nThese scripts rely on script mode — no `import os`, unqualified `os` calls,\nstatements at the top level — and are worth reading before writing one:\n\n- `examples/v_script.vsh` — creates, lists and removes files with `mkdir`, `ls`\n  and `rm`\n- `.github/problem-matchers/register_all.vsh` — reads `getenv`, then walks a\n  folder with `walk_ext`\n\nA larger `.vsh` can also be written like a program, with `import os` and a\n`fn main`. The scripts bundled with the skills are written that way, for example\n`vlib/v/skills/v-workflow/scripts/check.vsh`; their `#!/usr/bin/env -S v run`\nshebang leaves no binary in the skill folder.\n\n## Related Skills\n\n- **The commands around a script**: see [v-tools](../v-tools/SKILL.md) for\n  `v time`, `v retry`, `v repeat` and `v check-md`.\n- **The build and test loop**: see [v-workflow](../v-workflow/SKILL.md).\n- **The language rules**: see [v-lang](../v-lang/SKILL.md); a script is still V.","vlib/v/skills/v-testing/SKILL.md":"---\nname: v-testing\ndescription: Writing and running V tests - _test.v layout, test_ function naming, the assert builtin, fixtures and cleanup with os.vtmp_dir and defer, and the VTEST_ONLY and VTEST_ONLY_FN filters for a subset. Use when adding or changing a V test, when a test does not compile or does not run, when choosing what to assert, or when the suite is slow and only part of it is needed. Does not cover the rest of the build loop (see v-workflow), the option and result semantics a test has to assert (see v-lang), reading a project through the MCP server (see v-mcp), scripting in V (see v-scripts), or the wider command surface (see v-tools).\nlicense: MIT\n---\n\n# Writing and running V tests\n\nA V test is a `test_` prefixed function in a `_test.v` file. There is no test\nframework and no test object: the function takes no parameters, and `assert` is a\nbuiltin.\n\n## Resource Routing\n\n- `references/PATTERNS.md` - Read when writing a fixture, a table-driven test, or\n  anything that needs a temporary file and cleanup.\n- `references/TROUBLESHOOTING.md` - Read when a test does not compile, does not\n  run, or does not fail the way it should.\n- `references/COVERAGE.md` - Read when measuring which lines and functions a run\n  reached, or generating the tests that are missing.\n\n## Quick Reference\n\n| Need | Do |\n| --- | --- |\n| A test | `fn test_the_thing() { ... }` in `foo_test.v` |\n| Compare | `assert got == want, 'message'` |\n| Test an option | `assert f() == none` |\n| Test a result | `assert f() == none` — it fails with the error text |\n| A temporary file | `os.join_path(os.vtmp_dir(), 'name_${stamp}')` |\n| Cleanup | `defer { os.rm(path) or {} }` |\n| Only some files | `VTEST_ONLY='http' v test dir/` |\n| Only some functions | `VTEST_ONLY_FN='test_login' v test dir/` |\n| Hide passing tests | `v -silent test dir/` |\n| Timings | `v -stats test dir/` |\n| Line coverage | `v -no-skip-unused -coverage vcov/ test dir/` then `v cover vcov/` |\n| Untested functions | `v cover vcov/ --uncovered` |\n| Missing test bodies | `v cover vcov/ --write-tests --filter myapp/` |\n\n## Layout\n\n`foo.v` is tested by `foo_test.v` beside it. A test file holds only `test_`\nfunctions, imports, and helpers.\n\n```v ignore\n// foo_test.v\nimport foo\n\nfn test_it_adds() {\n\tassert add(2, 3) == 5\n}\n\nfn test_it_does_not_mutate_its_argument() {\n\tmut n := 3\n\tincrement(n)\n\tassert n == 3, 'increment changed its argument to ${n}'\n}\n```\n\n**A test function takes no parameters.** Not `*testing.T`, not a context. If you\nare porting a Go or Rust test, this is the first thing to change.\n\nA test file in a library directory does **not** declare `module main`. It imports\nwhat it needs, like any other file. Only a test file that is the `main` module of\nits project says `module main`.\n\n## Assertions say what they compared\n\nA bare `assert` failure prints `assertion failed` and a line number. That is not\nenough to act on.\n\n```v ignore\n// Weak: `assertion failed` and nothing else.\nassert lookup(users, id) == expected\n\n// Strong: the message is the difference.\ngot := lookup(users, id)\nassert got == expected, 'lookup(${id}) = ${got}, want ${expected}'\n```\n\nPut **got before want**. It reads in the order the mind works, and it makes a\nfailing suite scannable.\n\nAssert on values, never on message text, which is free to change:\n\n```v ignore\n// Brittle.\nassert read_config(x) == none\n\n// Correct: absence is the contract.\nif cfg := read_config(x) {\n    assert false, 'expected no config, got ${cfg}'\n}\n```\n\n## Running a subset\n\nThe full suite is slow. Filter it rather than paying for all of it:\n\n```bash\nVTEST_ONLY='http' v test dir/          # only files whose path contains http\nVTEST_ONLY_FN='test_login' v test dir/ # only functions matching\nv -silent test dir/                    # hide passing tests\nv -stats test dir/                     # add timings\n```\n\nBoth filters accept a comma-separated list. File filters use path fragments;\nfunction filters use glob patterns. If a filter matches nothing, the raw runner\nreports zero tests and exits successfully. The wrapper uses the normal reporter\nfor its child runs and preserves other `VFLAGS` options. It reports an empty\nselection as a failure, including when no filter is set:\n\n```bash\nv run scripts/run-tests.vsh dir/ --file http --fn 'test_login*'\n```\n\nSee `references/TROUBLESHOOTING.md` for other causes of tests not running.\n\n## Measuring coverage\n\n```bash\nv -no-skip-unused -coverage vcov/ test ./   # record what the run entered\nv cover vcov/                                # one percentage per file\n```\n\n`-no-skip-unused` is not optional. Without it a function nothing calls is\ndropped from the build, and the report cannot tell \"not tested\" from \"not\nbuilt\".\n\nLine coverage cannot name a function no test calls when that function's body\nholds no statement a line point would record. Two flags answer it:\n\n```bash\nv cover vcov/ --uncovered    # one `path:line function` per untested function\nv cover vcov/ --write-tests --filter myapp/   # the test bodies it is missing\n```\n\nThe generator writes a starting point, not a test — a call with zero values and\na `// TODO:` line, because it cannot know what the function should return. It\nappends to an existing `_test.v` and never touches a test function that is\nalready there. `--dry-run` shows the plan.\n\nSee [references/COVERAGE.md](references/COVERAGE.md) for the whole surface,\nincluding `--functions`, `--json` and what `--write-tests` refuses to do.\n\n## Fixtures\n\n\nThere is no test object to hang a fixture on, so a test writes to `os.vtmp_dir()`\nand registers the removal with `defer`. A unique name per test keeps parallel runs\nfrom colliding.\n\n```v ignore\nfn test_it_reads_a_config() {\n\tstamp := os.getpid()\n\tpath := os.join_path(os.vtmp_dir(), 'cfg_${stamp}.json')\n\tos.write_file(path, '{\"port\": 8080}') or {\n\t\tassert false, 'could not write ${path}: ${err.msg()}'\n\t}\n\tdefer {\n\t\tos.rm(path) or {}\n\t}\n\t...\n}\n```\n\nSee `references/PATTERNS.md` for table-driven tests, sub-suites and helpers.\n\n## Validation\n\nA test that has never failed is not known to test anything. Break it on purpose\nonce, watch it fail for the right reason, then put it back.\n\n```bash\nVTEST_ONLY_FN='test_the_new_thing' v test path/to/file_test.v\nv -silent test path/to/dir/\n```\n\nThen `v fmt -verify path/to/file_test.v`. See [v-workflow](../v-workflow/SKILL.md).\n\n## Related Skills\n\n- **The build loop**: see [v-workflow](../v-workflow/SKILL.md) for `-check`,\n  `fmt -verify`, `vet` and the flags that gate a change.\n- **Option and Result semantics**: see [v-lang](../v-lang/SKILL.md) for what to\n  assert when a function returns `?T` or `!T`.\n- **Through the MCP server**: see [v-mcp](../v-mcp/SKILL.md) when `v_test_run` can\n  report what passed and what the failures said, without parsing runner output.\n","vlib/v/skills/v-tools/SKILL.md":"---\nname: v-tools\ndescription: The parts of the V command surface that are easy to miss - inspecting code (`v ast`, `v where`, `v mod why`), shrinking a compiler error (`v reduce`), project hygiene (`v missdoc`, `v check-md`, `v bump`, `v git-fmt-hook`), embedding binaries (`v bin2v`), shell integration (`v complete`), timing and repeating commands (`v time`, `v repeat`, `v retry`), and compiler upkeep (`v up`, `v clean`, `v env`, `v tracev`). Use when a task is about the toolchain rather than the program, when reaching for `v` by habit and a better command exists, or when asked what V can do. Does not cover writing `.vsh` scripts (see v-scripts), the language rules (see v-lang), the build and test loop (see v-workflow), or reading a project through the MCP server (see v-mcp).\nlicense: MIT\n---\n\n# V tools\n\nMost of V's command surface is reached by habit. This is the part that is not:\ncommands that answer a question, shrink a failure, or keep a project honest.\n`v help topics` lists everything; the ones below are the ones worth knowing.\n\n## Look at code before changing it\n\n```sh\nv ast -p file.v         # print the AST as JSON; without -p it writes file.json\nv where fn NAME         # where a symbol is declared; also struct, method, enum, const\nv mod why MODULE        # which import chain pulls a module into the build\n```\n\n`v where` finds declarations only, and searches vlib as well as the project;\n`-mod main` keeps it to the project. Where a symbol is used is answered by the\n`v_references` tool of the MCP server.\n\n`v mod why` is the one that answers a question people otherwise guess at: nothing\nin `v.mod` records what a build actually reaches, so a dependency can outlive the\ncode that needed it.\n\nThrough the MCP server the same ground is covered with less shell work; see\nv-mcp.\n\n## Shrink a compiler error\n\n```sh\nv reduce -m 'text of the error' file.v   # the smallest source that still errors\n```\n\nWithout `-m`, `v reduce` only looks for a C compilation error, so a checker error\nsuch as `unknown function: nope` needs its text passed in. The reduction lands in\n`rpdc_<file>.v` (`rpdc_bad.v` for `bad.v`) in the current directory. Pair it with\n`v bug`, which opens a prefilled issue, and the report carries a minimal\nreproducer instead of the file you happened to be editing.\n\n## Measure test coverage\n\n```sh\nv -no-skip-unused -coverage vcov/ test ./   # record what the run entered\nv cover vcov/                               # one percentage per file\nv cover vcov/ -H                            # the hottest lines\nv cover vcov/ --lcov out.info               # the format CI tools read\nv cover vcov/ --uncovered                   # `path:line function` per untested function\nv cover vcov/ --functions                   # every function, covered or not\nv cover vcov/ --json report.json            # machine-readable, both levels\nv cover vcov/ --write-tests --filter dir/   # the test bodies it is missing\n```\n\nTwo steps: the run records, `v cover` reads. Delete the directory between runs —\nthe counters are appended, so a stale one reports coverage that no longer\nexists.\n\n`--write-tests` generates a `test_` function per untested function, beside its\nsource, as a starting point and not a test: a call with zero values and a\n`// TODO:` line. It appends to an existing `_test.v`, leaves a test function\nthat is already there alone, and refuses without `--filter`, because a coverage\ndirectory holds every file a run reached.\n\nSee [v-testing](../v-testing/SKILL.md) for the details.\n\n## Project hygiene\n\n```sh\nv missdoc src/          # public functions with no documentation comment\nv check-md README.md    # are the ```v blocks in this markdown correct?\nv bump --patch          # or --minor / --major; bumps the version in v.mod\nv git-fmt-hook install  # format on commit\n```\n\n`v check-md` is worth knowing about specifically: it compiles the V examples in a\nmarkdown file, which catches a snippet that stopped compiling long after anyone\nlooked at the prose. `v missdoc` takes `-p` to include private functions and `-t`\nto include function tags.\n\n## Embed binary files\n\n```sh\nv bin2v -m main -w assets.v assets/logo.png assets/font.ttf\n```\n\nWrites each file into `assets.v` as a byte-array constant (`logo_png`, with its\nlength in `logo_png_len`), so a program carries its images and fonts instead of\nreading them from disk at runtime. Without `-w` the source goes to stdout. The\nconstants are private, so generate them into the module that uses them. For a\nsingle file, `$embed_file('assets/logo.png')` does the same at compile time.\n\n## Shell integration\n\n```sh\nv complete setup bash   # print the completion setup script; bash, fish, zsh, powershell\n```\n\nLoad it from the shell's startup file; for bash, add\n`source /dev/stdin <<<\"$(v complete setup bash)\"` to `~/.bashrc`.\n\n## Time, repeat, retry\n\n```sh\nv time CMD              # how long CMD took, and what it exited with\nv repeat CMD...         # repeat commands and collect statistics\nv retry CMD             # rerun until it succeeds, for up to 10 retries or 10 minutes\n```\n\n`v retry` is the one to reach for when a flaky network call or a busy port is the\nthing in the way; `-r` and `-t` change its limits. `v time` is the one when the\nquestion is which of two approaches is actually cheaper.\n\n## Compiler upkeep\n\n```sh\nv up                    # update V itself\nv clean                 # remove what a default build leaves behind\nv env                   # the environment variables that steer the compiler\nv tracev                # a tracing build of the compiler\n```\n\n`v env` is the first thing to read when the compiler or one of its tools behaves\ndifferently on another machine, because most of that behaviour is an environment\nvariable. `v help build-c` documents the C-backend flags — `-cc`, `-glibc`,\n`-musl` — when the C toolchain itself is the thing in question.\n\n## Other commands worth knowing\n\n```sh\nv ls                    # install and run the V language server\nv repl                  # the V REPL\nv watch file.v          # rebuild when the sources it needs change\nv crun file.v           # build and run, keeping the executable\nv tool NAME             # run a tool module by name\nv share file.v          # send a .v file to the V Playground\nv download URL          # fetch a file over http or https\nv symlink               # put v on PATH\n```\n\n`v download -RD URL` also runs the downloaded file with `v run` and deletes it\nafterwards. That executes remote code, so read the file first.\n\nPackage management is `v search`, `v show`, `v list`, `v outdated`, `v update`,\n`v upgrade` and `v remove`, over the VPM registry.\n\n## The command that installs this skill\n\n```sh\nv skills list           # what is bundled, and what is installed\nv skills add v-tools    # install one\nv skills update         # refresh what a new V revision changed\n```\n\n## Related Skills\n\n- **Scripts**: see [v-scripts](../v-scripts/SKILL.md) for `.vsh` script mode and\n  its executable cache.\n- **The build and test loop**: see [v-workflow](../v-workflow/SKILL.md).\n- **The language rules**: see [v-lang](../v-lang/SKILL.md).\n- **Reading a project through the compiler**: see [v-mcp](../v-mcp/SKILL.md) for\n  declarations, references and diagnostics without shell commands.","vlib/v/skills/v-veb/SKILL.md":"---\nname: v-veb\ndescription: Writing V web applications with veb - the App and Context structs, route attributes with path parameters, ctx.html and ctx.json, templates with $tmpl and $veb.html, middleware with Middleware.use and route_use, shared state with shared and lock, and static file mounting. Use when creating or changing a veb server, adding a route or handler, returning JSON or HTML, adding middleware, or serving files. Does not cover concurrency in general (see v-concurrency), the language rules (see v-lang), the build loop (see v-workflow), scripting a task in V (see v-scripts), or the wider command surface (see v-tools).\nlicense: MIT\n---\n\n# veb, V's web framework\n\nA veb server is two structs and a `main`. Handlers are methods on `App`, and every\nrequest runs with the whole `App` shared across threads.\n\n## Resource Routing\n\n- `references/ROUTES.md` - Read when adding a route, a path parameter, or a\n  response body.\n- `references/MIDDLEWARE.md` - Read when adding cross-cutting behaviour, shared\n  state, or static files.\n\n## Quick Reference\n\n| Need | Do |\n| --- | --- |\n| A route | `@['/path']` above a `pub fn (mut app App) handler(mut ctx Context) veb.Result` |\n| A path parameter | `@['/users/:id']` and an extra `id string` argument |\n| A specific method | `@[post]` or `@['/upload'; post]` |\n| HTML | `return $veb.html()` or `return $veb.html('page.html')` |\n| JSON | `return ctx.json(payload)` |\n| Plain text | `return ctx.text('...')` |\n| Middleware | `app.Middleware.use(handler: f)` |\n| Middleware for a path | `app.Middleware.route_use('/admin/:path...', handler: f)` |\n| Shared state | `field shared T`, mutated inside `lock app.state { }` |\n| Serve files | `app.mount_static_folder_at(...)` |\n| Serve a folder at root | `app.handle_static(directory_path, root)` |\n\n## The shape of a server\n\n```v ignore\nimport veb\n\nconst port = 8080\n\npub struct App {}\n\nstruct Context {\n\tveb.Context\n}\n\nfn main() {\n\tmut app := &App{}\n\tveb.run[App, Context](mut app, port)\n}\n\n@['/']\npub fn (mut app App) index(mut ctx Context) veb.Result {\n\treturn ctx.text('hello')\n}\n\n@[post]\npub fn (mut app App) submit(mut ctx Context) veb.Result {\n\treturn ctx.json({ok: true})\n}\n```\n\nThree things to notice:\n\n- `App` is `pub` and `Context` embeds `veb.Context`. Both are type arguments to\n  `veb.run`, which is how veb knows your types.\n- A handler takes `mut ctx Context` and returns `veb.Result`. Return the result of\n  a `ctx` method rather than constructing one.\n- `mut app` in the signature means the method may mutate `App`. A read-only handler\n  takes `app &App`, and veb accepts that too.\n\n## Routes\n\nThe attribute is a list of paths. Without a leading `/`, it is an HTTP method with\nthe default path.\n\n```v ignore\n@['/users/:user']\npub fn (mut app App) user_endpoint(mut ctx Context, user string) veb.Result {\n\treturn ctx.json({user: user})\n}\n\n@[post]\npub fn (mut app App) create(mut ctx Context) veb.Result { ... }\n\n@['/upload'; post]\npub fn (mut app App) upload(mut ctx Context) veb.Result { ... }\n```\n\nA `:name` in the path becomes an argument **after** `ctx`. A `:name...` at the end\ncaptures the rest of the path.\n\nSee `references/ROUTES.md` for matching rules and ordering.\n\n## Shared state\n\n`App` is shared across request threads, so a mutable field needs to say so, and\nevery write needs a lock:\n\n```v ignore\npub struct App {\nmut:\n\tstate shared State\n}\n\nstruct State {\nmut:\n\thits int\n}\n\npub fn (mut app App) index(mut ctx Context) veb.Result {\n\tlock app.state {\n\t\tapp.state.hits++\n\t}\n\treturn ctx.text('${app.state.hits}')\n}\n```\n\n`shared` is what makes the field shared rather than per-request. Omitting it, or\nwriting without the `lock`, is a data race — and it is the mistake that matters\nmost in a veb app.\n\n## Middleware\n\nMiddleware wraps requests. Global for the app, or scoped to a path:\n\n```v ignore\nstruct App {\n\tveb.Middleware[Context]\n}\n\nfn main() {\n\tmut app := &App{}\n\t// Chained, evaluated in order.\n\tapp.Middleware.use(handler: other_func1)\n\tapp.Middleware.use(handler: add_headers)\n\t// Only for matching paths.\n\tapp.Middleware.route_use('/admin/:path...', handler: check_auth)\n\tveb.run[App, Context](mut app, http_port)\n}\n```\n\nA middleware handler takes `mut ctx Context` and returns a `bool`. Return `true` to\ncontinue the chain; return `false` to stop it, which is how a middleware short\ncircuits a request.\n\nSee `references/MIDDLEWARE.md`.\n\n## Templates\n\n`$veb.html()` renders the templates directory; `$veb.html('name.html')` renders one\nfile; `$tmpl` renders a string in memory, which is how a layout is composed with a\npage:\n\n```v ignore\n@['/']\npub fn (app &App) index(mut ctx Context) veb.Result {\n\tcontent := $tmpl('templates/index.html')\n\tbase := $tmpl('templates/base.html')\n\treturn ctx.html(base)\n}\n```\n\n## Validation\n\nA veb app is checked the same way as any V code, and `-check` catches most of it —\nincluding a handler whose signature does not match what veb expects:\n\n```bash\nv -check -shared path/to/veb_app.v     # library-shaped: needs -shared\nv fmt -verify path/to/veb_app.v\nv run path/to/main.v\n```\n\nSee [v-workflow](../v-workflow/SKILL.md) for the full loop.\n\n## Related Skills\n\n- **Concurrency**: see [v-concurrency](../v-concurrency/SKILL.md) for what\n  `shared` means and the lock discipline that goes with it.\n- **The build loop**: see [v-workflow](../v-workflow/SKILL.md) for `-check`,\n  `-shared`, `fmt -verify` and the other flags.\n- **The language rules**: see [v-lang](../v-lang/SKILL.md) for `mut` and for\n  `?T` versus `!T` when a handler has to return an error.","vlib/v/skills/v-workflow/SKILL.md":"---\nname: v-workflow\ndescription: How to build, type-check, format, vet, test and ship V code, and how the v.mod module system resolves imports. Use before declaring a V change finished, when a build or test command is needed, when a dependency will not resolve, when working in the V compiler's own source tree, or when a v.mod needs reading or writing. Covers flag placement, the difference between -check and a real build, and when ./v self is required. Does not cover the language rules themselves (see v-lang), writing test cases (see v-testing), working through the MCP server (see v-mcp), scripting a task in V (see v-scripts), or the wider command surface (see v-tools).\nlicense: MIT\n---\n\n# The V build loop\n\nV's compiler is fast and its checker is the authority. A V change is not finished\nuntil the checker, the formatter and the tests have all said so.\n\n## Resource Routing\n\n- `references/FLAGS.md` - Read when a flag is ignored or has to be moved, or when\n  choosing between `-prod`, `-g`, `-cflags` and `-ldflags`.\n- `references/V-MOD.md` - Read when a dependency will not resolve, or when\n  deciding between `v install`, `v link` and `v unlink`.\n- `references/COMPILER-WORKFLOW.md` - Read only when the change is inside the V\n  compiler's own tree, where the rules above are not enough.\n\n## Flags go before the subcommand\n\nThis is the first thing that goes wrong. Every compiler option comes **before**\nthe command; anything after it is passed to the command, not the compiler.\n\n```bash\nv -prod run main.v      # correct: -prod is a compiler option\nv run main.v -prod      # wrong: main.v receives -prod as an argument\n```\n\n`VFLAGS` sets options for every invocation, which is how CI pins a C compiler:\n\n```bash\nexport VFLAGS='-cc gcc'\n```\n\n## The loop\n\nRun these in order. Each one is cheaper than the one after it.\n\n```bash\nv -check path/to/file.v        # type-check only, no binary produced\nv fmt -verify path/to/file.v   # would the formatter change it?\nv -stats test path/to/file_test.v\nv vet -W path/to/file.v        # suspicious constructs, failures as errors\n```\n\nFor **library** code rather than a `main` module, `-check` and `vet` need\n`-shared`, or they fail with *\"project must include a `main` module\"*:\n\n```bash\nv -check -shared vlib/v/skills/\n```\n\n**Validation**: `v fmt -verify` and `v -check` are the two that gate everything\nelse. Run them on every file you touched before reporting the change done. A\ngreen test run does not imply a formatted file, and a formatted file does not\nimply it compiles.\n\n## Quick Reference\n\n| Question | Command |\n| --- | --- |\n| Does it type-check? | `v -check file.v` |\n| Is it formatted? | `v fmt -verify file.v` |\n| Format it | `v fmt -w file.v` |\n| Does it pass? | `v -stats test dir/` |\n| Only some tests | `VTEST_ONLY='pattern' v test file_test.v` |\n| Does the formatter agree with CI? | `v -silent test-fmt` |\n| Anything suspicious? | `v vet -W file.v` |\n| What does this symbol resolve to? | `v where Symbol` |\n| Does this still work? | `v doctor` |\n| Add a dependency | `v install module.name` |\n\n## Errors worth reading properly\n\nV's diagnostics carry a file, a line and a column, and the column is usually\nexact:\n\n```\nmain.v:12:5: error: unknown function: nope\n   12 |     nope()\n      |     ~~~~\n```\n\nThe caret span is the whole expression. Read the message first, then look at the\nspan — the span is frequently wider than you expect, because it covers the call\nrather than the name.\n\nDo not fix a V error by reading the file again. Fix it from the span.\n\n## The V compiler's own tree\n\nThe rules change when the change is inside the compiler itself. A compiler change\nmeans a rebuild before anything else, or you test a stale binary:\n\n```bash\n./v self                      # rebuild ./v from the current sources\n./v -silent test vlib/v/      # the compiler's own tests\n./v -silent vlib/v/compiler_errors_test.v\n```\n\n`vlib/v/` and `cmd/v/` changes additionally mean the test runner, the formatter\nand the language server should be tested too. See `references/COMPILER-WORKFLOW.md`\nfor the full list and what each one catches.\n\n## Related Skills\n\n- **The language rules**: see [v-lang](../v-lang/SKILL.md) when the compiler\n  rejects an option, a result, a match or an assignment.\n- **Writing tests**: see [v-testing](../v-testing/SKILL.md) for assertion forms,\n  fixtures and running a subset.\n- **Reading the project**: see [v-mcp](../v-mcp/SKILL.md) to get declarations,\n  references and diagnostics without reading files, and to edit through an\n  AST-aware rename.\n- **Dependencies and modules**: `references/V-MOD.md` here covers the module\n  system; [vpm](https://vlang.io/vpm.html) documents what is on the registry.","AGENTS.md":"<!-- V_AGENTS.md  v1.1  2026-02-04 -->\n\n# V Repo Guide\n\nPractical quick reference for the V compiler, standard library, and tools.\nWritten for AI coding agents; useful for humans too.\n\n## Contents\n* Quick Start\n* Top Rules\n* Agent Rules\n* Safety (Do Not Brick the Repo)\n* Divergences From Repo Docs\n* Quick Decisions\n* Common Workflow\n* Reporting\n* Prerequisites\n* Build & Rebuild\n* Run Programs\n* Testing\n* Code Style\n* Modules and Imports\n* C/JS Interop Hygiene\n* Environment-Specific Code (files and `$if`)\n* Compile-Time Code and Reflection\n* Debug\n* Compiler Performance\n* Compiler Architecture\n* Key Directories\n* Test Locations\n* Error Reporting (checker/parser)\n* Option/Result Types\n* Tools\n* Commits and PRs\n* Environment Variables\n* Gotchas\n\n## Quick Start\nGet operational from the repo root in three steps:\n\n1. Build once (only if `./v` is missing): `make`\n2. Rebuild the working compiler in place: `./v self`\n3. Use `./v` for everything:\n   * Run a file: `./v run examples/hello_world.v`\n   * Run tests: `./v -silent test vlib/v/`\n   * Format: `./v fmt -w path/to/file.v`\n\nThen read Top Rules and Agent Rules before making changes.\n\n## Top Rules\n* Use `./v` for building, running, and testing.\n* Put all V flags immediately after `./v` and before the\n  subcommand/file, e.g. `./v -g run file.v`\n  (not `./v run file.v -g`); flags after the subcommand are passed\n  to that subcommand.\n* Rebuild `./v` after compiler or core module changes\n  (see Build & Rebuild).\n* Run the smallest relevant tests; see Testing for triggers and\n  minimums.\n* Ask before large refactors or wide file touches (see Agent Rules).\n* Do not stash or modify unrelated files unless explicitly instructed.\n* This guide assumes agents run locally, not in CI; CI notes are\n  informational only.\n* When a summary is required, include behavior change, tests run, and\n  touched file paths.\n* If instructions overlap, prefer Build & Rebuild, Testing, and\n  Reporting.\n* If duplicates drift, treat Build & Rebuild, Testing, and Reporting as\n  canonical and align other sections to them.\n\n## Agent Rules\n\n### Repo root\nAll commands assume the repo root as the working directory. The default\nlocation is `/opt/v`, but this may differ in your environment. If a\ncommand fails due to missing paths, verify with `pwd` and adjust\naccordingly. Use a per-command workdir only when a task requires a\nsubdir.\n\n### Tone and output\n* Be concise by default. If the user asks for depth, provide it while\n  keeping structure tight.\n* Keep output easy to scan: short sections, bullets when listing,\n  commands in backticks, no filler.\n* Use a strict, operational tone unless higher-priority instructions\n  override it.\n* Ask only when required. If information is missing, ask a direct\n  question.\n\n### File access and edit scope\n* You may read and edit all files in the V repo without asking for\n  permission. This is file access, not change scope; scope is\n  constrained below. Reading is always OK.\n  Edits to `ci/` or `Dockerfile*` still require an explicit ask.\n* Run build, test, and format commands without asking for permission.\n  These are validation steps, not code changes. Only ask about edit\n  scope, not about running `fmt`, targeted tests, or `check-md`.\n* Only modify files required for the user request; avoid unrelated\n  refactors. If duplication is harmful, small refactors to remove it\n  are OK only when needed. Only refactor duplication in code you are\n  already touching, and only when it directly supports the request or\n  fixes a bug there. \"Touching\" means files already modified for the\n  request.\n* Avoid unrelated file changes; call them out if present.\n* Avoid touching `thirdparty/` unless explicitly requested. If changes\n  are needed there, ask for approval before proceeding.\n\n### When to ask\n* Ask before large refactors or wide file touches across multiple repo-root\n  directories like `cmd/`, `vlib/`, `doc/`, and `examples/`.\n  Exception: docs-only changes across many files are OK without asking;\n  call them out in the summary.\n* Ask before large behavioral changes within a single subsystem or file,\n  even if the file count is small. Examples: changes to parser rules,\n  checker resolution, codegen output shape, diagnostic text/ordering, or\n  tool CLI behavior. Large means user-visible changes in CLI flags,\n  output, diagnostics, or codegen shape. If unsure whether a change is\n  \"large,\" ask.\n* Ask before touching `ci/` or `Dockerfile*` unless explicitly\n  requested. If changes are needed there, confirm whether local\n  validation is expected or if CI-only coverage is acceptable.\n\n### Bootstrap\n* Bootstrap/compiler usage rules: see Top Rules and Build & Rebuild.\n\n### Completeness and summaries\n* If you cannot complete a requested step, state the blocker and partial\n  progress (what was attempted and what remains).\n* After substantial work, provide a short summary and list touched file\n  paths. Substantial work means any behavioral change, or changes in\n  more than one file. Always include tests run (or \"Not run\" with\n  reason) in the summary.\n* Never change tests just to silence failures. Update expectations only\n  when behavior changes are intended, and note the rationale in the\n  summary.\n\n### New files and deeper guidance\n* New file checklist: format with `./v fmt -w`, add doc comments for\n  any public functions, run `./v check-md` for markdown files, and\n  keep Markdown lines <= 100 chars. Add or update tests when introducing\n  a new public API.\n* For deeper edge cases, consult `CONTRIBUTING.md` and `TESTS.md`.\n\n## Safety (Keep the Compiler Current)\n* Rebuild the working `./v` binary in place with `./v self` after compiler\n  changes. Overwriting `./v` is the intended workflow.\n* Keep generated C when debugging backend issues: add `-keepc`.\n* Avoid hidden/bidirectional Unicode characters in source/markdown\n  files.\n* If the compiler becomes unusable, recover with:\n  1. `git stash`\n  2. `make`\n  3. `git stash apply`\n  Check `git status` before stashing to avoid hiding unrelated work.\n  Do not stash unless explicitly instructed or the compiler is bricked.\n\n## Divergences From Repo Docs\nThe repo docs use `v` in examples. In this environment, use `./v` for all\nbuilds, runs, tests, and in-place self-rebuilds.\n* `TESTS.md` suggests `v test-all` before PRs; ask before running\n  `./v test-all`.\nThese overrides keep commands anchored to this checkout.\n\n## Quick Decisions\n* For rebuild and test choices, follow Build & Rebuild and Testing.\n* Follow Common Workflow for the default execution order.\n* For broader workflow guidance, see `CONTRIBUTING.md`.\n\n### Compact decision table (rebuild/tests)\nUse this table to pick the minimum rebuild/tests quickly. See Build &\nRebuild and Testing for full details and edge cases.\nCommands omit `./v` for brevity; assume the `./v` prefix.\nThis table is the minimum set; check Testing for additional triggers.\nREPL and backend changes have additional triggers in Testing.\nNote: `cmd/v/` is compiler scope; treat changes there as compiler\nchanges.\nWhen in doubt, ask before proceeding.\nIf you read only one section for tests, **read Testing**.\n\n| Change area | Rebuild? | Minimum tests to run |\n| --- | --- | --- |\n| Docs only (`.md`) | No | `check-md file.md` |\n| Compiler (`vlib/v/`, `cmd/v/`) | Yes | `-silent vlib/v/compiler_errors_test.v`; `test vlib/v/` |\n| Core modules (builtin/strings/os/strconv/time) | Yes | Smallest relevant tests |\n| Parser-only (`vlib/v/parser/`) | Yes | `test vlib/v/parser/` |\n| Checker-only (`vlib/v/types/`) | Yes | `test vlib/v/types/` |\n| Comptime (`vlib/v/types/`, `vlib/v/transform/`) | Yes | `test vlib/v/tests/`; comptime-related tests |\n| vlib (non-compiler) | No | Nearest `*_test.v` or `test vlib/path/` |\n| Tools (`cmd/tools/`) | No | Tool-specific test; else nearest `*_test.v` |\n| Diagnostic/output changes | Yes | `vlib/v/slow_tests/inout/compiler_test.v` |\n| C codegen (`vlib/v/gen/c/`) | Yes | `test vlib/v/gen/c/` |\n\n## Common Workflow\n0. Before work: `git status`; ensure `./v` exists; rebuild if needed.\n   If `./v` is missing, see Quick Start or Build & Rebuild.\n1. Edit the relevant files.\n2. If compiler sources or core modules changed, rebuild `./v` with `./v self`\n   (see Build & Rebuild).\n3. Format touched `.v`/`.vsh` files and run `./v check-md` on\n   touched markdown.\n4. Run the smallest relevant tests for the change scope (see Testing).\n\nSee Build & Rebuild for rebuild triggers and flags.\n\n## Reporting\n* When a summary is required (see Agent Rules), it must include:\n  * Behavior changes (or \"No behavior change\").\n  * Tests run (or \"Not run\" with a reason).\n  * Touched file paths.\n  * Note unrelated changes if present.\n  * Note doc updates if public behavior/tool output changed.\n* If public behavior or tool output changes, update relevant docs\n  (README.md, `doc/`, `tutorials/`) and note it.\n  For example: README.md for top-level CLI usage, `doc/` for\n  compiler/tool docs, `tutorials/` for learning material.\n* If you add or change a public API, update module docs or README and\n  note it. Public API includes stdlib functions/types and user-visible\n  compiler flags, diagnostics, tool CLI behavior (including `cmd/tools`\n  and stdlib CLI tools), or output formats.\n* Update `CHANGELOG.md` or `ROADMAP.md` only when explicitly requested.\n* Public behavior includes compiler output, diagnostics, user-facing\n  CLI, and stdlib API. This includes developer-facing flags, error\n  codes, or output ordering changes.\n* Internal refactors with no public behavior change do not require doc\n  updates.\n* If unsure whether behavior is public, ask the user before updating\n  docs.\n* Acceptable reasons for not running tests: docs-only change, no\n  relevant tests, or environment constraints. Be specific.\n* Docs-only changes: run `./v check-md file.md`; no other tests\n  required unless a test explicitly reads those docs.\n  No rebuild is needed unless compiler or core modules changed.\n* For docs-only, explicitly mention `check-md` in the summary.\n* If you update `.out` files, state the rationale in the summary.\n* Do not update `.out` files unless a behavior change is intended;\n  otherwise treat mismatches as regressions.\n* Example summary line: `Behavior change: none` or\n  `Behavior change: fixed X`.\n* Behavior change includes output, API, error messages, and test\n  expectations.\n* If blocked, note what was attempted and why it failed.\n* If tooling/network restrictions block a step, state the restriction\n  and the closest viable alternative.\n* Use `git status` to confirm touched files before reporting.\n\n## Prerequisites\n* Toolchain: `make`, `git`, and a C compiler (`clang` or `gcc`).\n* Windows: use `makev.bat` for the initial build.\n\n## Build & Rebuild\n* Initial build (only if `./v` is missing): `make`\n  (Windows: `makev.bat`).\n* Rebuild `./v` in place: `./v self`\n  * This intentionally overwrites the active compiler with one built from the\n    current checkout.\n* If `./v` is missing, run `make` first, then build `./v`.\n* This section is the source of truth for rebuild triggers.\n  If a rule appears elsewhere, defer to this section.\n* Rebuild triggers:\n  * Compiler sources in `vlib/v/` or `cmd/v/`.\n  * Core modules: `builtin`, `strings`, `os`, `strconv`, `time`.\n* If `./v` exists but compiler sources changed, still rebuild `./v`\n  before tests.\n* If unsure whether compiler/core changes happened, rebuild `./v`.\n* Common flags:\n  * `-g` debug info (V line numbers).\n  * `-cg` debug info (C line numbers); often combined with `-keepc`.\n  * `-keepc` keep generated C file(s).\n  * `-prod` optimized build.\n  * `-o file` output path/name.\n  * `-cc clang` pick a C compiler.\n  * `-cstrict` be stricter about the generated C.\n  * `-b js|native|wasm` select a backend.\n  * `-os <os>` cross-compile target selection (when supported).\n\n## Code Style\n* Comments: add succinct comments only when code is not self-explanatory.\n  Do not delete existing comments unless they are incorrect; you may fix\n  grammar or spelling without changing meaning.\n  Add V doc comments right before each new or modified public function\n  or method.\n  The V doc comments should start with the name of the fn,\n  example: `// the_name does ...`\n* Copy pasta: avoid copy pasta. If there's duplicate logic, move to a\n  function only when it is required for the request and within code you\n  are already touching.\n* Avoid using `unsafe{ code }` blocks where possible, and minimize\n  their scope. `unsafe` is acceptable for low-level interop (e.g. C\n  pointer casts, manual memory management) but should never wrap\n  ordinary V logic. When `unsafe` is required, keep the block as small\n  as possible and add a comment explaining why it is necessary.\n* Keep Markdown lines <= 100 chars (the checker is strict).\n  Apply to touched lines in modified Markdown files.\n* Avoid hidden/bidirectional Unicode characters in source/markdown\n  files.\n* Non-V files: keep existing formatting; only reformat if required by\n  the change.\n* Treat `.vv` files in `vlib/v/slow_tests/inout/` as fixtures; avoid\n  formatting unless a behavior change is intended and output\n  expectations are updated.\n* Formatting and check commands are in Tools.\n\n## Modules and Imports\n* Module names must match their directory name (no hierarchy in the\n  `module` line). Mismatches cause silent import failures.\n* Imports follow the folder hierarchy (e.g. `import abc.def`).\n\n## C/JS Interop Hygiene\n* Avoid using `C.` or `JS.` symbols in plain `.v` files.\n  * Use `.c.v` / `.js.v` files.\n  * Enable `-Wimpure-v` to catch accidental impurity.\n* Do not refactor existing `C.`/`JS.` uses in `.v` files unless\n  required. If you must change those lines, prefer moving the interop\n  code to `.c.v`/`.js.v`.\n* For C interop, use correct `const_` prefixes in C function\n  redeclarations when needed (helps with `-cstrict` and C static\n  analysis tooling).\n\n## Environment-Specific Code (files and `$if`)\nV supports environment-specific file suffixes. Prefer them when the\nwhole file is platform/backend specific.\n\nCommon patterns:\n* Backend splits:\n  * `*.c.v` (C backend), `*.js.v` (JS backend), `*.native.v` (native\n    backend), `*.wasm.v` (WASM backend)\n* OS splits:\n  * `*_windows.c.v`, `*_linux.c.v`, `*_nix.c.v`, with `*_default.c.v`\n    as fallback\n* Flag splits:\n  * `*_d_customflag.v` is included only with `-d customflag`\n  * `*_notd_customflag.v` is included only when that flag is NOT passed\n\nNotes:\n* Do not use combinatorial suffixes like `_d_flag_linux.c.v`; use\n  `_d_flag.v` plus `$if linux {}` inside the file.\n* For smaller platform differences, use compile-time `$if` blocks:\n  * `$if windows { ... } $else { ... }`\n* See Compile-Time Code and Reflection for the full `$if` reference.\n\n## Compile-Time Code and Reflection\nV uses `$` as a prefix for compile-time (comptime) operations. These\nare evaluated by the compiler, not at runtime. AI agents frequently\nconfuse comptime and runtime constructs; this section clarifies the\nboundaries.\n\n### Compile-time `$if`\n`$if` evaluates conditions at compile time. It is not a runtime `if`.\nUse it for platform, compiler, and custom-flag checks:\n* `$if windows { ... } $else $if linux { ... } $else { ... }`\n* `$if debug { ... }` (enabled by `-g` or `-cg`)\n* `$if prod { ... }` (enabled by `-prod`)\n* `$if custom_flag ? { ... }` (enabled by `-d custom_flag`)\n\nFull list of builtin `$if` options: see the table at\n<https://docs.vlang.io/conditional-compilation.html>.\n\nCommon mistakes:\n* Using runtime `if` where `$if` is needed for platform-specific code.\n  Runtime `if` will fail to compile if it references platform-specific\n  symbols; `$if` excludes the block entirely on non-matching platforms.\n* Forgetting the `?` suffix for custom flags: `$if myflag ? { ... }`.\n  Without `?`, the compiler treats it as a builtin option and silently\n  ignores it.\n\n### Compile-time `$for`\n`$for` iterates over type metadata at compile time. It works with:\n* `StructType.fields` - iterate struct fields\n* `StructType.methods` - iterate struct methods\n* `EnumType.values` - iterate enum values\n* `StructType.attributes` - iterate struct attributes\n* `SumType.variants` - iterate sum type variants\n* `method.params` - iterate method parameters\n\nInside `$for` blocks, use `$if` to branch on field/method types:\n```v oksyntax\nfn main() {\n\t$for field in MyStruct.fields {\n\t\t$if field.typ is string {\n\t\t\tprintln(field.name)\n\t\t}\n\t}\n}\n```\n\nAlso works with generics: `T.fields`, `param.fields` where\n`fn gen[T](param T) {}`.\n\nCommon mistakes:\n* Using runtime `for` to iterate struct fields. This does not work;\n  V has no runtime reflection. Always use `$for`.\n* Calling `obj.$method()` outside a `$for m in Type.methods` block.\n  The `$method()` call is only valid inside a comptime method\n  iteration.\n* Assuming comptime `$for` produces a runtime loop. It does not; the\n  compiler unrolls it into concrete code for each field/method/variant.\n\n### Compile-time functions\n\nOnly the following `$`-prefixed functions are supported:\n* `$embed_file('path')` - embed a file's contents into the binary.\n  Paths can be absolute, relative to the source file, or use pseudo\n  variables like `@VEXEROOT` or `@VMODROOT`.\n* `$tmpl('path')` - compile a V template file (used by veb).\n* `$env('VAR')` - read an environment variable at compile time.\n* `$d('ident', default)` - read a `-d ident=value` compile-time\n  define, with a default fallback.\n* `$res('path')` - embed a resource (Android).\n* `$compile_error('msg')` - emit a compile-time error.\n* `$compile_warn('msg')` - emit a compile-time warning.\n* `$pkgconfig('name')` - query pkg-config at compile time.\n* `$typeof(expr)` - compatibility alias for `typeof(expr)`.\n* `$sizeof(Type)` - compatibility alias for `sizeof(Type)`.\n* `$isreftype(expr)` - compatibility alias for `isreftype(expr)`.\n* `$__offsetof(Struct, field_name)` - compatibility alias for\n  `__offsetof(Struct, field_name)`.\n* `$dump(expr)` - compatibility alias for `dump(expr)`.\n\nCommon mistakes:\n* Prefer the unprefixed `typeof`, `sizeof`, `isreftype`, `__offsetof`,\n  and `dump` forms in new code. Their `$`-prefixed compatibility aliases\n  are also accepted.\n* Assuming `$embed_file` returns a string. It returns an\n  `EmbedFileData` object; use `.to_string()` or `.to_bytes()`.\n* Using `$env` where a runtime `os.getenv` is appropriate, or vice\n  versa. `$env` is baked in at compile time and cannot change at\n  runtime.\n\n### Compile-time pseudo variables\nThese are `@`-prefixed identifiers substituted at compile time:\n* `@FN` - current function name.\n* `@METHOD` - `ReceiverType.MethodName`.\n* `@MOD` - current module name.\n* `@STRUCT` - current struct name.\n* `@FILE`, `@DIR`, `@LINE`, `@COLUMN`, `@FILE_LINE` - source\n  location.\n* `@LOCATION` - file, line, and current type+method; good for logging.\n* `@VEXE`, `@VEXEROOT` - path to the V compiler and its directory.\n* `@VHASH`, `@VCURRENTHASH` - compiler commit hashes.\n* `@VMOD_FILE`, `@VMODHASH`, `@VMODROOT` - nearest `v.mod` info.\n* `@BUILD_DATE`, `@BUILD_TIME`, `@BUILD_TIMESTAMP` - build time (UTC).\n  Override with the `SOURCE_DATE_EPOCH` env var for reproducible\n  builds.\n* `@OS`, `@CCOMPILER`, `@BACKEND`, `@PLATFORM` - build environment.\n\n### Compile-time type groups\nComptime type groups combine multiple types into a higher-level type\nfor use in generic or comptime `$if` checks:\n* `$int` - all integer types\n* `$float` - all float types\n* `$array` - all array types\n* `$map` - all map types\n* `$struct` - all struct types\n* `$enum` - all enum types\n* `$alias` - all type aliases\n* `$sumtype` - all sum types\n* `$function` - all function types\n* `$interface` - all interface types\n* `$option` - all option types\n\nExample: `$if field.typ is $int { ... }`\n\n### Comptime changes and testing\nComptime logic lives primarily in `vlib/v/types/checker_comptime.v` and\n`vlib/v/transform/comptime.v`. Changes here require a rebuild of\n`./v` and should be tested with `./v -silent test vlib/v/tests/` plus\nany comptime-specific tests. See the decision table and Testing.\n\n## Run Programs\n* Compile and run: `./v run file.v`.\n* Just compile: `./v file.v` (creates executable).\n* With debug info: `./v -g run file.v`.\n* Debug run (C line numbers): `./v -keepc -cg run file.v`.\n* Example: `./v run examples/hello_world.v`.\n\n## Testing\nRun:\n* File (shows test output): `./v path/to/file_test.v`.\n* File (test runner report only): `./v test path/to/file_test.v`.\n* Dir: `./v -silent test path/to/dir/`.\n* Dir with statistics/metrics: `./v -stats test path/to/dir/`.\n* Compiler: `./v -silent vlib/v/compiler_errors_test.v`.\n* Fix outputs (only when intended):\n  `VAUTOFIX=1 ./v -silent vlib/v/compiler_errors_test.v`.\n* All: `./v test-all`.\n  Ask before running `./v test-all` unless explicitly requested.\n\nWhen:\n* Rule of thumb: for localized changes, run the smallest relevant\n  tests. If any trigger below matches, run the listed tests.\n* Minimum tests listed are the floor, not the ceiling; add targeted\n  tests for cross-cutting changes.\n* If unsure which tests apply, ask the user before proceeding.\n* If in doubt, prefer the smallest targeted test and ask.\n* Run all tests that apply. Start with the smallest targeted tests;\n  add slow tests as needed. Order does not matter.\n* Compiler changes (`vlib/v/` or `cmd/v/`):\n  Run `./v -silent vlib/v/compiler_errors_test.v`,\n  `./v -silent test vlib/v/`.\n* Parser-only changes (`vlib/v/parser/`):\n  Run `./v -silent test vlib/v/parser/`.\n* Checker-only changes (`vlib/v/types`):\n  Run `./v -silent test vlib/v/types/`.\n* vlib changes: Run nearest `*_test.v` or\n  `./v -silent test vlib/path/`.\n* Tool changes (`cmd/tools/`): Run tool-specific tests. If none exist,\n  run the smallest relevant `*_test.v` that exercises the tool.\n  Note: `cmd/v/` is compiler scope, not tools.\n  Examples: `cmd/tools/vfmt` -> `cmd/tools/vfmt_test.v` and\n  `vlib/v/gen/v/gen_test.v`.\n  `cmd/tools/vdoc` -> `cmd/tools/vdoc/vdoc_test.v`.\n* Diagnostic/output changes:\n  Run `./v -silent vlib/v/slow_tests/inout/compiler_test.v`.\n* C codegen changes: Run `./v -silent test vlib/v/gen/c/`.\n  Consider a stricter validation pass:\n  `./v -cstrict -cc clang -silent test vlib/v/`.\n* REPL changes: Run `./v -silent vlib/v/slow_tests/repl/repl_test.v`.\n* Broad refactors: Run `./v -silent test-all`.\n* Backend-specific changes: run the smallest relevant tests for the\n  affected backend. JS/native/WASM backends are incomplete, so avoid\n  broad `-b <backend> test vlib/` runs. Prefer targeted `*_test.v`\n  files or small test dirs with `-b js|native|wasm`.\n\nIf time-constrained, prioritize\n`./v -silent vlib/v/compiler_errors_test.v` and the smallest targeted tests.\nRun `vlib/v/slow_tests/inout/compiler_test.v` and the tests under\n`vlib/v/gen/c/` when output or codegen changes are likely.\nSee `TESTS.md` for more guidance on test selection and output\nexpectations.\nSee `CONTRIBUTING.md` for broader workflow guidance.\n\nConcrete triggers:\n* `vlib/v/slow_tests/inout/compiler_test.v` when error text or output\n  formatting changes, or changes in checker/parser error reporting.\n* `./v -silent test vlib/v/gen/c/` for changes under `vlib/v/gen/c/`\n  or C codegen output paths.\n\nTypes:\n* Standard: `*_test.v` files with `test_` functions.\n* Output: `.vv` source + `.out` expected output in\n  `vlib/v/slow_tests/inout/`.\n  Example: `./v -silent vlib/v/slow_tests/inout/compiler_test.v`.\n* `vlib/v/tests/**` may use `.run.out` expectations; run with\n  `./v -silent test vlib/v/tests`.\n\nDocs-only guidance: see Reporting.\nIf time-boxed, run at least the smallest relevant test and note\nskipped coverage in the summary.\n\n### Useful env variables and flags while testing\n* `VAUTOFIX=1` - Auto-update .out files when tests fail (run twice).\n  Use only when a behavior change is intended.\n* `VTEST_ONLY=glob_pattern` - Run only tests matching pattern.\n* `VTEST_HIDE_OK=1` - Hide successful tests, show only failures.\n* `./v -silent test path/to/dir/` - Show only failed tests (if any), and a summary report.\n* `-cc tcc` can speed test builds when TCC is available.\n* Output expectations: update `.out` files only when behavior changes\n  are intended; note the rationale in the summary.\n\n## Debug\n* See what the C compiler is doing:\n  * `-showcc` prints the C compile command.\n  * `-show-c-output` prints the C compiler output.\n* Build a temporary compiler for debug flags:\n  * `./v -o ./w -d trace_checker cmd/v`\n  * Then run: `./w file.v`\n* Keep and inspect generated C:\n  * `-keepc -cg` is the common combo.\n  * `-printfn <name> -o file.c` emits only the named C function to standard\n    output. The `name` uses the `modulename__fnname` format (e.g.\n    `main__main`). This flag can be repeated to print multiple\n    functions. Methods/generics may use more complex C names; use\n    `-keepc` to confirm exact symbols.\n* Trace stages:\n  `-d trace_scanner|trace_parser|trace_checker|trace_gen`.\n  These flags can help diagnose a problem when a stage stops earlier\n  than expected.\n* Time stages: `-d time_parsing|time_checking`.\n* V panics: `-keepc -g`.\n* C segfaults: `-keepc -cg -cc clang`.\n* JS/native/wasm: prefer small, focused test files with\n  `-b js|native|wasm` and `-show-c-output` where applicable; avoid\n  broad `-b <backend> test vlib/`.\n* Quick code location technique:\n  When debugging code generation issues, add unique comment tags to\n  relevant code generation points (e.g., `/*tom51*/`) in the compiler\n  source. Rebuild and generate C code, then search the generated file for these\n  tags to quickly map generated C code back to the exact compiler source\n  location. This is especially useful when multiple code paths generate\n  similar-looking output and you need to identify which path is actually used.\n  Example:\n  ```v\n  // In vlib/v/gen/c/stmt.v:\n  g.write('builtin___option_ok/*tom51*/(&(${styp}[]) { ')\n\n  // After rebuild, search generated C code for \"tom51\":\n  // builtin___option_ok/*tom51*/(&(int[]) { ... });\n  ```\n  Remember to remove these debug tags after fixing the issue.\n\n## Compiler Performance\n\nFor the fixed self-compilation benchmark, results, and agent regression policy, read\n[vself_speed.md](vself_speed.md). Every compiler fix must preserve or improve that efficiency;\ninvestigate instruction-count regressions before merging.\n\nUse **retired CPU instructions per fixed workload and per parsed source line** as the primary\ncompiler optimization metrics; lower is better. On macOS arm64,\n`/usr/bin/time -l` reports `instructions retired` from hardware counters. Under matched conditions,\nthis is the most reliable local measure of compiler work: it is less sensitive than elapsed time to\nCPU frequency and scheduling. Also retain elapsed time, cycles, and peak memory; fewer instructions\ndo not guarantee lower latency or memory use. Compare counts on the same machine and architecture.\n\nThe instruction counter is scoped to the measured process; unrelated processes do not add their\ninstructions to it. An idle host is not required for instruction comparisons. Counts are dynamic,\nnot a static property of the binary: synchronization, retries, and runtime paths can vary. Use\nserial paired measurements to check that variation. Record host load as context for elapsed time\nand cycles; use controlled load when making latency claims.\n\nFor instructions per line, divide by physical lines in the `.v` and `.vh` files actually parsed,\nincluding builtins, imports, blank lines, and comments. Collect `parsed .v lines` plus\n`parsed .vh lines` in a separate unmeasured `-v` run with the same input flags. Preserve that\ninventory and verify that both compilers process the same files and line count. Keep the fixture\nfixed: padding files or changing source complexity must not count as an optimization. Retain total\ninstructions alongside instructions/line; establish a new baseline when the fixture changes.\nInstructions per second depends on hardware, scheduling, and instruction mix; do not use it as a\ncompiler-efficiency score. See [vself_speed.md](vself_speed.md) for the self-compilation fixture.\n\nBuild a production compiler outside the measured run, then measure compilation to C:\n\n```sh\nenv -u VFLAGS -u VEXE VJOBS=1 SOURCE_DATE_EPOCH=0 V_MACOS_V3_NO_FALLBACK=1 \\\n  ./v -prod -cc clang self || exit 1\nbenchmark_out=$(mktemp -d \"${TMPDIR:-/tmp}/v-perf.XXXXXX\")\nenv -u VFLAGS -u VEXE VJOBS=1 SOURCE_DATE_EPOCH=0 V_MACOS_V3_NO_FALLBACK=1 \\\n  /usr/bin/time -l -o \"$benchmark_out/time.txt\" \\\n  ./v -nocache -o \"$benchmark_out/sha3.c\" /absolute/path/to/sha3.v \\\n  > \"$benchmark_out/stdout.txt\" 2> \"$benchmark_out/stderr.txt\"\nbenchmark_status=$?\nprintf '%s\\n' \"$benchmark_status\" > \"$benchmark_out/exit-status.txt\"\ncat \"$benchmark_out/time.txt\"\n```\n\nReplace the source path with the fixed benchmark fixture. Read the integer on the\n`instructions retired` line. The `-prod` above optimizes the compiler itself; the timed command uses\nthe normal input compilation mode. Keep input flags identical between baseline and candidate.\n`-o ...c` includes parsing, checking, transforms, and C generation, while excluding the external C\ncompiler and execution of the generated program. `-check-syntax` measures a different workload;\nlabel it separately and verify which stages that compiler version executes. `-nocache` disables\ncompiler caching; it does not flush the operating system's filesystem cache.\n`V_MACOS_V3_NO_FALLBACK=1` prevents silent legacy-compiler fallback when measuring V3 on macOS.\n\nFor a baseline/candidate comparison:\n\n1. Build both compilers with the same production build recipe and C toolchain. Verify correctness,\n   diagnostics, and generated C before measuring. For a change intended to preserve output, require\n   identical C, allowing only explicitly recorded build metadata differences. Validate generated\n   C when changing the fixture or compiler representation; matching invalid C is not sufficient.\n2. Use the same host, OS, fixture, standard library, flags, environment, and filesystem placement.\n   Keep executable, source, and output paths identical between roles where possible; record any\n   remaining differences. Set `VJOBS=1` and `SOURCE_DATE_EPOCH=0`, and disable tracing/profiling.\n   Clear unintended compiler environment overrides, including `VFLAGS` and `VEXE`; record the\n   effective environment. Measure only successfully rebuilt compilers, never a stale binary.\n   Run only one benchmark process at a time. Other builds or tests on the host do not invalidate\n   process instruction counts; do not pause unrelated work or wait for an idle host. Record that\n   elapsed time and cycles collected under uncontrolled load are contextual measurements.\n3. Run one warmup per role, then **ten measured pairs**, alternating baseline/candidate order:\n   AB, BA, AB, BA, and so on. Record all 22 runs, excluding both warmups from statistics.\n   Use a distinct timing/stdout/stderr log for every run and the same generated C output path.\n4. Require exit status zero and exactly one positive `instructions retired` counter in every sample.\n   Preserve failed or incomplete runs, but do not report them as a completed comparison. If the\n   counter is missing or zero, report it as unavailable; elapsed time is not an instruction count.\n5. Report each role's median instructions/line and total instructions, the fixed parsed-line count,\n   percentage change between medians, median paired instruction difference, and how many pairs\n   made the candidate worse. Report memory and elapsed time alongside instructions. Retain all\n   raw logs, exact commands/environment, compiler commits, binary hashes,\n   fixture hashes, and output verification so the result can be reproduced.\n\nDo not select the fastest sample or compare medians from different measurement sessions as proof of\na small improvement. Investigate inconsistent pair results before accepting an optimization. For\nend-to-end build latency or parallel scaling, measure that workload separately with its intended\nworker count and external C compiler, and state that scope explicitly.\n\n## Compiler Architecture\nThe V compiler has the following stages, orchestrated by `v.driver`:\n`v.scanner` -> `v.parser`/`v.flat` -> `v.types` -> `v.transform` ->\n`v.markused` -> `v.gen.c`.\nAdditional backends and lowerings include FastC, WebAssembly, SSA, MIR,\ninstruction selection, and native ARM64 generation.\n\n### Key Directories\n* `vlib/`: Standard library (changes here can affect the compiler\n  itself).\n* `cmd/v/v.v`: Compiler entry.\n* `cmd/tools/`: vfmt, vdoc, vup, vquest, etc.\n* `examples/`: Example programs.\n* `thirdparty/`: Bundled C libraries (tcc, mbedtls, sokol, etc.).\n* `vlib/v/`: Compiler modules.\n  * scanner/ - Tokenizer\n  * token/ - Token definitions\n  * parser/ - Produces a flat AST\n  * flat/ - Compact AST storage\n  * types/ - Name resolution and type checking\n  * transform/ - AST simplification\n  * markused/ - Dead code eliminator\n  * gen/c/ - C code generation (primary backend, known as cgen)\n  * gen/fastc/ - Scanner-to-C fast path\n  * gen/arm64/ - Native ARM64 backend\n  * gen/wasm/ - WebAssembly backend\n\n### Test Locations\n* `vlib/v/tests/`: Compiler feature tests (including comptime).\n* `vlib/v/slow_tests/`: Output-matching and slow tests for the\n  compiler.\n* `vlib/v/slow_tests/inout/` - Output comparison tests\n  (.vv + .out pairs)\n* `vlib/v/parser/tests/` - Parser error tests\n* `vlib/v/checker/tests/` - Checker error tests\n* `vlib/v/gen/c/` - C codegen unit and integration tests\n* `examples/compiletime/` - Comptime usage examples (including\n  `reflection.v`)\n\n## Error Reporting (checker/parser)\nScanner and parser diagnostics carry `v.token.Pos` spans. Type-checker\ndiagnostics are collected by `v.types.TypeChecker`; shared diagnostic\nformatting and message types live in `vlib/v/errors/`.\n\n## Option/Result Types\n* Syntax: `?Type` (optional, can be none) vs `!Type` (result, can\n  error).\n* Common bugs: Unwrapping in if guards, struct init with option fields,\n  ternaries with options.\n* When changing option/result behavior, add a regression test in\n  `vlib/v/tests/`.\n* Tests: Search `vlib/v/tests/` for option/result test files.\n* Pitfall: Options in ternaries, SQL statements, and fixed arrays need\n  special handling in cgen.\n\n## Tools\n* Note: if a rule overlaps with Testing, follow Testing.\n* Format: `./v fmt -w <file>` for touched `.v` and `.vsh` files.\n  Format only touched files unless explicitly asked to reformat broader\n  scope.\n* Treat new files as touched for formatting and markdown checks.\n* Check *all* files are formatted: `./v -silent test-fmt`.\n  Run only when asked or when validating the full tree.\n* Check markdown: `./v check-md file.md` for touched `.md` files\n  (required before commits).\n* Code style checker: `./v vet vlib/v`\n  Run only when asked or when making broad checker changes (more than\n  3 files in `vlib/v/types/`).\n* Module docs: `./v doc -readme -all -l module_name`.\n* Search: `rg pattern` (or `git grep`); list files: `rg --files`.\n* Auto-format hook: `./v git-fmt-hook install`.\n\n## Commits and PRs\n* See `CONTRIBUTING.md` for full commit message conventions and PR\n  workflow.\n* Keep commits focused: one logical change per commit.\n* If your branch has diverged and a rebase or merge is needed, do not\n  force-push or drop hunks without asking. State the conflict and let\n  the user decide the resolution strategy. When in doubt, prefer\n  `git rebase` over `git merge` for a linear history, but always ask\n  if the situation is ambiguous.\n\n## Environment Variables\n* VFLAGS: Pass flags to all V invocations\n  (e.g., `VFLAGS='-g' ./v test-all`).\n* VEXE: Path to V compiler executable (useful in CI/scripts).\n* TMPDIR: Controls where `.tmp.c` files are written (V uses\n  `TMPDIR/v/`).\n* SOURCE_DATE_EPOCH: Override build timestamp for reproducible builds\n  (affects `@BUILD_DATE`, `@BUILD_TIME`, `@BUILD_TIMESTAMP`).\n* V2-specific: `V2CC`, `V2CFLAGS`, `V2VERBOSE` (for v2 development).\n\n## Gotchas\n* Core modules (`builtin`, `strings`, `os`, `strconv`, `time`) can\n  affect the compiler because it is a V program. Rebuild `./v` when\n  they change.\n* Module names must match their directory name; mismatches cause silent\n  import failures. See Modules and Imports.\n* Some V programs and tools hardcode `os.execute('v ...')` in their\n  source. The `./v` workflow does not protect against these; if you\n  encounter unexpected behavior from such calls, check whether the\n  code is invoking the system `v` instead of `./v` and adjust\n  accordingly.\n* Stale `./v` can cause confusing failures; rebuild if behavior\n  seems off.\n* Output tests require exact matches; whitespace changes break tests.\n* Formatting reminders: follow Code Style for `fmt` and `check-md`.\n* C compilation errors? Check generated C with `-keepc`\n  (creates `/tmp/*.tmp.c`; see also TMPDIR in Environment Variables).\n* Compile-time code (`$if`, `$for`, `$embed_file`, etc.) is a common\n  source of AI mistakes. See Compile-Time Code and Reflection for\n  correct usage and pitfalls.\n* Broken compiler? See Safety for the recovery sequence.\n* Consult `CONTRIBUTING.md` and `TESTS.md` for:\n  * New tests or test infra changes.\n  * Output tests or `.out` updates.\n  * Broad refactors or compiler-wide changes.\n  * Unsure which slow test runner applies to your change.\n","cmd/tools/README.md":"# tools Documentation\n\nPath: `cmd/tools/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/README.md)","cmd/tools/fast/README.md":"# fast Documentation\n\nPath: `cmd/tools/fast/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/fast/README.md)","cmd/tools/scripting/README.md":"# scripting Documentation\n\nPath: `cmd/tools/scripting/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/scripting/README.md)","cmd/tools/testing/README.md":"# testing Documentation\n\nPath: `cmd/tools/testing/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/testing/README.md)","cmd/tools/vcreate/README.md":"# vcreate Documentation\n\nPath: `cmd/tools/vcreate/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vcreate/README.md)","cmd/tools/vgenerate/README.md":"# vgenerate Documentation\n\nPath: `cmd/tools/vgenerate/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vgenerate/README.md)","cmd/tools/vmcp/README.md":"# vmcp Documentation\n\nPath: `cmd/tools/vmcp/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vmcp/README.md)","cmd/tools/vpbgen/README.md":"# vpbgen Documentation\n\nPath: `cmd/tools/vpbgen/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vpbgen/README.md)","cmd/tools/vpm/README.md":"# vpm Documentation\n\nPath: `cmd/tools/vpm/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vpm/README.md)","cmd/tools/vskills/README.md":"# vskills Documentation\n\nPath: `cmd/tools/vskills/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vskills/README.md)","cmd/tools/vvet/README.md":"# vvet Documentation\n\nPath: `cmd/tools/vvet/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vvet/README.md)","examples/cpu_features/SSE_and_MMX_Extensions/README.md":"# SSE_and_MMX_Extensions Documentation\n\nPath: `examples/cpu_features/SSE_and_MMX_Extensions/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/examples/cpu_features/SSE_and_MMX_Extensions/README.md)","cmd/tools/vpm/test_utils/README.md":"# test_utils Documentation\n\nPath: `cmd/tools/vpm/test_utils/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vpm/test_utils/README.md)","vlib/x/async/tools/README.md":"# tools Documentation\n\nPath: `vlib/x/async/tools/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/x/async/tools/README.md)","vlib/x/executor/tools/README.md":"# tools Documentation\n\nPath: `vlib/x/executor/tools/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/x/executor/tools/README.md)","cmd/tools/vdoc/testdata/attributes/main.md":"# Attributes Skill\n\nPath: `cmd/tools/vdoc/testdata/attributes/main.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vdoc/testdata/attributes/main.md)","cmd/tools/vdoc/testdata/enum_helpers/main.md":"# Enum_helpers Skill\n\nPath: `cmd/tools/vdoc/testdata/enum_helpers/main.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vdoc/testdata/enum_helpers/main.md)","cmd/tools/vdoc/testdata/output_formats/README.md":"# output_formats Documentation\n\nPath: `cmd/tools/vdoc/testdata/output_formats/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vdoc/testdata/output_formats/README.md)","cmd/tools/vdoc/testdata/output_formats/main.md":"# Output_formats Skill\n\nPath: `cmd/tools/vdoc/testdata/output_formats/main.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vdoc/testdata/output_formats/main.md)","cmd/tools/vdoc/testdata/readme_in_project_root/README.md":"# readme_in_project_root Documentation\n\nPath: `cmd/tools/vdoc/testdata/readme_in_project_root/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vdoc/testdata/readme_in_project_root/README.md)","vlib/v/skills/v-concurrency/references/CHANNELS.md":"# References Skill\n\nPath: `vlib/v/skills/v-concurrency/references/CHANNELS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-concurrency/references/CHANNELS.md)","vlib/v/skills/v-concurrency/references/PATTERNS.md":"# References Skill\n\nPath: `vlib/v/skills/v-concurrency/references/PATTERNS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-concurrency/references/PATTERNS.md)","vlib/v/skills/v-lang/references/COMPTIME.md":"# References Skill\n\nPath: `vlib/v/skills/v-lang/references/COMPTIME.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-lang/references/COMPTIME.md)","vlib/v/skills/v-lang/references/MUTABILITY.md":"# References Skill\n\nPath: `vlib/v/skills/v-lang/references/MUTABILITY.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-lang/references/MUTABILITY.md)","vlib/v/skills/v-lang/references/OPTION-RESULT.md":"# References Skill\n\nPath: `vlib/v/skills/v-lang/references/OPTION-RESULT.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-lang/references/OPTION-RESULT.md)","vlib/v/skills/v-lang/references/SUMTYPES.md":"# References Skill\n\nPath: `vlib/v/skills/v-lang/references/SUMTYPES.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-lang/references/SUMTYPES.md)","vlib/v/skills/v-mcp/references/EDITING.md":"# References Skill\n\nPath: `vlib/v/skills/v-mcp/references/EDITING.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-mcp/references/EDITING.md)","vlib/v/skills/v-mcp/references/TOOLS.md":"# References Skill\n\nPath: `vlib/v/skills/v-mcp/references/TOOLS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-mcp/references/TOOLS.md)","vlib/v/skills/v-memory/references/OWNERSHIP.md":"# References Skill\n\nPath: `vlib/v/skills/v-memory/references/OWNERSHIP.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-memory/references/OWNERSHIP.md)","vlib/v/skills/v-memory/references/UNSAFE.md":"# References Skill\n\nPath: `vlib/v/skills/v-memory/references/UNSAFE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-memory/references/UNSAFE.md)","vlib/v/skills/v-package/references/SUBCOMMANDS.md":"# References Skill\n\nPath: `vlib/v/skills/v-package/references/SUBCOMMANDS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-package/references/SUBCOMMANDS.md)","vlib/v/skills/v-project/references/TEMPLATES.md":"# References Skill\n\nPath: `vlib/v/skills/v-project/references/TEMPLATES.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-project/references/TEMPLATES.md)","vlib/v/skills/v-testing/references/COVERAGE.md":"# References Skill\n\nPath: `vlib/v/skills/v-testing/references/COVERAGE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-testing/references/COVERAGE.md)","vlib/v/skills/v-testing/references/PATTERNS.md":"# References Skill\n\nPath: `vlib/v/skills/v-testing/references/PATTERNS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-testing/references/PATTERNS.md)","vlib/v/skills/v-testing/references/TROUBLESHOOTING.md":"# References Skill\n\nPath: `vlib/v/skills/v-testing/references/TROUBLESHOOTING.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-testing/references/TROUBLESHOOTING.md)","vlib/v/skills/v-veb/references/MIDDLEWARE.md":"# References Skill\n\nPath: `vlib/v/skills/v-veb/references/MIDDLEWARE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-veb/references/MIDDLEWARE.md)","vlib/v/skills/v-veb/references/ROUTES.md":"# References Skill\n\nPath: `vlib/v/skills/v-veb/references/ROUTES.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-veb/references/ROUTES.md)","vlib/v/skills/v-workflow/references/COMPILER-WORKFLOW.md":"# References Skill\n\nPath: `vlib/v/skills/v-workflow/references/COMPILER-WORKFLOW.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-workflow/references/COMPILER-WORKFLOW.md)","vlib/v/skills/v-workflow/references/FLAGS.md":"# References Skill\n\nPath: `vlib/v/skills/v-workflow/references/FLAGS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-workflow/references/FLAGS.md)","vlib/v/skills/v-workflow/references/V-MOD.md":"# References Skill\n\nPath: `vlib/v/skills/v-workflow/references/V-MOD.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-workflow/references/V-MOD.md)"},"files":{"vlib/v/skills/v-memory/SKILL.md":"---\nname: v-memory\ndescription: V's memory model - GC modes (-gc boehm, -gc none, -prealloc), the optional -ownership checking that catches use-after-move, what unsafe unlocks, and the C interop rules. Use when choosing a GC mode, when a program leaks or grows without bound, when deciding whether unsafe is worth it, when passing a V pointer to C, or when a move or borrow error appears. Does not cover general language rules (see v-lang), data structure choice (see v-lang's structs and maps section), profiling (see v-workflow), scripting a task in V (see v-scripts), or the wider command surface (see v-tools).\nlicense: MIT\n---\n\n# Memory in V\n\nThe default needs no thought: the Boehm collector traces and frees what is no\nlonger reachable. Every question below only arises when you leave that default.\n\n## Resource Routing\n\n- `references/OWNERSHIP.md` - Read when `-ownership` is in play, or when you want\n  a use-after-move caught at compile time rather than at 3am.\n- `references/UNSAFE.md` - Read before writing any `unsafe` block, and before\n  passing a V pointer into C.\n\n## Quick Reference\n\n| Situation | Mode |\n| --- | --- |\n| An ordinary program | default (Boehm GC) — do nothing |\n| A short-lived batch job, or a self-contained binary | `-gc none` |\n| A compiler or batch tool | `-prealloc` |\n| You want use-after-move caught at compile time | `-ownership` |\n| Talking to a C library | `unsafe`, and keep it in one place |\n| Tuning, before guessing | `-stats run` |\n\n**Default**: leave the GC alone. `-gc none` and `-prealloc` are not performance\nswitches; they remove the collector and move the responsibility to you.\n\n## The three modes\n\n```bash\nv run main.v                    # Boehm tracing GC\nv -gc none -prod main.v         # no collector at all\nv -prealloc -prod main.v        # arena allocation\n```\n\n| Mode | Frees | Cost |\n| --- | --- | --- |\n| Boehm (default) | unreachable memory | tracing pauses; memory is not returned promptly |\n| `-gc none` | nothing, unless you do | you must free; no cycles; no finalisers |\n| `-prealloc` | everything, at exit | only good for one-shot single-threaded work |\n\n`-gc none` means no cycle collection, so a reference cycle leaks. For a program\nthat runs once and exits — a compiler, a code generator, a batch script — that\ntrade is usually worth it.\n\n`-prealloc` is documented as suitable only for short-lived, single-threaded,\nbatch-like programs. Do not reach for it in a server.\n\n**Race builds are the exception to all of it.** `-race` does not work with `-gc\nnone` or `-prealloc`: ThreadSanitizer has to see every allocation and free, which\na collector or an arena hides. So a program you want to race-test is a program you\nmust run under the default GC. It also needs `clang` or `gcc` with the\nThreadSanitizer runtime, and prefers `clang`, because gcc does not instrument\ncopies of whole struct values such as strings and arrays, so it misses races on\nthem.\n\n## Check before you optimise\n\n```bash\nv -stats run main.v\n```\n\nThat reports allocations. `-gc none` on a program that allocates heavily and\nexits quickly can be a large win; on a long-running one it is a leak generator.\nMeasure first.\n\n## Ownership: catching a move at compile time\n\nV has an optional ownership system, off by default, enabled with `-ownership`. It\ntracks owned values and rejects using one after it has been moved:\n\n```v ignore\nfn main() {\n\ts1 := 'hello'.to_owned()\n\ts2 := s1          // s1 is moved into s2\n\tprintln(s1)       // error: use of moved value: `s1`\n}\n```\n\nStrings created with `.to_owned()` or `.clone()`, ordinary string slices, and the\n`Owned` / `Copy` / `Drop` struct markers participate. Literals and primitives are\nunaffected; arbitrary structs, maps and slices are not tracked as owned on their\nown. `doc/ownership.md` is the reference; `references/OWNERSHIP.md` here is the summary.\n\n```bash\nv -ownership file.v        # check and compile\n```\n\nIt also defines a `ownership` compile-time flag, so `$if ownership ? { ... }` and\n`*_d_ownership.v` files select the enabled behaviour.\n\n## unsafe\n\n`unsafe` exists for C interop and for a handful of low-level primitives. It is not\na way to make the compiler stop complaining.\n\nThe rule: one `unsafe` block, as small as possible, with a comment saying why it is\nsound. If a function needs two, split it so each has one.\n\n```v ignore\nimport unsafe\n\n// Fixed-size stack buffer; the size must be a compile-time constant.\npub fn (b &Buf) fill(c byte) {\n\tunsafe {\n\t\tb.data = char(c)\n\t\tb.len = b.size\n\t}\n}\n```\n\nSee `references/UNSAFE.md` for what it does not check and what that costs.\n\n## C interop\n\nV's own rule is the one to follow: avoid `C.` and `JS.` in plain `.v` files and put\nthe interop in `.c.v` or `.js.v` files. Then the compiler can tell which code is\nplatform-specific, and `-Wimpure-v` catches an interop call that escaped.\n\n```v ignore\n// buffer_windows.c.v\nmodule main\n\nfn windows_only() int {\n\tunsafe {\n\t\treturn C.GetLastError()\n\t}\n}\n```\n\nA V pointer handed to C must not be freed or moved by the caller, and the C side\nmust not retain it after the V object goes out of scope. See `references/UNSAFE.md`.\n\n## Validation\n\n```bash\nv -ownership file.v            # catch a move at compile time\nv -stats run main.v            # measure before changing the mode\nv -check main.v                # plain, portable V\n```\n\nFor library code add `-shared`. See [v-workflow](../v-workflow/SKILL.md).\n\n## Related Skills\n\n- **The language rules**: see [v-lang](../v-lang/SKILL.md) for `mut` and for when\n  to reach for a struct rather than a map, which changes how much is allocated.\n- **The build loop**: see [v-workflow](../v-workflow/SKILL.md) for `-prod`,\n  `-stats` and the other flags this skill uses.\n- **Concurrency**: see [v-concurrency](../v-concurrency/SKILL.md) for the shared\n  state whose lifetime a lock has to protect.\n","vlib/v/skills/v-project/SKILL.md":"---\nname: v-project\ndescription: Create V projects with v new or v init and choose a binary, library or web template.\nlicense: MIT\n---\n\n# Creating a V project\n\nUse this when starting a project, scaffolding a module, or choosing a template.\nFor building and testing, see `v-workflow`; for language rules, see `v-lang`; for\nother commands, see `v-tools`.\n\nTwo commands, one decision: `v new` makes a directory, `v init` works in the one\nyou are already in.\n\n## The decision\n\n- `v new <name>` — creates `<name>/` and puts the project in it.\n- `v init` — puts the project in the current directory. Use this when the\n  directory already exists.\n\nBoth take the same three template flags, and both create a `v.mod`.\n\n## Put template flags before the project name\n\nTemplate flags belong to the `new` or `init` subcommand. For `new`, put them\nafter `new` and before the project name:\n\n```bash\nv new myapp           # executable, the default\nv new --lib mylib     # library\nv new --web myapp     # veb web app\n```\n\n`v new mylib --lib` fails with *\"too many arguments\"*: flag parsing stops at the\nproject name, so the later flag is read as another argument.\n\n## What each template makes\n\n| Flag | Entry point | Module | Extra |\n| --- | --- | --- | --- |\n| `--bin` (default) | `main.v` | `main` | — |\n| `--lib` | `<name>.v` | `<name>` | `tests/<fn>_test.v` |\n| `--web` | `main.v` | `main` | `assets/main.css`, `templates/index.html` |\n\nAll three also create `v.mod`, `.editorconfig`, `.gitattributes` and\n`.gitignore`.\n\n## v.mod\n\n```\nModule {\n\tname: 'myapp'\n\tdescription: ''\n\tversion: '0.0.0'\n\tlicense: 'MIT'\n\tdependencies: []\n}\n```\n\nCreating one starts a prompt for the description, version and license. **The\nprompt only runs when stdin is a terminal.** With piped or redirected stdin,\nthe defaults are used. A non-interactive\n`v new` requires the project name; `v init` derives it from the current directory.\nFor example:\n\n```bash\nv new myapp < /dev/null\n```\n\nIf git is installed and the directory is not already a git project, `git init`\nruns as part of setup.\n\n## After it exists\n\n```bash\ncd myapp\nv run .          # build and run an executable or web project\nv -check .       # type-check only\nv test .         # run the tests\n```\n\nThe module name is `main` for executable and web templates. For a library, the\nmodule name is the project name.\n\n## Resource Routing\n\n- `references/TEMPLATES.md` - Read when choosing between `--bin`, `--lib` and\n  `--web`, or when a template's contents need checking against the current\n  compiler.\n\n## Quick Reference\n\n```bash\nv new <name>          # executable project in <name>/\nv new --lib <name>    # library project\nv new --web <name>    # veb project\nv init                # project in the current directory\nv init --lib          # library in the current directory\n```\n","vlib/v/skills/v-lang/SKILL.md":"---\nname: v-lang\ndescription: The V language rules that make code which looks right fail to compile or behave wrongly - ?T versus !T, sum types and match exhaustiveness, mut receivers, module names matching directories, comptime $ forms, and explicit type conversion. Use when writing or reviewing any V code that uses an Option, a Result, a sum type, a match, generics, or a custom flag, and when a V compiler error names an option, a result, exhaustiveness, mutability, or a module. Does not cover the build and test loop (see v-workflow), writing tests (see v-testing), reading a V project through the MCP server (see v-mcp), scripting in V (see v-scripts), or the wider command surface (see v-tools).\nlicense: MIT\n---\n\n# The V rules that bite\n\nMost mistakes an agent makes in V are not algorithmic. They are these rules, and\neach one fails in a way that does not look like the mistake.\n\n## Resource Routing\n\n- `references/OPTION-RESULT.md` - Read when deciding whether a function returns\n  `?T` or `!T`, or when an `or {}` block is not doing what you expected.\n- `references/SUMTYPES.md` - Read when modelling a closed set of states, or when\n  `match` reports a non-exhaustive match.\n- `references/COMPTIME.md` - Read when a symbol only exists on some platforms, or\n  when reaching for reflection or `$embed_file`.\n- `references/MUTABILITY.md` - Read when an assignment is rejected, or when a\n  mutation to a field does not reach the caller.\n\n## Quick Reference\n\n| You want | Write | Not |\n| --- | --- | --- |\n| A value that may be absent | `fn f() ?int` | `return 0` as a sentinel |\n| An operation that can fail | `fn f() !int` | `return -1` |\n| Unwrap, no default | `x or { return error('...') }` | `x or { 0 }` on a result |\n| Unwrap, with a default | `x or { 0 }` on an option | `x or { return }` on an option |\n| Branch on a closed set | `enum` + exhaustive `match` | a string constant |\n| Mutate an argument | `fn f(mut x T)` | `x = ...` silently failing |\n| Mutate the caller's struct | `fn (mut t &T)` | `fn (mut t T)` (a copy) |\n| Run code at compile time | `$if`, `$for` | a runtime `if` |\n\n## `?T` is not `!T`\n\n`?T` holds a value or `none`. `!T` holds a value or an error. They unwrap\ndifferently, and using one where the other belongs is a type error rather than a\nlogic error.\n\n```v ignore\n// An option: absence is part of the answer.\nfn find_port(cfg Config) ?int {\n    if !cfg.has_port {\n        return none\n    }\n    return cfg.port\n}\n\n// A result: the operation can fail, and the failure carries why.\nfn read_port(path string) !int {\n    lines := os.read_lines(path) or {\n        return error('cannot read ${path}: ${err.msg()}')\n    }\n    ...\n}\n```\n\nThe rule that catches people: inside `or { ... }` on a **result**, the block must\nproduce that result's error value, so it is `return error(...)` and not a bare\n`return`. On an **option**, `or { default }` supplies a value.\n\n**Default**: return `!T` for anything that touches the outside world, and `?T` for\na lookup that is legitimately absent. Full detail, including how to propagate and\nwhen a default is honest, in `references/OPTION-RESULT.md`.\n\n## Sum types and match\n\nV enums plus `match` are the way to model a closed set. `match` over an enum is\nchecked for exhaustiveness, which is the point: adding a variant turns every\nincomplete `match` into a compile error instead of a silent fallthrough.\n\nThe diagnostic is exact: `non-exhaustive match expression without `else``. Two\nways out — handle every variant, or add an `else` branch and accept that a new\nvariant changes nothing.\n\n```v ignore\nenum Status {\n    pending\n    running\n    done\n    failed\n}\n\nfn label(s Status) string {\n    return match s {\n        .pending { 'queued' }\n        .running { 'in progress' }\n        .done { 'finished' }\n        .failed { 'could not finish' }\n    }\n}\n```\n\nVariants are shared: `type Job = Status | string` is a sum type, and a `match`\nover it must handle both sides. See `references/SUMTYPES.md`.\n\n## mut is not optional\n\nFunction arguments are immutable. A receiver is immutable unless it is declared\n`mut`. This is the single most common V compile error, and it always has the same\ncause: the author assumed mutation was the default.\n\n```v ignore\n// Rejected: `app` is immutable.\n// fn serve(app App) { app.port = 8080 }\n\n// Accepted: the argument is mutable.\nfn serve(mut app App) { app.port = 8080 }\n\n// Accepted, and it reaches the caller: the receiver is a pointer.\nfn serve(app &App) { app.port = 8080 }\n\n// Accepted: the field itself is declared mutable.\nstruct Server {\nmut:\n    port int\n}\n```\n\n`mut` on a **value** receiver mutates a copy, so the caller's struct is\nunchanged. `mut` on a **pointer** receiver mutates the caller's. That difference\nis silent at the call site, which is why `references/MUTABILITY.md` exists.\n\n## Module names must match directories\n\n`module foo` must live in a directory named `foo`. A mismatch imports cleanly and\nthen resolves to nothing, with no error pointing at the name. The `module` line\ncarries no hierarchy — the directory nesting supplies it.\n\nThis is why a project built by `v new` has `src/main.v` with `module main` rather\nthan `module src`.\n\n## Comptime is `$`, and it excludes branches\n\nAnything starting with `$` runs while the compiler runs. The branches of an\n`$if` are **excluded entirely** on a platform that does not match, which is the\nonly reason a platform-specific import inside one is safe.\n\n```v ignore\n$if windows {\n    import sys.windows as wsys\n}\n$if linux {\n    import sys.linux\n}\n```\n\nA runtime `if` that mentions a platform-specific symbol will not compile on the\nother platforms, because there is no exclusion. Available in `$if`: `windows`,\n`linux`, `macos`, `js`, `freebsd`, `android`, `debug`, `prod`, and your own\n`$d custom_flag ? { ... }` — note the `?`.\n\n## No implicit conversions\n\n`int` is 32 bit and `i64` is 64. `f64` and `int` do not mix. Nothing converts\nimplicitly, which is deliberate: a silent narrowing is a bug you find later.\n\nBe explicit at the boundary, and prefer `strconv` over hand-rolled parsing.\n\n## Validation\n\nAfter changing V code, prove it rather than reading it again:\n\n```bash\nv -check path/to/file.v        # type-check only, no binary\nv fmt -verify path/to/file.v  # would the formatter change it?\n```\n\nFor library code rather than a `main` module, `-check` alone fails with *\"project\nmust include a `main` module\"*; use `v -check -shared path/to/file.v`.\n\nRun both before you report the change done. See `v-workflow` for the full loop.\n\n## Related Skills\n\n- **The build and test loop**: see [v-workflow](../v-workflow/SKILL.md) for\n  `v.mod`, dependencies, when `./v self` is required, and the flags that go\n  before the subcommand.\n- **Writing tests**: see [v-testing](../v-testing/SKILL.md) when the change needs\n  a `_test.v` file, or when `?T` and `!T` need asserting.\n- **Asking the compiler**: see [v-mcp](../v-mcp/SKILL.md) when you want the\n  declarations, references or diagnostics of a file without reading it, or when\n  you want to edit through an AST-aware rename.\n- **Memory and GC**: see [v-memory](../v-memory/SKILL.md) for GC modes, ownership\n  checking and `unsafe`.","vlib/v/skills/v-concurrency/SKILL.md":"---\nname: v-concurrency\ndescription: V's concurrency primitives - spawn, the sync module (channels, Mutex, RwMutex, WaitGroup, Once, Pool, select), and parallel.amap for parallel map and run. Use when writing code with goroutines, threads, channels, locks, shared mutable state, a WaitGroup, or a race, and when reviewing V code that touches them. Covers how to wait for spawned work and why a detached spawn is a bug. Does not cover the mutability rules a lock protects (see v-lang), the build and test loop (see v-workflow), web servers (see v-veb), scripting a task in V (see v-scripts), or the wider command surface (see v-tools).\nlicense: MIT\n---\n\n# Concurrency in V\n\nV has three levels: `parallel.amap` when you have a collection to process, `spawn`\nplus a `WaitGroup` when you have a fixed set of tasks, and `sync` channels and\nlocks when the tasks have to talk to each other.\n\n## Resource Routing\n\n- `references/CHANNELS.md` - Read when passing work or results between threads, or\n  when a producer must not block.\n- `references/PATTERNS.md` - Read for worker pools, fan-out, a single-writer\n  cache, and the shapes that deadlock.\n\n## Reach for these first\n\n| Situation | Use |\n| --- | --- |\n| Process a slice, collect results | `parallel.amap(items, worker)` |\n| Process a slice, discard results | `parallel.run(items, worker)` |\n| A fixed number of independent tasks | `spawn f()` plus `sync.WaitGroup` |\n| Hand work to a pool | a channel plus a fixed set of consumers |\n| Share mutable state | `sync.Mutex`, and keep the critical section small |\n| Initialise once, lazily | `sync.Once` |\n| Reuse objects | `sync.Pool` |\n| Wait for any of several channels | `select` |\n\nReaching for `spawn` when `amap` would do is the most common mistake. `spawn`\nmakes you wait for the work; `amap` does it for you.\n\n## parallel: do not hand-roll this\n\n```v ignore\nimport arrays.parallel\n\nfn test_it_doubles_every_number() {\n\titems := [1, 2, 3, 4]\n\tgot := parallel.amap(items, fn (n int) int {\n\t\treturn n * 2\n\t})\n\tassert got == [2, 4, 6, 8]\n}\n```\n\n`amap` preserves order and returns a slice; `run` discards results. Both take an\noptional `Params` whose `workers` defaults to `runtime.nr_jobs()`, which reads\n`VJOBS`.\n\nThis is worth preferring over a loop with a `WaitGroup` even when the loop is\nshort: there is no way to forget to wait.\n\n## spawn needs a WaitGroup\n\n`spawn` starts a thread and returns immediately. Nothing waits for it, so a\nprogram that ends, or a function that returns, can leave it mid-write.\n\n```v ignore\nimport sync\n\nfn fetch_all(urls []string) []string {\n\tmut wg := sync.new_waitgroup()\n\tmut results := []string{len: urls.len}\n\tfor url, i in urls {\n\t\twg.add(1)\n\t\tspawn fn () {\n\t\t\tresults[i] = fetch(url)\n\t\t\twg.done()\n\t\t}\n\t}\n\twg.wait()\n\treturn results\n}\n```\n\nEvery rule that matters here is easy to get wrong:\n\n- **`wg.add(1)` before the spawn, `wg.done()` inside it.** A `done()` without a\n  matching `add()` panics.\n- **All `wg.go()` / `spawn` before `wg.wait()`.** The doc comment says so\n  explicitly: adding after waiting is a race.\n- **`wait()` or nothing.** A detached spawn is a bug unless it is genuinely\n  fire-and-forget, and V is not a language where that is the default answer.\n- **Never let a spawned function panic.** It takes the whole process down.\n\n`sync.WaitGroup.go(f)` bundles the `add` and the thread start, which removes two\nof the four mistakes:\n\n```v ignore\nmut wg := sync.new_waitgroup()\nwg.go(fn () { work() })\nwg.wait()\n```\n\n## Sharing state needs a lock\n\nA struct shared between threads needs a `Mutex`, and the rule is to hold it for\nthe shortest possible span:\n\n```v ignore\nimport sync\n\nstruct Counter {\nmut:\n\tmu    sync.Mutex\n\ttotal int\n}\n\nfn (mut c Counter) add(n int) {\n\tlock c.mu {\n\t\tc.total += n\n\t}\n}\n```\n\nDo not hold a lock across a channel operation, a `spawn`, or anything that can\nblock. Two locks taken in different orders by two threads deadlock, so pick one\norder and hold to it.\n\n`RwMutex` is for a structure read far more often than it is written. If the write\nrate is not clearly lower, `Mutex` is simpler and fast enough.\n\n## Channels are the typed default\n\nA channel moves one value type between threads and gives you a buffer:\n\n```v ignore\nmut jobs := chan Job{cap: 16}\nmut results := chan Result{cap: 16}\n\nspawn fn () {\n\tfor job in jobs {\n\t\tresults <- run(job)\n\t}\n\tclose(results)\n}\n\nfor r in results {\n\tprintln(r)\n}\n```\n\nNote `chan Job{cap: 16}` — the capacity is part of the type, and a buffered channel\nlets a producer get ahead instead of blocking on every send.\n\nSee `references/CHANNELS.md` for closing, `select` and timeouts.\n\n## Atomics for counters\n\nFor a single value — a counter, a flag, a pointer — a lock is more than needed:\n\n```v ignore\nimport sync.stdatomic\n\nmut hits := stdatomic.new_atomic(0u64)\nhits.add(1u64)\ntotal := hits.load()\n```\n\nThe type is fixed when the atomic is created, and `load`, `store` and `add` need\n`mut`. There are also plain functions taking a pointer (`stdatomic.add_u64(&x, 1)`)\nfor the cases where an `AtomicVal` field is awkward.\n\nUse atomics for one or two values. The moment you need two of them to change\ntogether there is no transaction, and you want a `Mutex`:\n\n```v ignore\n// Two values that must stay consistent with each other.\nlock c.mu {\n    c.total += n\n    c.checks += 1\n}\n```\n\n## Validation\n\nConcurrency bugs are timing bugs, and reasoning about them is unreliable. Run\nunder the race detector and read what it reports:\n\n```bash\nv -race run main.v\nv -race test dir/\n```\n\nA clean race run does not prove the absence of races, but a reported one is\nreal. Never disable a report without explaining it. See `v-workflow` for the rest\nof the loop.\n\n## Related Skills\n\n- **Mutability**: see [v-lang](../v-lang/SKILL.md) for `mut` receivers and the\n  value-versus-pointer difference a lock protects.\n- **The build loop**: see [v-workflow](../v-workflow/SKILL.md) for `-race`,\n  `-prod` and the other flags.\n- **Web servers**: see [v-veb](../v-veb/SKILL.md) when the shared state is a\n  request counter and `veb` already gives you the locking idiom.","vlib/v/skills/v-package/SKILL.md":"---\nname: v-package\ndescription: \"Manage V packages: install, update, search, remove and link local modules.\"\nlicense: MIT\n---\n\n# V packages with vpm\n\nUse this when adding dependencies, resolving a missing package, or working with\n`v install`, `v update`, `v search` or `v link`. For building and testing, see\n`v-workflow`; for language rules, see `v-lang`.\n\nInvoke package commands as `v <subcommand>`, such as `v install <module>`.\nThe frontend dispatches these commands to the `vpm` tool; `v pm` is not a package\nsubcommand.\n\n```bash\nv install <module>   # install a package\nv update             # update the installed packages\nv outdated           # which installed packages have a newer version\nv list               # what is installed\nv remove <module>    # remove a package\nv show <module>      # information about a package\nv search <keyword>   # search vpm.vlang.io\nv why <module>       # why a module is in the dependency graph\nv link               # symlink the current project into the modules directory\nv unlink             # remove that symlink\nv upgrade            # upgrade all outdated modules\n```\n\n## Where modules go\n\n`~/.vmodules`, or `$VMODULES` when it is set. A module is stored under its name\nwith `.` replaced by the path separator, so `prantlf.json` becomes\n`~/.vmodules/prantlf/json`.\n\n## Installing\n\n`v install <module>` installs a package into the modules directory. It does not\nadd the package to the project's `v.mod`; add the dependency there explicitly.\nRun `v install` without package arguments from the project directory to install\nthe dependencies already declared in `v.mod`.\n\nGit packages include their file contents in the initial clone, so checkout can\ncomplete without a second network request for missing file blobs. Submodules are\ninstalled recursively.\n\n## Removing\n\n`v remove <module>` refuses two cases rather than guessing: a module outside the\nmodules directory, and one that was not installed by vpm. For a linked module the\nanswer is `v unlink` first.\n\n## Linking a local module\n\n`v link` symlinks the current project into the modules directory, so a change\nthere is seen without reinstalling. `v unlink` removes the symlink.\n\n## Resource Routing\n\n- `references/SUBCOMMANDS.md` - Read when a subcommand is missing, ignored, or\n  behaves differently from this summary.\n\n## Quick Reference\n\n```bash\nv install <module>\nv update\nv outdated\nv list\nv remove <module>\nv show <module>\nv search <keyword>\nv why <module>\nv link\nv unlink\nv upgrade\n```\n","vlib/v/skills/v-mcp/SKILL.md":"---\nname: v-mcp\ndescription: Using the V MCP server, `v mcp serve`, to read and change V code through the compiler itself - the AST, declarations, references, diagnostics, stdlib docs, an AST-aware rename, a guarded edit and the formatter. Use when an agent needs to know what a V file declares, whether V code compiles, where a symbol is used, or what a stdlib function takes, and to make a change safely across files. Does not cover the language rules themselves (see v-lang), the build and test loop (see v-workflow), writing test cases (see v-testing), scripting a task in V (see v-scripts), or the command surface outside the MCP server (see v-tools).\nlicense: MIT\n---\n\n# The V MCP server\n\n`v mcp serve` speaks MCP over stdio, or over HTTP with `--http`. It is the V\ncompiler exposed as tools: it calls the parser, the checker and the formatter **in\nprocess**, so it answers correctly about code that does not compile yet.\n\n```json\n{\n  \"mcpServers\": {\n    \"v\": { \"command\": \"v\", \"args\": [\"mcp\", \"serve\"] }\n  }\n}\n```\n\n## Resource Routing\n\n- `references/TOOLS.md` - Read for the arguments of a specific tool, or when you\n  need the tool that answers a particular question.\n- `references/EDITING.md` - Read before changing anything through the server. It\n  covers the guards and why each one exists.\n\n## Quick Reference\n\n| Question | Tool |\n| --- | --- |\n| What project is this? | `v_project_info` |\n| What modules can I import? | `v_modules` |\n| What files are here? | `v_files` |\n| What does this file declare? | `v_symbols` |\n| What is at the cursor? | `v_symbol_at` |\n| Where is this used? | `v_references` |\n| What does this stdlib call take? | `v_stdlib_doc` |\n| Does it compile? | `v_check` |\n| Do the tests pass? | `v_test_run` |\n| What are this app's routes? | `v_veb_routes` |\n| Rename a symbol | `v_rename_symbol` |\n| Change a known range | `v_edit_replace` |\n| Format a file | `v_format` |\n\n## A working order\n\n1. **`v_project_info` once per session.** It names the module, the compiler, the\n   root every path is relative to, and whether this is the V checkout itself.\n2. **`v_symbols`** to learn what a file declares. It carries the doc comments, so\n   it usually answers \"what is this file for\" without reading the body.\n3. **`v_check` after every change.** It is the only authoritative answer to \"does\n   it compile\", and it returns records with file, line and column.\n4. **`v_test_run`** for behaviour. `v_check` cannot catch a wrong result.\n\n## Prefer asking over reading\n\nThe whole point of the server is that it knows more than a file read does. Reach\nfor it before guessing:\n\n- a signature you are unsure of: `v_stdlib_doc`, not an invented one\n- whether an edit is safe: `v_references`, then `v_rename_symbol`\n- what a file contains: `v_symbols`, not a full read\n- what broke: `v_check`, not a re-read\n\nAn invented stdlib signature costs a compile cycle at best. A hand-written rename\nmisses the comment that mentions the name and the call in a file you never opened.\n\n## Editing is guarded by default\n\nThree tools write. None of them writes unless asked twice:\n\n- `v_rename_symbol` defaults to `dry_run: true` and lists every position it would\n  change.\n- `v_format` defaults to `write: false` and reports `before` and `after`.\n- `v_edit_replace` requires `expected_old`: you read the range and pass it back\n  verbatim, so it refuses to write over a change you did not see.\n\nRead the plan, then pass the flag that applies it. See `references/EDITING.md`.\n\n`--read-only` does not register these three at all, so a session started that way\ncannot write by accident.\n\n## Before guessing, ask\n\n- **A language rule**: the `v-lang` skill covers the parts agents get wrong, most\n  of all `?T` versus `!T` and comptime code.\n- **A build or module question**: the `v-workflow` skill, and `v_modules` for what\n  is importable right now.\n- **The code's shape**: `v_ast`, in the same JSON `v ast -p` prints.\n\n## When the tools cannot answer\n\n`v_run`, `v_test_run`, `v_doctor` and `v_eval` start the compiler. They are\nslower than the in-process tools and unavailable when the compiler cannot be\nstarted. A tool that could not start the compiler says so explicitly — it reports\n`started: false` with an error rather than an exit code, so a sandbox that cannot\nspawn processes is never mistaken for a failed check.\n\nIf you see that, the answer is about the environment, not about the code. Do not\nstart changing the code to satisfy a tool that never ran.\n\n## Validation\n\nThe server is the checker. Do not finish a change without it:\n\n```json\n{\"name\": \"v_check\", \"arguments\": {\"path\": \"src/main.v\"}}\n```\n\nThen format what you touched with `v_format`, and run the tests with\n`v_test_run`. See [v-workflow](../v-workflow/SKILL.md) for the equivalent shell\ncommands.\n\n## Related Skills\n\n- **The language rules**: see [v-lang](../v-lang/SKILL.md) for `?T` versus `!T`,\n  sum types, `mut` and comptime code.\n- **The build loop**: see [v-workflow](../v-workflow/SKILL.md) for `v.mod`,\n  dependencies, flags and when `./v self` is required.\n- **Tests**: see [v-testing](../v-testing/SKILL.md) for what to assert.\n- **Web apps**: see [v-veb](../v-veb/SKILL.md) for veb, and use `v_veb_routes` to\n  read the routes of an app you have not opened.","vlib/v/skills/v-scripts/SKILL.md":"---\nname: v-scripts\ndescription: Writing V as a scripting language - `.vsh` script mode, its implicit `import os` and unqualified `os` calls, running a script directly, the executable-cache contract that governs rebuilds, compiling a script without running it, and shebang scripts with no file extension. Use when a task is a build, release, maintenance or one-off automation job that would otherwise be bash, when deciding between a script and a small program, or when a `.vsh` file behaves differently from a `.v` file. Does not cover the language rules (see v-lang), the build and test loop (see v-workflow), writing test cases (see v-testing), or the wider command surface (see v-tools).\nlicense: MIT\n---\n\n# V scripts\n\nA file ending in `.vsh` is parsed in **script mode**. It is still V — the same\ntypes, the same modules, the same compiler — but two rules make it read like a\nshell script, and both are worth knowing before writing one, because they are\nwhy a script can fail in a way a program would not.\n\n## What script mode changes\n\n**`os` is imported for you.** A `.vsh` file gets an implicit `import os` when it\ndoes not already import `os` itself. If it does import `os` explicitly, that\nimport wins and keeps its own alias and selected symbols; the implicit one is\nnever added on top.\n\n**`os` functions are called unqualified.** In script mode an unqualified\n`read_file` resolves to `os.read_file`, so the usual `os.` prefix is optional:\n\n```v ignore\nprintln('hello')\ncontent := read_file('data.txt') or { panic(err) }\n```\n\nThat resolution reaches public `os` functions and constants (for example\n`args`). A local or parameter of the same name shadows it, so naming a variable\n`args` or `exists` changes what the bare name means — and that shadowing is\nscoped to the script, not to a `.v` file compiled beside it.\n\nNeither rule applies to a plain `.v` file. If a name resolves in a `.vsh` but not\nin a `.v` file, this is why.\n\n## Running and building scripts\n\nRunning the file runs the script; there is no separate interpreter to install.\n\n```sh\nv script.vsh                 # build if needed, then run\nv run script.vsh             # rebuild and run, keep no executable\nv build script.vsh           # compile to an executable, do not run it\nv -skip-running script.vsh   # the same thing\n```\n\nOn Unix-like systems a script that starts with this shebang runs by its path,\nonce it is executable (`chmod +x script.vsh`, then `./script.vsh`):\n\n```sh ignore\n#!/usr/bin/env -S v\n```\n\nFor `.vsh` scripts on systems whose `/usr/bin/env` has no `-S` (BusyBox,\nOpenBSD), put `cmd/tools/vrun` on the PATH and start the script with\n`#!/usr/bin/env vrun` instead; it runs the script with `v run`.\n\n`v script.vsh` follows **V's executable-cache contract**: it builds the script\nas `script` (the name without `.vsh`) next to the source and reuses that binary\non the next run, which is what makes repeated invocation cheap. It rebuilds when\nthe script is newer than the binary, or when an imported module changed. The\n`#!/usr/bin/env -S v` shebang goes through the same cache.\n\n`v run script.vsh` skips the cache: it rebuilds on every run and leaves no\nexecutable behind, and so do the `#!/usr/bin/env -S v run` and `vrun` shebangs.\nThe cached binary has to be deleted by hand: `v clean` handles `.v` files and\ndirectories, not `.vsh` scripts.\n\n## Scripts with no extension\n\nA file with a fully custom name and a shebang runs as a script when it starts\nwith:\n\n```sh ignore\n#!/usr/bin/env -S v -raw-vsh-tmp-prefix tmp\n```\n\n`tmp` is the prefix for the built executable, which is kept as\n`tmp.<filename>` beside the script. **Caution:** that name is overwritten if it\nalready exists. To rebuild every time instead of caching, use\n`#!/usr/bin/env -S v -raw-vsh-tmp-prefix tmp run`.\n\nThis runs in `crun` mode. V's own documentation recommends it for scripts that go\non the PATH and advises against it for build or deploy scripts.\n\n## Choosing between a script and a program\n\nReach for a script when the job is sequencing external tools, moving files,\nwatching a directory, or wrapping a build step. Reach for a program when there is\ndomain logic to test, when it needs a data structure, or when it will be imported.\n\nThe test suite exists to keep that boundary honest: if a `.vsh` has grown logic\nworth asserting on, move it into a `_test.v` alongside a real program. Script\nmode does not make a script untestable — it compiles like anything else.\n\n## Worked examples in the tree\n\nThese scripts rely on script mode — no `import os`, unqualified `os` calls,\nstatements at the top level — and are worth reading before writing one:\n\n- `examples/v_script.vsh` — creates, lists and removes files with `mkdir`, `ls`\n  and `rm`\n- `.github/problem-matchers/register_all.vsh` — reads `getenv`, then walks a\n  folder with `walk_ext`\n\nA larger `.vsh` can also be written like a program, with `import os` and a\n`fn main`. The scripts bundled with the skills are written that way, for example\n`vlib/v/skills/v-workflow/scripts/check.vsh`; their `#!/usr/bin/env -S v run`\nshebang leaves no binary in the skill folder.\n\n## Related Skills\n\n- **The commands around a script**: see [v-tools](../v-tools/SKILL.md) for\n  `v time`, `v retry`, `v repeat` and `v check-md`.\n- **The build and test loop**: see [v-workflow](../v-workflow/SKILL.md).\n- **The language rules**: see [v-lang](../v-lang/SKILL.md); a script is still V.","vlib/v/skills/v-testing/SKILL.md":"---\nname: v-testing\ndescription: Writing and running V tests - _test.v layout, test_ function naming, the assert builtin, fixtures and cleanup with os.vtmp_dir and defer, and the VTEST_ONLY and VTEST_ONLY_FN filters for a subset. Use when adding or changing a V test, when a test does not compile or does not run, when choosing what to assert, or when the suite is slow and only part of it is needed. Does not cover the rest of the build loop (see v-workflow), the option and result semantics a test has to assert (see v-lang), reading a project through the MCP server (see v-mcp), scripting in V (see v-scripts), or the wider command surface (see v-tools).\nlicense: MIT\n---\n\n# Writing and running V tests\n\nA V test is a `test_` prefixed function in a `_test.v` file. There is no test\nframework and no test object: the function takes no parameters, and `assert` is a\nbuiltin.\n\n## Resource Routing\n\n- `references/PATTERNS.md` - Read when writing a fixture, a table-driven test, or\n  anything that needs a temporary file and cleanup.\n- `references/TROUBLESHOOTING.md` - Read when a test does not compile, does not\n  run, or does not fail the way it should.\n- `references/COVERAGE.md` - Read when measuring which lines and functions a run\n  reached, or generating the tests that are missing.\n\n## Quick Reference\n\n| Need | Do |\n| --- | --- |\n| A test | `fn test_the_thing() { ... }` in `foo_test.v` |\n| Compare | `assert got == want, 'message'` |\n| Test an option | `assert f() == none` |\n| Test a result | `assert f() == none` — it fails with the error text |\n| A temporary file | `os.join_path(os.vtmp_dir(), 'name_${stamp}')` |\n| Cleanup | `defer { os.rm(path) or {} }` |\n| Only some files | `VTEST_ONLY='http' v test dir/` |\n| Only some functions | `VTEST_ONLY_FN='test_login' v test dir/` |\n| Hide passing tests | `v -silent test dir/` |\n| Timings | `v -stats test dir/` |\n| Line coverage | `v -no-skip-unused -coverage vcov/ test dir/` then `v cover vcov/` |\n| Untested functions | `v cover vcov/ --uncovered` |\n| Missing test bodies | `v cover vcov/ --write-tests --filter myapp/` |\n\n## Layout\n\n`foo.v` is tested by `foo_test.v` beside it. A test file holds only `test_`\nfunctions, imports, and helpers.\n\n```v ignore\n// foo_test.v\nimport foo\n\nfn test_it_adds() {\n\tassert add(2, 3) == 5\n}\n\nfn test_it_does_not_mutate_its_argument() {\n\tmut n := 3\n\tincrement(n)\n\tassert n == 3, 'increment changed its argument to ${n}'\n}\n```\n\n**A test function takes no parameters.** Not `*testing.T`, not a context. If you\nare porting a Go or Rust test, this is the first thing to change.\n\nA test file in a library directory does **not** declare `module main`. It imports\nwhat it needs, like any other file. Only a test file that is the `main` module of\nits project says `module main`.\n\n## Assertions say what they compared\n\nA bare `assert` failure prints `assertion failed` and a line number. That is not\nenough to act on.\n\n```v ignore\n// Weak: `assertion failed` and nothing else.\nassert lookup(users, id) == expected\n\n// Strong: the message is the difference.\ngot := lookup(users, id)\nassert got == expected, 'lookup(${id}) = ${got}, want ${expected}'\n```\n\nPut **got before want**. It reads in the order the mind works, and it makes a\nfailing suite scannable.\n\nAssert on values, never on message text, which is free to change:\n\n```v ignore\n// Brittle.\nassert read_config(x) == none\n\n// Correct: absence is the contract.\nif cfg := read_config(x) {\n    assert false, 'expected no config, got ${cfg}'\n}\n```\n\n## Running a subset\n\nThe full suite is slow. Filter it rather than paying for all of it:\n\n```bash\nVTEST_ONLY='http' v test dir/          # only files whose path contains http\nVTEST_ONLY_FN='test_login' v test dir/ # only functions matching\nv -silent test dir/                    # hide passing tests\nv -stats test dir/                     # add timings\n```\n\nBoth filters accept a comma-separated list. File filters use path fragments;\nfunction filters use glob patterns. If a filter matches nothing, the raw runner\nreports zero tests and exits successfully. The wrapper uses the normal reporter\nfor its child runs and preserves other `VFLAGS` options. It reports an empty\nselection as a failure, including when no filter is set:\n\n```bash\nv run scripts/run-tests.vsh dir/ --file http --fn 'test_login*'\n```\n\nSee `references/TROUBLESHOOTING.md` for other causes of tests not running.\n\n## Measuring coverage\n\n```bash\nv -no-skip-unused -coverage vcov/ test ./   # record what the run entered\nv cover vcov/                                # one percentage per file\n```\n\n`-no-skip-unused` is not optional. Without it a function nothing calls is\ndropped from the build, and the report cannot tell \"not tested\" from \"not\nbuilt\".\n\nLine coverage cannot name a function no test calls when that function's body\nholds no statement a line point would record. Two flags answer it:\n\n```bash\nv cover vcov/ --uncovered    # one `path:line function` per untested function\nv cover vcov/ --write-tests --filter myapp/   # the test bodies it is missing\n```\n\nThe generator writes a starting point, not a test — a call with zero values and\na `// TODO:` line, because it cannot know what the function should return. It\nappends to an existing `_test.v` and never touches a test function that is\nalready there. `--dry-run` shows the plan.\n\nSee [references/COVERAGE.md](references/COVERAGE.md) for the whole surface,\nincluding `--functions`, `--json` and what `--write-tests` refuses to do.\n\n## Fixtures\n\n\nThere is no test object to hang a fixture on, so a test writes to `os.vtmp_dir()`\nand registers the removal with `defer`. A unique name per test keeps parallel runs\nfrom colliding.\n\n```v ignore\nfn test_it_reads_a_config() {\n\tstamp := os.getpid()\n\tpath := os.join_path(os.vtmp_dir(), 'cfg_${stamp}.json')\n\tos.write_file(path, '{\"port\": 8080}') or {\n\t\tassert false, 'could not write ${path}: ${err.msg()}'\n\t}\n\tdefer {\n\t\tos.rm(path) or {}\n\t}\n\t...\n}\n```\n\nSee `references/PATTERNS.md` for table-driven tests, sub-suites and helpers.\n\n## Validation\n\nA test that has never failed is not known to test anything. Break it on purpose\nonce, watch it fail for the right reason, then put it back.\n\n```bash\nVTEST_ONLY_FN='test_the_new_thing' v test path/to/file_test.v\nv -silent test path/to/dir/\n```\n\nThen `v fmt -verify path/to/file_test.v`. See [v-workflow](../v-workflow/SKILL.md).\n\n## Related Skills\n\n- **The build loop**: see [v-workflow](../v-workflow/SKILL.md) for `-check`,\n  `fmt -verify`, `vet` and the flags that gate a change.\n- **Option and Result semantics**: see [v-lang](../v-lang/SKILL.md) for what to\n  assert when a function returns `?T` or `!T`.\n- **Through the MCP server**: see [v-mcp](../v-mcp/SKILL.md) when `v_test_run` can\n  report what passed and what the failures said, without parsing runner output.\n","vlib/v/skills/v-tools/SKILL.md":"---\nname: v-tools\ndescription: The parts of the V command surface that are easy to miss - inspecting code (`v ast`, `v where`, `v mod why`), shrinking a compiler error (`v reduce`), project hygiene (`v missdoc`, `v check-md`, `v bump`, `v git-fmt-hook`), embedding binaries (`v bin2v`), shell integration (`v complete`), timing and repeating commands (`v time`, `v repeat`, `v retry`), and compiler upkeep (`v up`, `v clean`, `v env`, `v tracev`). Use when a task is about the toolchain rather than the program, when reaching for `v` by habit and a better command exists, or when asked what V can do. Does not cover writing `.vsh` scripts (see v-scripts), the language rules (see v-lang), the build and test loop (see v-workflow), or reading a project through the MCP server (see v-mcp).\nlicense: MIT\n---\n\n# V tools\n\nMost of V's command surface is reached by habit. This is the part that is not:\ncommands that answer a question, shrink a failure, or keep a project honest.\n`v help topics` lists everything; the ones below are the ones worth knowing.\n\n## Look at code before changing it\n\n```sh\nv ast -p file.v         # print the AST as JSON; without -p it writes file.json\nv where fn NAME         # where a symbol is declared; also struct, method, enum, const\nv mod why MODULE        # which import chain pulls a module into the build\n```\n\n`v where` finds declarations only, and searches vlib as well as the project;\n`-mod main` keeps it to the project. Where a symbol is used is answered by the\n`v_references` tool of the MCP server.\n\n`v mod why` is the one that answers a question people otherwise guess at: nothing\nin `v.mod` records what a build actually reaches, so a dependency can outlive the\ncode that needed it.\n\nThrough the MCP server the same ground is covered with less shell work; see\nv-mcp.\n\n## Shrink a compiler error\n\n```sh\nv reduce -m 'text of the error' file.v   # the smallest source that still errors\n```\n\nWithout `-m`, `v reduce` only looks for a C compilation error, so a checker error\nsuch as `unknown function: nope` needs its text passed in. The reduction lands in\n`rpdc_<file>.v` (`rpdc_bad.v` for `bad.v`) in the current directory. Pair it with\n`v bug`, which opens a prefilled issue, and the report carries a minimal\nreproducer instead of the file you happened to be editing.\n\n## Measure test coverage\n\n```sh\nv -no-skip-unused -coverage vcov/ test ./   # record what the run entered\nv cover vcov/                               # one percentage per file\nv cover vcov/ -H                            # the hottest lines\nv cover vcov/ --lcov out.info               # the format CI tools read\nv cover vcov/ --uncovered                   # `path:line function` per untested function\nv cover vcov/ --functions                   # every function, covered or not\nv cover vcov/ --json report.json            # machine-readable, both levels\nv cover vcov/ --write-tests --filter dir/   # the test bodies it is missing\n```\n\nTwo steps: the run records, `v cover` reads. Delete the directory between runs —\nthe counters are appended, so a stale one reports coverage that no longer\nexists.\n\n`--write-tests` generates a `test_` function per untested function, beside its\nsource, as a starting point and not a test: a call with zero values and a\n`// TODO:` line. It appends to an existing `_test.v`, leaves a test function\nthat is already there alone, and refuses without `--filter`, because a coverage\ndirectory holds every file a run reached.\n\nSee [v-testing](../v-testing/SKILL.md) for the details.\n\n## Project hygiene\n\n```sh\nv missdoc src/          # public functions with no documentation comment\nv check-md README.md    # are the ```v blocks in this markdown correct?\nv bump --patch          # or --minor / --major; bumps the version in v.mod\nv git-fmt-hook install  # format on commit\n```\n\n`v check-md` is worth knowing about specifically: it compiles the V examples in a\nmarkdown file, which catches a snippet that stopped compiling long after anyone\nlooked at the prose. `v missdoc` takes `-p` to include private functions and `-t`\nto include function tags.\n\n## Embed binary files\n\n```sh\nv bin2v -m main -w assets.v assets/logo.png assets/font.ttf\n```\n\nWrites each file into `assets.v` as a byte-array constant (`logo_png`, with its\nlength in `logo_png_len`), so a program carries its images and fonts instead of\nreading them from disk at runtime. Without `-w` the source goes to stdout. The\nconstants are private, so generate them into the module that uses them. For a\nsingle file, `$embed_file('assets/logo.png')` does the same at compile time.\n\n## Shell integration\n\n```sh\nv complete setup bash   # print the completion setup script; bash, fish, zsh, powershell\n```\n\nLoad it from the shell's startup file; for bash, add\n`source /dev/stdin <<<\"$(v complete setup bash)\"` to `~/.bashrc`.\n\n## Time, repeat, retry\n\n```sh\nv time CMD              # how long CMD took, and what it exited with\nv repeat CMD...         # repeat commands and collect statistics\nv retry CMD             # rerun until it succeeds, for up to 10 retries or 10 minutes\n```\n\n`v retry` is the one to reach for when a flaky network call or a busy port is the\nthing in the way; `-r` and `-t` change its limits. `v time` is the one when the\nquestion is which of two approaches is actually cheaper.\n\n## Compiler upkeep\n\n```sh\nv up                    # update V itself\nv clean                 # remove what a default build leaves behind\nv env                   # the environment variables that steer the compiler\nv tracev                # a tracing build of the compiler\n```\n\n`v env` is the first thing to read when the compiler or one of its tools behaves\ndifferently on another machine, because most of that behaviour is an environment\nvariable. `v help build-c` documents the C-backend flags — `-cc`, `-glibc`,\n`-musl` — when the C toolchain itself is the thing in question.\n\n## Other commands worth knowing\n\n```sh\nv ls                    # install and run the V language server\nv repl                  # the V REPL\nv watch file.v          # rebuild when the sources it needs change\nv crun file.v           # build and run, keeping the executable\nv tool NAME             # run a tool module by name\nv share file.v          # send a .v file to the V Playground\nv download URL          # fetch a file over http or https\nv symlink               # put v on PATH\n```\n\n`v download -RD URL` also runs the downloaded file with `v run` and deletes it\nafterwards. That executes remote code, so read the file first.\n\nPackage management is `v search`, `v show`, `v list`, `v outdated`, `v update`,\n`v upgrade` and `v remove`, over the VPM registry.\n\n## The command that installs this skill\n\n```sh\nv skills list           # what is bundled, and what is installed\nv skills add v-tools    # install one\nv skills update         # refresh what a new V revision changed\n```\n\n## Related Skills\n\n- **Scripts**: see [v-scripts](../v-scripts/SKILL.md) for `.vsh` script mode and\n  its executable cache.\n- **The build and test loop**: see [v-workflow](../v-workflow/SKILL.md).\n- **The language rules**: see [v-lang](../v-lang/SKILL.md).\n- **Reading a project through the compiler**: see [v-mcp](../v-mcp/SKILL.md) for\n  declarations, references and diagnostics without shell commands.","vlib/v/skills/v-veb/SKILL.md":"---\nname: v-veb\ndescription: Writing V web applications with veb - the App and Context structs, route attributes with path parameters, ctx.html and ctx.json, templates with $tmpl and $veb.html, middleware with Middleware.use and route_use, shared state with shared and lock, and static file mounting. Use when creating or changing a veb server, adding a route or handler, returning JSON or HTML, adding middleware, or serving files. Does not cover concurrency in general (see v-concurrency), the language rules (see v-lang), the build loop (see v-workflow), scripting a task in V (see v-scripts), or the wider command surface (see v-tools).\nlicense: MIT\n---\n\n# veb, V's web framework\n\nA veb server is two structs and a `main`. Handlers are methods on `App`, and every\nrequest runs with the whole `App` shared across threads.\n\n## Resource Routing\n\n- `references/ROUTES.md` - Read when adding a route, a path parameter, or a\n  response body.\n- `references/MIDDLEWARE.md` - Read when adding cross-cutting behaviour, shared\n  state, or static files.\n\n## Quick Reference\n\n| Need | Do |\n| --- | --- |\n| A route | `@['/path']` above a `pub fn (mut app App) handler(mut ctx Context) veb.Result` |\n| A path parameter | `@['/users/:id']` and an extra `id string` argument |\n| A specific method | `@[post]` or `@['/upload'; post]` |\n| HTML | `return $veb.html()` or `return $veb.html('page.html')` |\n| JSON | `return ctx.json(payload)` |\n| Plain text | `return ctx.text('...')` |\n| Middleware | `app.Middleware.use(handler: f)` |\n| Middleware for a path | `app.Middleware.route_use('/admin/:path...', handler: f)` |\n| Shared state | `field shared T`, mutated inside `lock app.state { }` |\n| Serve files | `app.mount_static_folder_at(...)` |\n| Serve a folder at root | `app.handle_static(directory_path, root)` |\n\n## The shape of a server\n\n```v ignore\nimport veb\n\nconst port = 8080\n\npub struct App {}\n\nstruct Context {\n\tveb.Context\n}\n\nfn main() {\n\tmut app := &App{}\n\tveb.run[App, Context](mut app, port)\n}\n\n@['/']\npub fn (mut app App) index(mut ctx Context) veb.Result {\n\treturn ctx.text('hello')\n}\n\n@[post]\npub fn (mut app App) submit(mut ctx Context) veb.Result {\n\treturn ctx.json({ok: true})\n}\n```\n\nThree things to notice:\n\n- `App` is `pub` and `Context` embeds `veb.Context`. Both are type arguments to\n  `veb.run`, which is how veb knows your types.\n- A handler takes `mut ctx Context` and returns `veb.Result`. Return the result of\n  a `ctx` method rather than constructing one.\n- `mut app` in the signature means the method may mutate `App`. A read-only handler\n  takes `app &App`, and veb accepts that too.\n\n## Routes\n\nThe attribute is a list of paths. Without a leading `/`, it is an HTTP method with\nthe default path.\n\n```v ignore\n@['/users/:user']\npub fn (mut app App) user_endpoint(mut ctx Context, user string) veb.Result {\n\treturn ctx.json({user: user})\n}\n\n@[post]\npub fn (mut app App) create(mut ctx Context) veb.Result { ... }\n\n@['/upload'; post]\npub fn (mut app App) upload(mut ctx Context) veb.Result { ... }\n```\n\nA `:name` in the path becomes an argument **after** `ctx`. A `:name...` at the end\ncaptures the rest of the path.\n\nSee `references/ROUTES.md` for matching rules and ordering.\n\n## Shared state\n\n`App` is shared across request threads, so a mutable field needs to say so, and\nevery write needs a lock:\n\n```v ignore\npub struct App {\nmut:\n\tstate shared State\n}\n\nstruct State {\nmut:\n\thits int\n}\n\npub fn (mut app App) index(mut ctx Context) veb.Result {\n\tlock app.state {\n\t\tapp.state.hits++\n\t}\n\treturn ctx.text('${app.state.hits}')\n}\n```\n\n`shared` is what makes the field shared rather than per-request. Omitting it, or\nwriting without the `lock`, is a data race — and it is the mistake that matters\nmost in a veb app.\n\n## Middleware\n\nMiddleware wraps requests. Global for the app, or scoped to a path:\n\n```v ignore\nstruct App {\n\tveb.Middleware[Context]\n}\n\nfn main() {\n\tmut app := &App{}\n\t// Chained, evaluated in order.\n\tapp.Middleware.use(handler: other_func1)\n\tapp.Middleware.use(handler: add_headers)\n\t// Only for matching paths.\n\tapp.Middleware.route_use('/admin/:path...', handler: check_auth)\n\tveb.run[App, Context](mut app, http_port)\n}\n```\n\nA middleware handler takes `mut ctx Context` and returns a `bool`. Return `true` to\ncontinue the chain; return `false` to stop it, which is how a middleware short\ncircuits a request.\n\nSee `references/MIDDLEWARE.md`.\n\n## Templates\n\n`$veb.html()` renders the templates directory; `$veb.html('name.html')` renders one\nfile; `$tmpl` renders a string in memory, which is how a layout is composed with a\npage:\n\n```v ignore\n@['/']\npub fn (app &App) index(mut ctx Context) veb.Result {\n\tcontent := $tmpl('templates/index.html')\n\tbase := $tmpl('templates/base.html')\n\treturn ctx.html(base)\n}\n```\n\n## Validation\n\nA veb app is checked the same way as any V code, and `-check` catches most of it —\nincluding a handler whose signature does not match what veb expects:\n\n```bash\nv -check -shared path/to/veb_app.v     # library-shaped: needs -shared\nv fmt -verify path/to/veb_app.v\nv run path/to/main.v\n```\n\nSee [v-workflow](../v-workflow/SKILL.md) for the full loop.\n\n## Related Skills\n\n- **Concurrency**: see [v-concurrency](../v-concurrency/SKILL.md) for what\n  `shared` means and the lock discipline that goes with it.\n- **The build loop**: see [v-workflow](../v-workflow/SKILL.md) for `-check`,\n  `-shared`, `fmt -verify` and the other flags.\n- **The language rules**: see [v-lang](../v-lang/SKILL.md) for `mut` and for\n  `?T` versus `!T` when a handler has to return an error.","vlib/v/skills/v-workflow/SKILL.md":"---\nname: v-workflow\ndescription: How to build, type-check, format, vet, test and ship V code, and how the v.mod module system resolves imports. Use before declaring a V change finished, when a build or test command is needed, when a dependency will not resolve, when working in the V compiler's own source tree, or when a v.mod needs reading or writing. Covers flag placement, the difference between -check and a real build, and when ./v self is required. Does not cover the language rules themselves (see v-lang), writing test cases (see v-testing), working through the MCP server (see v-mcp), scripting a task in V (see v-scripts), or the wider command surface (see v-tools).\nlicense: MIT\n---\n\n# The V build loop\n\nV's compiler is fast and its checker is the authority. A V change is not finished\nuntil the checker, the formatter and the tests have all said so.\n\n## Resource Routing\n\n- `references/FLAGS.md` - Read when a flag is ignored or has to be moved, or when\n  choosing between `-prod`, `-g`, `-cflags` and `-ldflags`.\n- `references/V-MOD.md` - Read when a dependency will not resolve, or when\n  deciding between `v install`, `v link` and `v unlink`.\n- `references/COMPILER-WORKFLOW.md` - Read only when the change is inside the V\n  compiler's own tree, where the rules above are not enough.\n\n## Flags go before the subcommand\n\nThis is the first thing that goes wrong. Every compiler option comes **before**\nthe command; anything after it is passed to the command, not the compiler.\n\n```bash\nv -prod run main.v      # correct: -prod is a compiler option\nv run main.v -prod      # wrong: main.v receives -prod as an argument\n```\n\n`VFLAGS` sets options for every invocation, which is how CI pins a C compiler:\n\n```bash\nexport VFLAGS='-cc gcc'\n```\n\n## The loop\n\nRun these in order. Each one is cheaper than the one after it.\n\n```bash\nv -check path/to/file.v        # type-check only, no binary produced\nv fmt -verify path/to/file.v   # would the formatter change it?\nv -stats test path/to/file_test.v\nv vet -W path/to/file.v        # suspicious constructs, failures as errors\n```\n\nFor **library** code rather than a `main` module, `-check` and `vet` need\n`-shared`, or they fail with *\"project must include a `main` module\"*:\n\n```bash\nv -check -shared vlib/v/skills/\n```\n\n**Validation**: `v fmt -verify` and `v -check` are the two that gate everything\nelse. Run them on every file you touched before reporting the change done. A\ngreen test run does not imply a formatted file, and a formatted file does not\nimply it compiles.\n\n## Quick Reference\n\n| Question | Command |\n| --- | --- |\n| Does it type-check? | `v -check file.v` |\n| Is it formatted? | `v fmt -verify file.v` |\n| Format it | `v fmt -w file.v` |\n| Does it pass? | `v -stats test dir/` |\n| Only some tests | `VTEST_ONLY='pattern' v test file_test.v` |\n| Does the formatter agree with CI? | `v -silent test-fmt` |\n| Anything suspicious? | `v vet -W file.v` |\n| What does this symbol resolve to? | `v where Symbol` |\n| Does this still work? | `v doctor` |\n| Add a dependency | `v install module.name` |\n\n## Errors worth reading properly\n\nV's diagnostics carry a file, a line and a column, and the column is usually\nexact:\n\n```\nmain.v:12:5: error: unknown function: nope\n   12 |     nope()\n      |     ~~~~\n```\n\nThe caret span is the whole expression. Read the message first, then look at the\nspan — the span is frequently wider than you expect, because it covers the call\nrather than the name.\n\nDo not fix a V error by reading the file again. Fix it from the span.\n\n## The V compiler's own tree\n\nThe rules change when the change is inside the compiler itself. A compiler change\nmeans a rebuild before anything else, or you test a stale binary:\n\n```bash\n./v self                      # rebuild ./v from the current sources\n./v -silent test vlib/v/      # the compiler's own tests\n./v -silent vlib/v/compiler_errors_test.v\n```\n\n`vlib/v/` and `cmd/v/` changes additionally mean the test runner, the formatter\nand the language server should be tested too. See `references/COMPILER-WORKFLOW.md`\nfor the full list and what each one catches.\n\n## Related Skills\n\n- **The language rules**: see [v-lang](../v-lang/SKILL.md) when the compiler\n  rejects an option, a result, a match or an assignment.\n- **Writing tests**: see [v-testing](../v-testing/SKILL.md) for assertion forms,\n  fixtures and running a subset.\n- **Reading the project**: see [v-mcp](../v-mcp/SKILL.md) to get declarations,\n  references and diagnostics without reading files, and to edit through an\n  AST-aware rename.\n- **Dependencies and modules**: `references/V-MOD.md` here covers the module\n  system; [vpm](https://vlang.io/vpm.html) documents what is on the registry.","AGENTS.md":"<!-- V_AGENTS.md  v1.1  2026-02-04 -->\n\n# V Repo Guide\n\nPractical quick reference for the V compiler, standard library, and tools.\nWritten for AI coding agents; useful for humans too.\n\n## Contents\n* Quick Start\n* Top Rules\n* Agent Rules\n* Safety (Do Not Brick the Repo)\n* Divergences From Repo Docs\n* Quick Decisions\n* Common Workflow\n* Reporting\n* Prerequisites\n* Build & Rebuild\n* Run Programs\n* Testing\n* Code Style\n* Modules and Imports\n* C/JS Interop Hygiene\n* Environment-Specific Code (files and `$if`)\n* Compile-Time Code and Reflection\n* Debug\n* Compiler Performance\n* Compiler Architecture\n* Key Directories\n* Test Locations\n* Error Reporting (checker/parser)\n* Option/Result Types\n* Tools\n* Commits and PRs\n* Environment Variables\n* Gotchas\n\n## Quick Start\nGet operational from the repo root in three steps:\n\n1. Build once (only if `./v` is missing): `make`\n2. Rebuild the working compiler in place: `./v self`\n3. Use `./v` for everything:\n   * Run a file: `./v run examples/hello_world.v`\n   * Run tests: `./v -silent test vlib/v/`\n   * Format: `./v fmt -w path/to/file.v`\n\nThen read Top Rules and Agent Rules before making changes.\n\n## Top Rules\n* Use `./v` for building, running, and testing.\n* Put all V flags immediately after `./v` and before the\n  subcommand/file, e.g. `./v -g run file.v`\n  (not `./v run file.v -g`); flags after the subcommand are passed\n  to that subcommand.\n* Rebuild `./v` after compiler or core module changes\n  (see Build & Rebuild).\n* Run the smallest relevant tests; see Testing for triggers and\n  minimums.\n* Ask before large refactors or wide file touches (see Agent Rules).\n* Do not stash or modify unrelated files unless explicitly instructed.\n* This guide assumes agents run locally, not in CI; CI notes are\n  informational only.\n* When a summary is required, include behavior change, tests run, and\n  touched file paths.\n* If instructions overlap, prefer Build & Rebuild, Testing, and\n  Reporting.\n* If duplicates drift, treat Build & Rebuild, Testing, and Reporting as\n  canonical and align other sections to them.\n\n## Agent Rules\n\n### Repo root\nAll commands assume the repo root as the working directory. The default\nlocation is `/opt/v`, but this may differ in your environment. If a\ncommand fails due to missing paths, verify with `pwd` and adjust\naccordingly. Use a per-command workdir only when a task requires a\nsubdir.\n\n### Tone and output\n* Be concise by default. If the user asks for depth, provide it while\n  keeping structure tight.\n* Keep output easy to scan: short sections, bullets when listing,\n  commands in backticks, no filler.\n* Use a strict, operational tone unless higher-priority instructions\n  override it.\n* Ask only when required. If information is missing, ask a direct\n  question.\n\n### File access and edit scope\n* You may read and edit all files in the V repo without asking for\n  permission. This is file access, not change scope; scope is\n  constrained below. Reading is always OK.\n  Edits to `ci/` or `Dockerfile*` still require an explicit ask.\n* Run build, test, and format commands without asking for permission.\n  These are validation steps, not code changes. Only ask about edit\n  scope, not about running `fmt`, targeted tests, or `check-md`.\n* Only modify files required for the user request; avoid unrelated\n  refactors. If duplication is harmful, small refactors to remove it\n  are OK only when needed. Only refactor duplication in code you are\n  already touching, and only when it directly supports the request or\n  fixes a bug there. \"Touching\" means files already modified for the\n  request.\n* Avoid unrelated file changes; call them out if present.\n* Avoid touching `thirdparty/` unless explicitly requested. If changes\n  are needed there, ask for approval before proceeding.\n\n### When to ask\n* Ask before large refactors or wide file touches across multiple repo-root\n  directories like `cmd/`, `vlib/`, `doc/`, and `examples/`.\n  Exception: docs-only changes across many files are OK without asking;\n  call them out in the summary.\n* Ask before large behavioral changes within a single subsystem or file,\n  even if the file count is small. Examples: changes to parser rules,\n  checker resolution, codegen output shape, diagnostic text/ordering, or\n  tool CLI behavior. Large means user-visible changes in CLI flags,\n  output, diagnostics, or codegen shape. If unsure whether a change is\n  \"large,\" ask.\n* Ask before touching `ci/` or `Dockerfile*` unless explicitly\n  requested. If changes are needed there, confirm whether local\n  validation is expected or if CI-only coverage is acceptable.\n\n### Bootstrap\n* Bootstrap/compiler usage rules: see Top Rules and Build & Rebuild.\n\n### Completeness and summaries\n* If you cannot complete a requested step, state the blocker and partial\n  progress (what was attempted and what remains).\n* After substantial work, provide a short summary and list touched file\n  paths. Substantial work means any behavioral change, or changes in\n  more than one file. Always include tests run (or \"Not run\" with\n  reason) in the summary.\n* Never change tests just to silence failures. Update expectations only\n  when behavior changes are intended, and note the rationale in the\n  summary.\n\n### New files and deeper guidance\n* New file checklist: format with `./v fmt -w`, add doc comments for\n  any public functions, run `./v check-md` for markdown files, and\n  keep Markdown lines <= 100 chars. Add or update tests when introducing\n  a new public API.\n* For deeper edge cases, consult `CONTRIBUTING.md` and `TESTS.md`.\n\n## Safety (Keep the Compiler Current)\n* Rebuild the working `./v` binary in place with `./v self` after compiler\n  changes. Overwriting `./v` is the intended workflow.\n* Keep generated C when debugging backend issues: add `-keepc`.\n* Avoid hidden/bidirectional Unicode characters in source/markdown\n  files.\n* If the compiler becomes unusable, recover with:\n  1. `git stash`\n  2. `make`\n  3. `git stash apply`\n  Check `git status` before stashing to avoid hiding unrelated work.\n  Do not stash unless explicitly instructed or the compiler is bricked.\n\n## Divergences From Repo Docs\nThe repo docs use `v` in examples. In this environment, use `./v` for all\nbuilds, runs, tests, and in-place self-rebuilds.\n* `TESTS.md` suggests `v test-all` before PRs; ask before running\n  `./v test-all`.\nThese overrides keep commands anchored to this checkout.\n\n## Quick Decisions\n* For rebuild and test choices, follow Build & Rebuild and Testing.\n* Follow Common Workflow for the default execution order.\n* For broader workflow guidance, see `CONTRIBUTING.md`.\n\n### Compact decision table (rebuild/tests)\nUse this table to pick the minimum rebuild/tests quickly. See Build &\nRebuild and Testing for full details and edge cases.\nCommands omit `./v` for brevity; assume the `./v` prefix.\nThis table is the minimum set; check Testing for additional triggers.\nREPL and backend changes have additional triggers in Testing.\nNote: `cmd/v/` is compiler scope; treat changes there as compiler\nchanges.\nWhen in doubt, ask before proceeding.\nIf you read only one section for tests, **read Testing**.\n\n| Change area | Rebuild? | Minimum tests to run |\n| --- | --- | --- |\n| Docs only (`.md`) | No | `check-md file.md` |\n| Compiler (`vlib/v/`, `cmd/v/`) | Yes | `-silent vlib/v/compiler_errors_test.v`; `test vlib/v/` |\n| Core modules (builtin/strings/os/strconv/time) | Yes | Smallest relevant tests |\n| Parser-only (`vlib/v/parser/`) | Yes | `test vlib/v/parser/` |\n| Checker-only (`vlib/v/types/`) | Yes | `test vlib/v/types/` |\n| Comptime (`vlib/v/types/`, `vlib/v/transform/`) | Yes | `test vlib/v/tests/`; comptime-related tests |\n| vlib (non-compiler) | No | Nearest `*_test.v` or `test vlib/path/` |\n| Tools (`cmd/tools/`) | No | Tool-specific test; else nearest `*_test.v` |\n| Diagnostic/output changes | Yes | `vlib/v/slow_tests/inout/compiler_test.v` |\n| C codegen (`vlib/v/gen/c/`) | Yes | `test vlib/v/gen/c/` |\n\n## Common Workflow\n0. Before work: `git status`; ensure `./v` exists; rebuild if needed.\n   If `./v` is missing, see Quick Start or Build & Rebuild.\n1. Edit the relevant files.\n2. If compiler sources or core modules changed, rebuild `./v` with `./v self`\n   (see Build & Rebuild).\n3. Format touched `.v`/`.vsh` files and run `./v check-md` on\n   touched markdown.\n4. Run the smallest relevant tests for the change scope (see Testing).\n\nSee Build & Rebuild for rebuild triggers and flags.\n\n## Reporting\n* When a summary is required (see Agent Rules), it must include:\n  * Behavior changes (or \"No behavior change\").\n  * Tests run (or \"Not run\" with a reason).\n  * Touched file paths.\n  * Note unrelated changes if present.\n  * Note doc updates if public behavior/tool output changed.\n* If public behavior or tool output changes, update relevant docs\n  (README.md, `doc/`, `tutorials/`) and note it.\n  For example: README.md for top-level CLI usage, `doc/` for\n  compiler/tool docs, `tutorials/` for learning material.\n* If you add or change a public API, update module docs or README and\n  note it. Public API includes stdlib functions/types and user-visible\n  compiler flags, diagnostics, tool CLI behavior (including `cmd/tools`\n  and stdlib CLI tools), or output formats.\n* Update `CHANGELOG.md` or `ROADMAP.md` only when explicitly requested.\n* Public behavior includes compiler output, diagnostics, user-facing\n  CLI, and stdlib API. This includes developer-facing flags, error\n  codes, or output ordering changes.\n* Internal refactors with no public behavior change do not require doc\n  updates.\n* If unsure whether behavior is public, ask the user before updating\n  docs.\n* Acceptable reasons for not running tests: docs-only change, no\n  relevant tests, or environment constraints. Be specific.\n* Docs-only changes: run `./v check-md file.md`; no other tests\n  required unless a test explicitly reads those docs.\n  No rebuild is needed unless compiler or core modules changed.\n* For docs-only, explicitly mention `check-md` in the summary.\n* If you update `.out` files, state the rationale in the summary.\n* Do not update `.out` files unless a behavior change is intended;\n  otherwise treat mismatches as regressions.\n* Example summary line: `Behavior change: none` or\n  `Behavior change: fixed X`.\n* Behavior change includes output, API, error messages, and test\n  expectations.\n* If blocked, note what was attempted and why it failed.\n* If tooling/network restrictions block a step, state the restriction\n  and the closest viable alternative.\n* Use `git status` to confirm touched files before reporting.\n\n## Prerequisites\n* Toolchain: `make`, `git`, and a C compiler (`clang` or `gcc`).\n* Windows: use `makev.bat` for the initial build.\n\n## Build & Rebuild\n* Initial build (only if `./v` is missing): `make`\n  (Windows: `makev.bat`).\n* Rebuild `./v` in place: `./v self`\n  * This intentionally overwrites the active compiler with one built from the\n    current checkout.\n* If `./v` is missing, run `make` first, then build `./v`.\n* This section is the source of truth for rebuild triggers.\n  If a rule appears elsewhere, defer to this section.\n* Rebuild triggers:\n  * Compiler sources in `vlib/v/` or `cmd/v/`.\n  * Core modules: `builtin`, `strings`, `os`, `strconv`, `time`.\n* If `./v` exists but compiler sources changed, still rebuild `./v`\n  before tests.\n* If unsure whether compiler/core changes happened, rebuild `./v`.\n* Common flags:\n  * `-g` debug info (V line numbers).\n  * `-cg` debug info (C line numbers); often combined with `-keepc`.\n  * `-keepc` keep generated C file(s).\n  * `-prod` optimized build.\n  * `-o file` output path/name.\n  * `-cc clang` pick a C compiler.\n  * `-cstrict` be stricter about the generated C.\n  * `-b js|native|wasm` select a backend.\n  * `-os <os>` cross-compile target selection (when supported).\n\n## Code Style\n* Comments: add succinct comments only when code is not self-explanatory.\n  Do not delete existing comments unless they are incorrect; you may fix\n  grammar or spelling without changing meaning.\n  Add V doc comments right before each new or modified public function\n  or method.\n  The V doc comments should start with the name of the fn,\n  example: `// the_name does ...`\n* Copy pasta: avoid copy pasta. If there's duplicate logic, move to a\n  function only when it is required for the request and within code you\n  are already touching.\n* Avoid using `unsafe{ code }` blocks where possible, and minimize\n  their scope. `unsafe` is acceptable for low-level interop (e.g. C\n  pointer casts, manual memory management) but should never wrap\n  ordinary V logic. When `unsafe` is required, keep the block as small\n  as possible and add a comment explaining why it is necessary.\n* Keep Markdown lines <= 100 chars (the checker is strict).\n  Apply to touched lines in modified Markdown files.\n* Avoid hidden/bidirectional Unicode characters in source/markdown\n  files.\n* Non-V files: keep existing formatting; only reformat if required by\n  the change.\n* Treat `.vv` files in `vlib/v/slow_tests/inout/` as fixtures; avoid\n  formatting unless a behavior change is intended and output\n  expectations are updated.\n* Formatting and check commands are in Tools.\n\n## Modules and Imports\n* Module names must match their directory name (no hierarchy in the\n  `module` line). Mismatches cause silent import failures.\n* Imports follow the folder hierarchy (e.g. `import abc.def`).\n\n## C/JS Interop Hygiene\n* Avoid using `C.` or `JS.` symbols in plain `.v` files.\n  * Use `.c.v` / `.js.v` files.\n  * Enable `-Wimpure-v` to catch accidental impurity.\n* Do not refactor existing `C.`/`JS.` uses in `.v` files unless\n  required. If you must change those lines, prefer moving the interop\n  code to `.c.v`/`.js.v`.\n* For C interop, use correct `const_` prefixes in C function\n  redeclarations when needed (helps with `-cstrict` and C static\n  analysis tooling).\n\n## Environment-Specific Code (files and `$if`)\nV supports environment-specific file suffixes. Prefer them when the\nwhole file is platform/backend specific.\n\nCommon patterns:\n* Backend splits:\n  * `*.c.v` (C backend), `*.js.v` (JS backend), `*.native.v` (native\n    backend), `*.wasm.v` (WASM backend)\n* OS splits:\n  * `*_windows.c.v`, `*_linux.c.v`, `*_nix.c.v`, with `*_default.c.v`\n    as fallback\n* Flag splits:\n  * `*_d_customflag.v` is included only with `-d customflag`\n  * `*_notd_customflag.v` is included only when that flag is NOT passed\n\nNotes:\n* Do not use combinatorial suffixes like `_d_flag_linux.c.v`; use\n  `_d_flag.v` plus `$if linux {}` inside the file.\n* For smaller platform differences, use compile-time `$if` blocks:\n  * `$if windows { ... } $else { ... }`\n* See Compile-Time Code and Reflection for the full `$if` reference.\n\n## Compile-Time Code and Reflection\nV uses `$` as a prefix for compile-time (comptime) operations. These\nare evaluated by the compiler, not at runtime. AI agents frequently\nconfuse comptime and runtime constructs; this section clarifies the\nboundaries.\n\n### Compile-time `$if`\n`$if` evaluates conditions at compile time. It is not a runtime `if`.\nUse it for platform, compiler, and custom-flag checks:\n* `$if windows { ... } $else $if linux { ... } $else { ... }`\n* `$if debug { ... }` (enabled by `-g` or `-cg`)\n* `$if prod { ... }` (enabled by `-prod`)\n* `$if custom_flag ? { ... }` (enabled by `-d custom_flag`)\n\nFull list of builtin `$if` options: see the table at\n<https://docs.vlang.io/conditional-compilation.html>.\n\nCommon mistakes:\n* Using runtime `if` where `$if` is needed for platform-specific code.\n  Runtime `if` will fail to compile if it references platform-specific\n  symbols; `$if` excludes the block entirely on non-matching platforms.\n* Forgetting the `?` suffix for custom flags: `$if myflag ? { ... }`.\n  Without `?`, the compiler treats it as a builtin option and silently\n  ignores it.\n\n### Compile-time `$for`\n`$for` iterates over type metadata at compile time. It works with:\n* `StructType.fields` - iterate struct fields\n* `StructType.methods` - iterate struct methods\n* `EnumType.values` - iterate enum values\n* `StructType.attributes` - iterate struct attributes\n* `SumType.variants` - iterate sum type variants\n* `method.params` - iterate method parameters\n\nInside `$for` blocks, use `$if` to branch on field/method types:\n```v oksyntax\nfn main() {\n\t$for field in MyStruct.fields {\n\t\t$if field.typ is string {\n\t\t\tprintln(field.name)\n\t\t}\n\t}\n}\n```\n\nAlso works with generics: `T.fields`, `param.fields` where\n`fn gen[T](param T) {}`.\n\nCommon mistakes:\n* Using runtime `for` to iterate struct fields. This does not work;\n  V has no runtime reflection. Always use `$for`.\n* Calling `obj.$method()` outside a `$for m in Type.methods` block.\n  The `$method()` call is only valid inside a comptime method\n  iteration.\n* Assuming comptime `$for` produces a runtime loop. It does not; the\n  compiler unrolls it into concrete code for each field/method/variant.\n\n### Compile-time functions\n\nOnly the following `$`-prefixed functions are supported:\n* `$embed_file('path')` - embed a file's contents into the binary.\n  Paths can be absolute, relative to the source file, or use pseudo\n  variables like `@VEXEROOT` or `@VMODROOT`.\n* `$tmpl('path')` - compile a V template file (used by veb).\n* `$env('VAR')` - read an environment variable at compile time.\n* `$d('ident', default)` - read a `-d ident=value` compile-time\n  define, with a default fallback.\n* `$res('path')` - embed a resource (Android).\n* `$compile_error('msg')` - emit a compile-time error.\n* `$compile_warn('msg')` - emit a compile-time warning.\n* `$pkgconfig('name')` - query pkg-config at compile time.\n* `$typeof(expr)` - compatibility alias for `typeof(expr)`.\n* `$sizeof(Type)` - compatibility alias for `sizeof(Type)`.\n* `$isreftype(expr)` - compatibility alias for `isreftype(expr)`.\n* `$__offsetof(Struct, field_name)` - compatibility alias for\n  `__offsetof(Struct, field_name)`.\n* `$dump(expr)` - compatibility alias for `dump(expr)`.\n\nCommon mistakes:\n* Prefer the unprefixed `typeof`, `sizeof`, `isreftype`, `__offsetof`,\n  and `dump` forms in new code. Their `$`-prefixed compatibility aliases\n  are also accepted.\n* Assuming `$embed_file` returns a string. It returns an\n  `EmbedFileData` object; use `.to_string()` or `.to_bytes()`.\n* Using `$env` where a runtime `os.getenv` is appropriate, or vice\n  versa. `$env` is baked in at compile time and cannot change at\n  runtime.\n\n### Compile-time pseudo variables\nThese are `@`-prefixed identifiers substituted at compile time:\n* `@FN` - current function name.\n* `@METHOD` - `ReceiverType.MethodName`.\n* `@MOD` - current module name.\n* `@STRUCT` - current struct name.\n* `@FILE`, `@DIR`, `@LINE`, `@COLUMN`, `@FILE_LINE` - source\n  location.\n* `@LOCATION` - file, line, and current type+method; good for logging.\n* `@VEXE`, `@VEXEROOT` - path to the V compiler and its directory.\n* `@VHASH`, `@VCURRENTHASH` - compiler commit hashes.\n* `@VMOD_FILE`, `@VMODHASH`, `@VMODROOT` - nearest `v.mod` info.\n* `@BUILD_DATE`, `@BUILD_TIME`, `@BUILD_TIMESTAMP` - build time (UTC).\n  Override with the `SOURCE_DATE_EPOCH` env var for reproducible\n  builds.\n* `@OS`, `@CCOMPILER`, `@BACKEND`, `@PLATFORM` - build environment.\n\n### Compile-time type groups\nComptime type groups combine multiple types into a higher-level type\nfor use in generic or comptime `$if` checks:\n* `$int` - all integer types\n* `$float` - all float types\n* `$array` - all array types\n* `$map` - all map types\n* `$struct` - all struct types\n* `$enum` - all enum types\n* `$alias` - all type aliases\n* `$sumtype` - all sum types\n* `$function` - all function types\n* `$interface` - all interface types\n* `$option` - all option types\n\nExample: `$if field.typ is $int { ... }`\n\n### Comptime changes and testing\nComptime logic lives primarily in `vlib/v/types/checker_comptime.v` and\n`vlib/v/transform/comptime.v`. Changes here require a rebuild of\n`./v` and should be tested with `./v -silent test vlib/v/tests/` plus\nany comptime-specific tests. See the decision table and Testing.\n\n## Run Programs\n* Compile and run: `./v run file.v`.\n* Just compile: `./v file.v` (creates executable).\n* With debug info: `./v -g run file.v`.\n* Debug run (C line numbers): `./v -keepc -cg run file.v`.\n* Example: `./v run examples/hello_world.v`.\n\n## Testing\nRun:\n* File (shows test output): `./v path/to/file_test.v`.\n* File (test runner report only): `./v test path/to/file_test.v`.\n* Dir: `./v -silent test path/to/dir/`.\n* Dir with statistics/metrics: `./v -stats test path/to/dir/`.\n* Compiler: `./v -silent vlib/v/compiler_errors_test.v`.\n* Fix outputs (only when intended):\n  `VAUTOFIX=1 ./v -silent vlib/v/compiler_errors_test.v`.\n* All: `./v test-all`.\n  Ask before running `./v test-all` unless explicitly requested.\n\nWhen:\n* Rule of thumb: for localized changes, run the smallest relevant\n  tests. If any trigger below matches, run the listed tests.\n* Minimum tests listed are the floor, not the ceiling; add targeted\n  tests for cross-cutting changes.\n* If unsure which tests apply, ask the user before proceeding.\n* If in doubt, prefer the smallest targeted test and ask.\n* Run all tests that apply. Start with the smallest targeted tests;\n  add slow tests as needed. Order does not matter.\n* Compiler changes (`vlib/v/` or `cmd/v/`):\n  Run `./v -silent vlib/v/compiler_errors_test.v`,\n  `./v -silent test vlib/v/`.\n* Parser-only changes (`vlib/v/parser/`):\n  Run `./v -silent test vlib/v/parser/`.\n* Checker-only changes (`vlib/v/types`):\n  Run `./v -silent test vlib/v/types/`.\n* vlib changes: Run nearest `*_test.v` or\n  `./v -silent test vlib/path/`.\n* Tool changes (`cmd/tools/`): Run tool-specific tests. If none exist,\n  run the smallest relevant `*_test.v` that exercises the tool.\n  Note: `cmd/v/` is compiler scope, not tools.\n  Examples: `cmd/tools/vfmt` -> `cmd/tools/vfmt_test.v` and\n  `vlib/v/gen/v/gen_test.v`.\n  `cmd/tools/vdoc` -> `cmd/tools/vdoc/vdoc_test.v`.\n* Diagnostic/output changes:\n  Run `./v -silent vlib/v/slow_tests/inout/compiler_test.v`.\n* C codegen changes: Run `./v -silent test vlib/v/gen/c/`.\n  Consider a stricter validation pass:\n  `./v -cstrict -cc clang -silent test vlib/v/`.\n* REPL changes: Run `./v -silent vlib/v/slow_tests/repl/repl_test.v`.\n* Broad refactors: Run `./v -silent test-all`.\n* Backend-specific changes: run the smallest relevant tests for the\n  affected backend. JS/native/WASM backends are incomplete, so avoid\n  broad `-b <backend> test vlib/` runs. Prefer targeted `*_test.v`\n  files or small test dirs with `-b js|native|wasm`.\n\nIf time-constrained, prioritize\n`./v -silent vlib/v/compiler_errors_test.v` and the smallest targeted tests.\nRun `vlib/v/slow_tests/inout/compiler_test.v` and the tests under\n`vlib/v/gen/c/` when output or codegen changes are likely.\nSee `TESTS.md` for more guidance on test selection and output\nexpectations.\nSee `CONTRIBUTING.md` for broader workflow guidance.\n\nConcrete triggers:\n* `vlib/v/slow_tests/inout/compiler_test.v` when error text or output\n  formatting changes, or changes in checker/parser error reporting.\n* `./v -silent test vlib/v/gen/c/` for changes under `vlib/v/gen/c/`\n  or C codegen output paths.\n\nTypes:\n* Standard: `*_test.v` files with `test_` functions.\n* Output: `.vv` source + `.out` expected output in\n  `vlib/v/slow_tests/inout/`.\n  Example: `./v -silent vlib/v/slow_tests/inout/compiler_test.v`.\n* `vlib/v/tests/**` may use `.run.out` expectations; run with\n  `./v -silent test vlib/v/tests`.\n\nDocs-only guidance: see Reporting.\nIf time-boxed, run at least the smallest relevant test and note\nskipped coverage in the summary.\n\n### Useful env variables and flags while testing\n* `VAUTOFIX=1` - Auto-update .out files when tests fail (run twice).\n  Use only when a behavior change is intended.\n* `VTEST_ONLY=glob_pattern` - Run only tests matching pattern.\n* `VTEST_HIDE_OK=1` - Hide successful tests, show only failures.\n* `./v -silent test path/to/dir/` - Show only failed tests (if any), and a summary report.\n* `-cc tcc` can speed test builds when TCC is available.\n* Output expectations: update `.out` files only when behavior changes\n  are intended; note the rationale in the summary.\n\n## Debug\n* See what the C compiler is doing:\n  * `-showcc` prints the C compile command.\n  * `-show-c-output` prints the C compiler output.\n* Build a temporary compiler for debug flags:\n  * `./v -o ./w -d trace_checker cmd/v`\n  * Then run: `./w file.v`\n* Keep and inspect generated C:\n  * `-keepc -cg` is the common combo.\n  * `-printfn <name> -o file.c` emits only the named C function to standard\n    output. The `name` uses the `modulename__fnname` format (e.g.\n    `main__main`). This flag can be repeated to print multiple\n    functions. Methods/generics may use more complex C names; use\n    `-keepc` to confirm exact symbols.\n* Trace stages:\n  `-d trace_scanner|trace_parser|trace_checker|trace_gen`.\n  These flags can help diagnose a problem when a stage stops earlier\n  than expected.\n* Time stages: `-d time_parsing|time_checking`.\n* V panics: `-keepc -g`.\n* C segfaults: `-keepc -cg -cc clang`.\n* JS/native/wasm: prefer small, focused test files with\n  `-b js|native|wasm` and `-show-c-output` where applicable; avoid\n  broad `-b <backend> test vlib/`.\n* Quick code location technique:\n  When debugging code generation issues, add unique comment tags to\n  relevant code generation points (e.g., `/*tom51*/`) in the compiler\n  source. Rebuild and generate C code, then search the generated file for these\n  tags to quickly map generated C code back to the exact compiler source\n  location. This is especially useful when multiple code paths generate\n  similar-looking output and you need to identify which path is actually used.\n  Example:\n  ```v\n  // In vlib/v/gen/c/stmt.v:\n  g.write('builtin___option_ok/*tom51*/(&(${styp}[]) { ')\n\n  // After rebuild, search generated C code for \"tom51\":\n  // builtin___option_ok/*tom51*/(&(int[]) { ... });\n  ```\n  Remember to remove these debug tags after fixing the issue.\n\n## Compiler Performance\n\nFor the fixed self-compilation benchmark, results, and agent regression policy, read\n[vself_speed.md](vself_speed.md). Every compiler fix must preserve or improve that efficiency;\ninvestigate instruction-count regressions before merging.\n\nUse **retired CPU instructions per fixed workload and per parsed source line** as the primary\ncompiler optimization metrics; lower is better. On macOS arm64,\n`/usr/bin/time -l` reports `instructions retired` from hardware counters. Under matched conditions,\nthis is the most reliable local measure of compiler work: it is less sensitive than elapsed time to\nCPU frequency and scheduling. Also retain elapsed time, cycles, and peak memory; fewer instructions\ndo not guarantee lower latency or memory use. Compare counts on the same machine and architecture.\n\nThe instruction counter is scoped to the measured process; unrelated processes do not add their\ninstructions to it. An idle host is not required for instruction comparisons. Counts are dynamic,\nnot a static property of the binary: synchronization, retries, and runtime paths can vary. Use\nserial paired measurements to check that variation. Record host load as context for elapsed time\nand cycles; use controlled load when making latency claims.\n\nFor instructions per line, divide by physical lines in the `.v` and `.vh` files actually parsed,\nincluding builtins, imports, blank lines, and comments. Collect `parsed .v lines` plus\n`parsed .vh lines` in a separate unmeasured `-v` run with the same input flags. Preserve that\ninventory and verify that both compilers process the same files and line count. Keep the fixture\nfixed: padding files or changing source complexity must not count as an optimization. Retain total\ninstructions alongside instructions/line; establish a new baseline when the fixture changes.\nInstructions per second depends on hardware, scheduling, and instruction mix; do not use it as a\ncompiler-efficiency score. See [vself_speed.md](vself_speed.md) for the self-compilation fixture.\n\nBuild a production compiler outside the measured run, then measure compilation to C:\n\n```sh\nenv -u VFLAGS -u VEXE VJOBS=1 SOURCE_DATE_EPOCH=0 V_MACOS_V3_NO_FALLBACK=1 \\\n  ./v -prod -cc clang self || exit 1\nbenchmark_out=$(mktemp -d \"${TMPDIR:-/tmp}/v-perf.XXXXXX\")\nenv -u VFLAGS -u VEXE VJOBS=1 SOURCE_DATE_EPOCH=0 V_MACOS_V3_NO_FALLBACK=1 \\\n  /usr/bin/time -l -o \"$benchmark_out/time.txt\" \\\n  ./v -nocache -o \"$benchmark_out/sha3.c\" /absolute/path/to/sha3.v \\\n  > \"$benchmark_out/stdout.txt\" 2> \"$benchmark_out/stderr.txt\"\nbenchmark_status=$?\nprintf '%s\\n' \"$benchmark_status\" > \"$benchmark_out/exit-status.txt\"\ncat \"$benchmark_out/time.txt\"\n```\n\nReplace the source path with the fixed benchmark fixture. Read the integer on the\n`instructions retired` line. The `-prod` above optimizes the compiler itself; the timed command uses\nthe normal input compilation mode. Keep input flags identical between baseline and candidate.\n`-o ...c` includes parsing, checking, transforms, and C generation, while excluding the external C\ncompiler and execution of the generated program. `-check-syntax` measures a different workload;\nlabel it separately and verify which stages that compiler version executes. `-nocache` disables\ncompiler caching; it does not flush the operating system's filesystem cache.\n`V_MACOS_V3_NO_FALLBACK=1` prevents silent legacy-compiler fallback when measuring V3 on macOS.\n\nFor a baseline/candidate comparison:\n\n1. Build both compilers with the same production build recipe and C toolchain. Verify correctness,\n   diagnostics, and generated C before measuring. For a change intended to preserve output, require\n   identical C, allowing only explicitly recorded build metadata differences. Validate generated\n   C when changing the fixture or compiler representation; matching invalid C is not sufficient.\n2. Use the same host, OS, fixture, standard library, flags, environment, and filesystem placement.\n   Keep executable, source, and output paths identical between roles where possible; record any\n   remaining differences. Set `VJOBS=1` and `SOURCE_DATE_EPOCH=0`, and disable tracing/profiling.\n   Clear unintended compiler environment overrides, including `VFLAGS` and `VEXE`; record the\n   effective environment. Measure only successfully rebuilt compilers, never a stale binary.\n   Run only one benchmark process at a time. Other builds or tests on the host do not invalidate\n   process instruction counts; do not pause unrelated work or wait for an idle host. Record that\n   elapsed time and cycles collected under uncontrolled load are contextual measurements.\n3. Run one warmup per role, then **ten measured pairs**, alternating baseline/candidate order:\n   AB, BA, AB, BA, and so on. Record all 22 runs, excluding both warmups from statistics.\n   Use a distinct timing/stdout/stderr log for every run and the same generated C output path.\n4. Require exit status zero and exactly one positive `instructions retired` counter in every sample.\n   Preserve failed or incomplete runs, but do not report them as a completed comparison. If the\n   counter is missing or zero, report it as unavailable; elapsed time is not an instruction count.\n5. Report each role's median instructions/line and total instructions, the fixed parsed-line count,\n   percentage change between medians, median paired instruction difference, and how many pairs\n   made the candidate worse. Report memory and elapsed time alongside instructions. Retain all\n   raw logs, exact commands/environment, compiler commits, binary hashes,\n   fixture hashes, and output verification so the result can be reproduced.\n\nDo not select the fastest sample or compare medians from different measurement sessions as proof of\na small improvement. Investigate inconsistent pair results before accepting an optimization. For\nend-to-end build latency or parallel scaling, measure that workload separately with its intended\nworker count and external C compiler, and state that scope explicitly.\n\n## Compiler Architecture\nThe V compiler has the following stages, orchestrated by `v.driver`:\n`v.scanner` -> `v.parser`/`v.flat` -> `v.types` -> `v.transform` ->\n`v.markused` -> `v.gen.c`.\nAdditional backends and lowerings include FastC, WebAssembly, SSA, MIR,\ninstruction selection, and native ARM64 generation.\n\n### Key Directories\n* `vlib/`: Standard library (changes here can affect the compiler\n  itself).\n* `cmd/v/v.v`: Compiler entry.\n* `cmd/tools/`: vfmt, vdoc, vup, vquest, etc.\n* `examples/`: Example programs.\n* `thirdparty/`: Bundled C libraries (tcc, mbedtls, sokol, etc.).\n* `vlib/v/`: Compiler modules.\n  * scanner/ - Tokenizer\n  * token/ - Token definitions\n  * parser/ - Produces a flat AST\n  * flat/ - Compact AST storage\n  * types/ - Name resolution and type checking\n  * transform/ - AST simplification\n  * markused/ - Dead code eliminator\n  * gen/c/ - C code generation (primary backend, known as cgen)\n  * gen/fastc/ - Scanner-to-C fast path\n  * gen/arm64/ - Native ARM64 backend\n  * gen/wasm/ - WebAssembly backend\n\n### Test Locations\n* `vlib/v/tests/`: Compiler feature tests (including comptime).\n* `vlib/v/slow_tests/`: Output-matching and slow tests for the\n  compiler.\n* `vlib/v/slow_tests/inout/` - Output comparison tests\n  (.vv + .out pairs)\n* `vlib/v/parser/tests/` - Parser error tests\n* `vlib/v/checker/tests/` - Checker error tests\n* `vlib/v/gen/c/` - C codegen unit and integration tests\n* `examples/compiletime/` - Comptime usage examples (including\n  `reflection.v`)\n\n## Error Reporting (checker/parser)\nScanner and parser diagnostics carry `v.token.Pos` spans. Type-checker\ndiagnostics are collected by `v.types.TypeChecker`; shared diagnostic\nformatting and message types live in `vlib/v/errors/`.\n\n## Option/Result Types\n* Syntax: `?Type` (optional, can be none) vs `!Type` (result, can\n  error).\n* Common bugs: Unwrapping in if guards, struct init with option fields,\n  ternaries with options.\n* When changing option/result behavior, add a regression test in\n  `vlib/v/tests/`.\n* Tests: Search `vlib/v/tests/` for option/result test files.\n* Pitfall: Options in ternaries, SQL statements, and fixed arrays need\n  special handling in cgen.\n\n## Tools\n* Note: if a rule overlaps with Testing, follow Testing.\n* Format: `./v fmt -w <file>` for touched `.v` and `.vsh` files.\n  Format only touched files unless explicitly asked to reformat broader\n  scope.\n* Treat new files as touched for formatting and markdown checks.\n* Check *all* files are formatted: `./v -silent test-fmt`.\n  Run only when asked or when validating the full tree.\n* Check markdown: `./v check-md file.md` for touched `.md` files\n  (required before commits).\n* Code style checker: `./v vet vlib/v`\n  Run only when asked or when making broad checker changes (more than\n  3 files in `vlib/v/types/`).\n* Module docs: `./v doc -readme -all -l module_name`.\n* Search: `rg pattern` (or `git grep`); list files: `rg --files`.\n* Auto-format hook: `./v git-fmt-hook install`.\n\n## Commits and PRs\n* See `CONTRIBUTING.md` for full commit message conventions and PR\n  workflow.\n* Keep commits focused: one logical change per commit.\n* If your branch has diverged and a rebase or merge is needed, do not\n  force-push or drop hunks without asking. State the conflict and let\n  the user decide the resolution strategy. When in doubt, prefer\n  `git rebase` over `git merge` for a linear history, but always ask\n  if the situation is ambiguous.\n\n## Environment Variables\n* VFLAGS: Pass flags to all V invocations\n  (e.g., `VFLAGS='-g' ./v test-all`).\n* VEXE: Path to V compiler executable (useful in CI/scripts).\n* TMPDIR: Controls where `.tmp.c` files are written (V uses\n  `TMPDIR/v/`).\n* SOURCE_DATE_EPOCH: Override build timestamp for reproducible builds\n  (affects `@BUILD_DATE`, `@BUILD_TIME`, `@BUILD_TIMESTAMP`).\n* V2-specific: `V2CC`, `V2CFLAGS`, `V2VERBOSE` (for v2 development).\n\n## Gotchas\n* Core modules (`builtin`, `strings`, `os`, `strconv`, `time`) can\n  affect the compiler because it is a V program. Rebuild `./v` when\n  they change.\n* Module names must match their directory name; mismatches cause silent\n  import failures. See Modules and Imports.\n* Some V programs and tools hardcode `os.execute('v ...')` in their\n  source. The `./v` workflow does not protect against these; if you\n  encounter unexpected behavior from such calls, check whether the\n  code is invoking the system `v` instead of `./v` and adjust\n  accordingly.\n* Stale `./v` can cause confusing failures; rebuild if behavior\n  seems off.\n* Output tests require exact matches; whitespace changes break tests.\n* Formatting reminders: follow Code Style for `fmt` and `check-md`.\n* C compilation errors? Check generated C with `-keepc`\n  (creates `/tmp/*.tmp.c`; see also TMPDIR in Environment Variables).\n* Compile-time code (`$if`, `$for`, `$embed_file`, etc.) is a common\n  source of AI mistakes. See Compile-Time Code and Reflection for\n  correct usage and pitfalls.\n* Broken compiler? See Safety for the recovery sequence.\n* Consult `CONTRIBUTING.md` and `TESTS.md` for:\n  * New tests or test infra changes.\n  * Output tests or `.out` updates.\n  * Broad refactors or compiler-wide changes.\n  * Unsure which slow test runner applies to your change.\n","cmd/tools/README.md":"# tools Documentation\n\nPath: `cmd/tools/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/README.md)","cmd/tools/fast/README.md":"# fast Documentation\n\nPath: `cmd/tools/fast/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/fast/README.md)","cmd/tools/scripting/README.md":"# scripting Documentation\n\nPath: `cmd/tools/scripting/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/scripting/README.md)","cmd/tools/testing/README.md":"# testing Documentation\n\nPath: `cmd/tools/testing/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/testing/README.md)","cmd/tools/vcreate/README.md":"# vcreate Documentation\n\nPath: `cmd/tools/vcreate/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vcreate/README.md)","cmd/tools/vgenerate/README.md":"# vgenerate Documentation\n\nPath: `cmd/tools/vgenerate/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vgenerate/README.md)","cmd/tools/vmcp/README.md":"# vmcp Documentation\n\nPath: `cmd/tools/vmcp/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vmcp/README.md)","cmd/tools/vpbgen/README.md":"# vpbgen Documentation\n\nPath: `cmd/tools/vpbgen/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vpbgen/README.md)","cmd/tools/vpm/README.md":"# vpm Documentation\n\nPath: `cmd/tools/vpm/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vpm/README.md)","cmd/tools/vskills/README.md":"# vskills Documentation\n\nPath: `cmd/tools/vskills/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vskills/README.md)","cmd/tools/vvet/README.md":"# vvet Documentation\n\nPath: `cmd/tools/vvet/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vvet/README.md)","examples/cpu_features/SSE_and_MMX_Extensions/README.md":"# SSE_and_MMX_Extensions Documentation\n\nPath: `examples/cpu_features/SSE_and_MMX_Extensions/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/examples/cpu_features/SSE_and_MMX_Extensions/README.md)","cmd/tools/vpm/test_utils/README.md":"# test_utils Documentation\n\nPath: `cmd/tools/vpm/test_utils/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vpm/test_utils/README.md)","vlib/x/async/tools/README.md":"# tools Documentation\n\nPath: `vlib/x/async/tools/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/x/async/tools/README.md)","vlib/x/executor/tools/README.md":"# tools Documentation\n\nPath: `vlib/x/executor/tools/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/x/executor/tools/README.md)","cmd/tools/vdoc/testdata/attributes/main.md":"# Attributes Skill\n\nPath: `cmd/tools/vdoc/testdata/attributes/main.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vdoc/testdata/attributes/main.md)","cmd/tools/vdoc/testdata/enum_helpers/main.md":"# Enum_helpers Skill\n\nPath: `cmd/tools/vdoc/testdata/enum_helpers/main.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vdoc/testdata/enum_helpers/main.md)","cmd/tools/vdoc/testdata/output_formats/README.md":"# output_formats Documentation\n\nPath: `cmd/tools/vdoc/testdata/output_formats/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vdoc/testdata/output_formats/README.md)","cmd/tools/vdoc/testdata/output_formats/main.md":"# Output_formats Skill\n\nPath: `cmd/tools/vdoc/testdata/output_formats/main.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vdoc/testdata/output_formats/main.md)","cmd/tools/vdoc/testdata/readme_in_project_root/README.md":"# readme_in_project_root Documentation\n\nPath: `cmd/tools/vdoc/testdata/readme_in_project_root/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vdoc/testdata/readme_in_project_root/README.md)","vlib/v/skills/v-concurrency/references/CHANNELS.md":"# References Skill\n\nPath: `vlib/v/skills/v-concurrency/references/CHANNELS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-concurrency/references/CHANNELS.md)","vlib/v/skills/v-concurrency/references/PATTERNS.md":"# References Skill\n\nPath: `vlib/v/skills/v-concurrency/references/PATTERNS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-concurrency/references/PATTERNS.md)","vlib/v/skills/v-lang/references/COMPTIME.md":"# References Skill\n\nPath: `vlib/v/skills/v-lang/references/COMPTIME.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-lang/references/COMPTIME.md)","vlib/v/skills/v-lang/references/MUTABILITY.md":"# References Skill\n\nPath: `vlib/v/skills/v-lang/references/MUTABILITY.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-lang/references/MUTABILITY.md)","vlib/v/skills/v-lang/references/OPTION-RESULT.md":"# References Skill\n\nPath: `vlib/v/skills/v-lang/references/OPTION-RESULT.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-lang/references/OPTION-RESULT.md)","vlib/v/skills/v-lang/references/SUMTYPES.md":"# References Skill\n\nPath: `vlib/v/skills/v-lang/references/SUMTYPES.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-lang/references/SUMTYPES.md)","vlib/v/skills/v-mcp/references/EDITING.md":"# References Skill\n\nPath: `vlib/v/skills/v-mcp/references/EDITING.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-mcp/references/EDITING.md)","vlib/v/skills/v-mcp/references/TOOLS.md":"# References Skill\n\nPath: `vlib/v/skills/v-mcp/references/TOOLS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-mcp/references/TOOLS.md)","vlib/v/skills/v-memory/references/OWNERSHIP.md":"# References Skill\n\nPath: `vlib/v/skills/v-memory/references/OWNERSHIP.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-memory/references/OWNERSHIP.md)","vlib/v/skills/v-memory/references/UNSAFE.md":"# References Skill\n\nPath: `vlib/v/skills/v-memory/references/UNSAFE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-memory/references/UNSAFE.md)","vlib/v/skills/v-package/references/SUBCOMMANDS.md":"# References Skill\n\nPath: `vlib/v/skills/v-package/references/SUBCOMMANDS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-package/references/SUBCOMMANDS.md)","vlib/v/skills/v-project/references/TEMPLATES.md":"# References Skill\n\nPath: `vlib/v/skills/v-project/references/TEMPLATES.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-project/references/TEMPLATES.md)","vlib/v/skills/v-testing/references/COVERAGE.md":"# References Skill\n\nPath: `vlib/v/skills/v-testing/references/COVERAGE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-testing/references/COVERAGE.md)","vlib/v/skills/v-testing/references/PATTERNS.md":"# References Skill\n\nPath: `vlib/v/skills/v-testing/references/PATTERNS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-testing/references/PATTERNS.md)","vlib/v/skills/v-testing/references/TROUBLESHOOTING.md":"# References Skill\n\nPath: `vlib/v/skills/v-testing/references/TROUBLESHOOTING.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-testing/references/TROUBLESHOOTING.md)","vlib/v/skills/v-veb/references/MIDDLEWARE.md":"# References Skill\n\nPath: `vlib/v/skills/v-veb/references/MIDDLEWARE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-veb/references/MIDDLEWARE.md)","vlib/v/skills/v-veb/references/ROUTES.md":"# References Skill\n\nPath: `vlib/v/skills/v-veb/references/ROUTES.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-veb/references/ROUTES.md)","vlib/v/skills/v-workflow/references/COMPILER-WORKFLOW.md":"# References Skill\n\nPath: `vlib/v/skills/v-workflow/references/COMPILER-WORKFLOW.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-workflow/references/COMPILER-WORKFLOW.md)","vlib/v/skills/v-workflow/references/FLAGS.md":"# References Skill\n\nPath: `vlib/v/skills/v-workflow/references/FLAGS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-workflow/references/FLAGS.md)","vlib/v/skills/v-workflow/references/V-MOD.md":"# References Skill\n\nPath: `vlib/v/skills/v-workflow/references/V-MOD.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-workflow/references/V-MOD.md)"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"<!-- V_AGENTS.md  v1.1  2026-02-04 -->\n\n# V Repo Guide\n\nPractical quick reference for the V compiler, standard library, and tools.\nWritten for AI coding agents; useful for humans too.\n\n## Contents\n* Quick Start\n* Top Rules\n* Agent Rules\n* Safety (Do Not Brick the Repo)\n* Divergences From Repo Docs\n* Quick Decisions\n* Common Workflow\n* Reporting\n* Prerequisites\n* Build & Rebuild\n* Run Programs\n* Testing\n* Code Style\n* Modules and Imports\n* C/JS Interop Hygiene\n* Environment-Specific Code (files and `$if`)\n* Compile-Time Code and Reflection\n* Debug\n* Compiler Performance\n* Compiler Architecture\n* Key Directories\n* Test Locations\n* Error Reporting (checker/parser)\n* Option/Result Types\n* Tools\n* Commits and PRs\n* Environment Variables\n* Gotchas\n\n## Quick Start\nGet operational from the repo root in three steps:\n\n1. Build once (only if `./v` is missing): `make`\n2. Rebuild the working compiler in place: `./v self`\n3. Use `./v` for everything:\n   * Run a file: `./v run examples/hello_world.v`\n   * Run tests: `./v -silent test vlib/v/`\n   * Format: `./v fmt -w path/to/file.v`\n\nThen read Top Rules and Agent Rules before making changes.\n\n## Top Rules\n* Use `./v` for building, running, and testing.\n* Put all V flags immediately after `./v` and before the\n  subcommand/file, e.g. `./v -g run file.v`\n  (not `./v run file.v -g`); flags after the subcommand are passed\n  to that subcommand.\n* Rebuild `./v` after compiler or core module changes\n  (see Build & Rebuild).\n* Run the smallest relevant tests; see Testing for triggers and\n  minimums.\n* Ask before large refactors or wide file touches (see Agent Rules).\n* Do not stash or modify unrelated files unless explicitly instructed.\n* This guide assumes agents run locally, not in CI; CI notes are\n  informational only.\n* When a summary is required, include behavior change, tests run, and\n  touched file paths.\n* If instructions overlap, prefer Build & Rebuild, Testing, and\n  Reporting.\n* If duplicates drift, treat Build & Rebuild, Testing, and Reporting as\n  canonical and align other sections to them.\n\n## Agent Rules\n\n### Repo root\nAll commands assume the repo root as the working directory. The default\nlocation is `/opt/v`, but this may differ in your environment. If a\ncommand fails due to missing paths, verify with `pwd` and adjust\naccordingly. Use a per-command workdir only when a task requires a\nsubdir.\n\n### Tone and output\n* Be concise by default. If the user asks for depth, provide it while\n  keeping structure tight.\n* Keep output easy to scan: short sections, bullets when listing,\n  commands in backticks, no filler.\n* Use a strict, operational tone unless higher-priority instructions\n  override it.\n* Ask only when required. If information is missing, ask a direct\n  question.\n\n### File access and edit scope\n* You may read and edit all files in the V repo without asking for\n  permission. This is file access, not change scope; scope is\n  constrained below. Reading is always OK.\n  Edits to `ci/` or `Dockerfile*` still require an explicit ask.\n* Run build, test, and format commands without asking for permission.\n  These are validation steps, not code changes. Only ask about edit\n  scope, not about running `fmt`, targeted tests, or `check-md`.\n* Only modify files required for the user request; avoid unrelated\n  refactors. If duplication is harmful, small refactors to remove it\n  are OK only when needed. Only refactor duplication in code you are\n  already touching, and only when it directly supports the request or\n  fixes a bug there. \"Touching\" means files already modified for the\n  request.\n* Avoid unrelated file changes; call them out if present.\n* Avoid touching `thirdparty/` unless explicitly requested. If changes\n  are needed there, ask for approval before proceeding.\n\n### When to ask\n* Ask before large refactors or wide file touches across multiple repo-root\n  directories like `cmd/`, `vlib/`, `doc/`, and `examples/`.\n  Exception: docs-only changes across many files are OK without asking;\n  call them out in the summary.\n* Ask before large behavioral changes within a single subsystem or file,\n  even if the file count is small. Examples: changes to parser rules,\n  checker resolution, codegen output shape, diagnostic text/ordering, or\n  tool CLI behavior. Large means user-visible changes in CLI flags,\n  output, diagnostics, or codegen shape. If unsure whether a change is\n  \"large,\" ask.\n* Ask before touching `ci/` or `Dockerfile*` unless explicitly\n  requested. If changes are needed there, confirm whether local\n  validation is expected or if CI-only coverage is acceptable.\n\n### Bootstrap\n* Bootstrap/compiler usage rules: see Top Rules and Build & Rebuild.\n\n### Completeness and summaries\n* If you cannot complete a requested step, state the blocker and partial\n  progress (what was attempted and what remains).\n* After substantial work, provide a short summary and list touched file\n  paths. Substantial work means any behavioral change, or changes in\n  more than one file. Always include tests run (or \"Not run\" with\n  reason) in the summary.\n* Never change tests just to silence failures. Update expectations only\n  when behavior changes are intended, and note the rationale in the\n  summary.\n\n### New files and deeper guidance\n* New file checklist: format with `./v fmt -w`, add doc comments for\n  any public functions, run `./v check-md` for markdown files, and\n  keep Markdown lines <= 100 chars. Add or update tests when introducing\n  a new public API.\n* For deeper edge cases, consult `CONTRIBUTING.md` and `TESTS.md`.\n\n## Safety (Keep the Compiler Current)\n* Rebuild the working `./v` binary in place with `./v self` after compiler\n  changes. Overwriting `./v` is the intended workflow.\n* Keep generated C when debugging backend issues: add `-keepc`.\n* Avoid hidden/bidirectional Unicode characters in source/markdown\n  files.\n* If the compiler becomes unusable, recover with:\n  1. `git stash`\n  2. `make`\n  3. `git stash apply`\n  Check `git status` before stashing to avoid hiding unrelated work.\n  Do not stash unless explicitly instructed or the compiler is bricked.\n\n## Divergences From Repo Docs\nThe repo docs use `v` in examples. In this environment, use `./v` for all\nbuilds, runs, tests, and in-place self-rebuilds.\n* `TESTS.md` suggests `v test-all` before PRs; ask before running\n  `./v test-all`.\nThese overrides keep commands anchored to this checkout.\n\n## Quick Decisions\n* For rebuild and test choices, follow Build & Rebuild and Testing.\n* Follow Common Workflow for the default execution order.\n* For broader workflow guidance, see `CONTRIBUTING.md`.\n\n### Compact decision table (rebuild/tests)\nUse this table to pick the minimum rebuild/tests quickly. See Build &\nRebuild and Testing for full details and edge cases.\nCommands omit `./v` for brevity; assume the `./v` prefix.\nThis table is the minimum set; check Testing for additional triggers.\nREPL and backend changes have additional triggers in Testing.\nNote: `cmd/v/` is compiler scope; treat changes there as compiler\nchanges.\nWhen in doubt, ask before proceeding.\nIf you read only one section for tests, **read Testing**.\n\n| Change area | Rebuild? | Minimum tests to run |\n| --- | --- | --- |\n| Docs only (`.md`) | No | `check-md file.md` |\n| Compiler (`vlib/v/`, `cmd/v/`) | Yes | `-silent vlib/v/compiler_errors_test.v`; `test vlib/v/` |\n| Core modules (builtin/strings/os/strconv/time) | Yes | Smallest relevant tests |\n| Parser-only (`vlib/v/parser/`) | Yes | `test vlib/v/parser/` |\n| Checker-only (`vlib/v/types/`) | Yes | `test vlib/v/types/` |\n| Comptime (`vlib/v/types/`, `vlib/v/transform/`) | Yes | `test vlib/v/tests/`; comptime-related tests |\n| vlib (non-compiler) | No | Nearest `*_test.v` or `test vlib/path/` |\n| Tools (`cmd/tools/`) | No | Tool-specific test; else nearest `*_test.v` |\n| Diagnostic/output changes | Yes | `vlib/v/slow_tests/inout/compiler_test.v` |\n| C codegen (`vlib/v/gen/c/`) | Yes | `test vlib/v/gen/c/` |\n\n## Common Workflow\n0. Before work: `git status`; ensure `./v` exists; rebuild if needed.\n   If `./v` is missing, see Quick Start or Build & Rebuild.\n1. Edit the relevant files.\n2. If compiler sources or core modules changed, rebuild `./v` with `./v self`\n   (see Build & Rebuild).\n3. Format touched `.v`/`.vsh` files and run `./v check-md` on\n   touched markdown.\n4. Run the smallest relevant tests for the change scope (see Testing).\n\nSee Build & Rebuild for rebuild triggers and flags.\n\n## Reporting\n* When a summary is required (see Agent Rules), it must include:\n  * Behavior changes (or \"No behavior change\").\n  * Tests run (or \"Not run\" with a reason).\n  * Touched file paths.\n  * Note unrelated changes if present.\n  * Note doc updates if public behavior/tool output changed.\n* If public behavior or tool output changes, update relevant docs\n  (README.md, `doc/`, `tutorials/`) and note it.\n  For example: README.md for top-level CLI usage, `doc/` for\n  compiler/tool docs, `tutorials/` for learning material.\n* If you add or change a public API, update module docs or README and\n  note it. Public API includes stdlib functions/types and user-visible\n  compiler flags, diagnostics, tool CLI behavior (including `cmd/tools`\n  and stdlib CLI tools), or output formats.\n* Update `CHANGELOG.md` or `ROADMAP.md` only when explicitly requested.\n* Public behavior includes compiler output, diagnostics, user-facing\n  CLI, and stdlib API. This includes developer-facing flags, error\n  codes, or output ordering changes.\n* Internal refactors with no public behavior change do not require doc\n  updates.\n* If unsure whether behavior is public, ask the user before updating\n  docs.\n* Acceptable reasons for not running tests: docs-only change, no\n  relevant tests, or environment constraints. Be specific.\n* Docs-only changes: run `./v check-md file.md`; no other tests\n  required unless a test explicitly reads those docs.\n  No rebuild is needed unless compiler or core modules changed.\n* For docs-only, explicitly mention `check-md` in the summary.\n* If you update `.out` files, state the rationale in the summary.\n* Do not update `.out` files unless a behavior change is intended;\n  otherwise treat mismatches as regressions.\n* Example summary line: `Behavior change: none` or\n  `Behavior change: fixed X`.\n* Behavior change includes output, API, error messages, and test\n  expectations.\n* If blocked, note what was attempted and why it failed.\n* If tooling/network restrictions block a step, state the restriction\n  and the closest viable alternative.\n* Use `git status` to confirm touched files before reporting.\n\n## Prerequisites\n* Toolchain: `make`, `git`, and a C compiler (`clang` or `gcc`).\n* Windows: use `makev.bat` for the initial build.\n\n## Build & Rebuild\n* Initial build (only if `./v` is missing): `make`\n  (Windows: `makev.bat`).\n* Rebuild `./v` in place: `./v self`\n  * This intentionally overwrites the active compiler with one built from the\n    current checkout.\n* If `./v` is missing, run `make` first, then build `./v`.\n* This section is the source of truth for rebuild triggers.\n  If a rule appears elsewhere, defer to this section.\n* Rebuild triggers:\n  * Compiler sources in `vlib/v/` or `cmd/v/`.\n  * Core modules: `builtin`, `strings`, `os`, `strconv`, `time`.\n* If `./v` exists but compiler sources changed, still rebuild `./v`\n  before tests.\n* If unsure whether compiler/core changes happened, rebuild `./v`.\n* Common flags:\n  * `-g` debug info (V line numbers).\n  * `-cg` debug info (C line numbers); often combined with `-keepc`.\n  * `-keepc` keep generated C file(s).\n  * `-prod` optimized build.\n  * `-o file` output path/name.\n  * `-cc clang` pick a C compiler.\n  * `-cstrict` be stricter about the generated C.\n  * `-b js|native|wasm` select a backend.\n  * `-os <os>` cross-compile target selection (when supported).\n\n## Code Style\n* Comments: add succinct comments only when code is not self-explanatory.\n  Do not delete existing comments unless they are incorrect; you may fix\n  grammar or spelling without changing meaning.\n  Add V doc comments right before each new or modified public function\n  or method.\n  The V doc comments should start with the name of the fn,\n  example: `// the_name does ...`\n* Copy pasta: avoid copy pasta. If there's duplicate logic, move to a\n  function only when it is required for the request and within code you\n  are already touching.\n* Avoid using `unsafe{ code }` blocks where possible, and minimize\n  their scope. `unsafe` is acceptable for low-level interop (e.g. C\n  pointer casts, manual memory management) but should never wrap\n  ordinary V logic. When `unsafe` is required, keep the block as small\n  as possible and add a comment explaining why it is necessary.\n* Keep Markdown lines <= 100 chars (the checker is strict).\n  Apply to touched lines in modified Markdown files.\n* Avoid hidden/bidirectional Unicode characters in source/markdown\n  files.\n* Non-V files: keep existing formatting; only reformat if required by\n  the change.\n* Treat `.vv` files in `vlib/v/slow_tests/inout/` as fixtures; avoid\n  formatting unless a behavior change is intended and output\n  expectations are updated.\n* Formatting and check commands are in Tools.\n\n## Modules and Imports\n* Module names must match their directory name (no hierarchy in the\n  `module` line). Mismatches cause silent import failures.\n* Imports follow the folder hierarchy (e.g. `import abc.def`).\n\n## C/JS Interop Hygiene\n* Avoid using `C.` or `JS.` symbols in plain `.v` files.\n  * Use `.c.v` / `.js.v` files.\n  * Enable `-Wimpure-v` to catch accidental impurity.\n* Do not refactor existing `C.`/`JS.` uses in `.v` files unless\n  required. If you must change those lines, prefer moving the interop\n  code to `.c.v`/`.js.v`.\n* For C interop, use correct `const_` prefixes in C function\n  redeclarations when needed (helps with `-cstrict` and C static\n  analysis tooling).\n\n## Environment-Specific Code (files and `$if`)\nV supports environment-specific file suffixes. Prefer them when the\nwhole file is platform/backend specific.\n\nCommon patterns:\n* Backend splits:\n  * `*.c.v` (C backend), `*.js.v` (JS backend), `*.native.v` (native\n    backend), `*.wasm.v` (WASM backend)\n* OS splits:\n  * `*_windows.c.v`, `*_linux.c.v`, `*_nix.c.v`, with `*_default.c.v`\n    as fallback\n* Flag splits:\n  * `*_d_customflag.v` is included only with `-d customflag`\n  * `*_notd_customflag.v` is included only when that flag is NOT passed\n\nNotes:\n* Do not use combinatorial suffixes like `_d_flag_linux.c.v`; use\n  `_d_flag.v` plus `$if linux {}` inside the file.\n* For smaller platform differences, use compile-time `$if` blocks:\n  * `$if windows { ... } $else { ... }`\n* See Compile-Time Code and Reflection for the full `$if` reference.\n\n## Compile-Time Code and Reflection\nV uses `$` as a prefix for compile-time (comptime) operations. These\nare evaluated by the compiler, not at runtime. AI agents frequently\nconfuse comptime and runtime constructs; this section clarifies the\nboundaries.\n\n### Compile-time `$if`\n`$if` evaluates conditions at compile time. It is not a runtime `if`.\nUse it for platform, compiler, and custom-flag checks:\n* `$if windows { ... } $else $if linux { ... } $else { ... }`\n* `$if debug { ... }` (enabled by `-g` or `-cg`)\n* `$if prod { ... }` (enabled by `-prod`)\n* `$if custom_flag ? { ... }` (enabled by `-d custom_flag`)\n\nFull list of builtin `$if` options: see the table at\n<https://docs.vlang.io/conditional-compilation.html>.\n\nCommon mistakes:\n* Using runtime `if` where `$if` is needed for platform-specific code.\n  Runtime `if` will fail to compile if it references platform-specific\n  symbols; `$if` excludes the block entirely on non-matching platforms.\n* Forgetting the `?` suffix for custom flags: `$if myflag ? { ... }`.\n  Without `?`, the compiler treats it as a builtin option and silently\n  ignores it.\n\n### Compile-time `$for`\n`$for` iterates over type metadata at compile time. It works with:\n* `StructType.fields` - iterate struct fields\n* `StructType.methods` - iterate struct methods\n* `EnumType.values` - iterate enum values\n* `StructType.attributes` - iterate struct attributes\n* `SumType.variants` - iterate sum type variants\n* `method.params` - iterate method parameters\n\nInside `$for` blocks, use `$if` to branch on field/method types:\n```v oksyntax\nfn main() {\n\t$for field in MyStruct.fields {\n\t\t$if field.typ is string {\n\t\t\tprintln(field.name)\n\t\t}\n\t}\n}\n```\n\nAlso works with generics: `T.fields`, `param.fields` where\n`fn gen[T](param T) {}`.\n\nCommon mistakes:\n* Using runtime `for` to iterate struct fields. This does not work;\n  V has no runtime reflection. Always use `$for`.\n* Calling `obj.$method()` outside a `$for m in Type.methods` block.\n  The `$method()` call is only valid inside a comptime method\n  iteration.\n* Assuming comptime `$for` produces a runtime loop. It does not; the\n  compiler unrolls it into concrete code for each field/method/variant.\n\n### Compile-time functions\n\nOnly the following `$`-prefixed functions are supported:\n* `$embed_file('path')` - embed a file's contents into the binary.\n  Paths can be absolute, relative to the source file, or use pseudo\n  variables like `@VEXEROOT` or `@VMODROOT`.\n* `$tmpl('path')` - compile a V template file (used by veb).\n* `$env('VAR')` - read an environment variable at compile time.\n* `$d('ident', default)` - read a `-d ident=value` compile-time\n  define, with a default fallback.\n* `$res('path')` - embed a resource (Android).\n* `$compile_error('msg')` - emit a compile-time error.\n* `$compile_warn('msg')` - emit a compile-time warning.\n* `$pkgconfig('name')` - query pkg-config at compile time.\n* `$typeof(expr)` - compatibility alias for `typeof(expr)`.\n* `$sizeof(Type)` - compatibility alias for `sizeof(Type)`.\n* `$isreftype(expr)` - compatibility alias for `isreftype(expr)`.\n* `$__offsetof(Struct, field_name)` - compatibility alias for\n  `__offsetof(Struct, field_name)`.\n* `$dump(expr)` - compatibility alias for `dump(expr)`.\n\nCommon mistakes:\n* Prefer the unprefixed `typeof`, `sizeof`, `isreftype`, `__offsetof`,\n  and `dump` forms in new code. Their `$`-prefixed compatibility aliases\n  are also accepted.\n* Assuming `$embed_file` returns a string. It returns an\n  `EmbedFileData` object; use `.to_string()` or `.to_bytes()`.\n* Using `$env` where a runtime `os.getenv` is appropriate, or vice\n  versa. `$env` is baked in at compile time and cannot change at\n  runtime.\n\n### Compile-time pseudo variables\nThese are `@`-prefixed identifiers substituted at compile time:\n* `@FN` - current function name.\n* `@METHOD` - `ReceiverType.MethodName`.\n* `@MOD` - current module name.\n* `@STRUCT` - current struct name.\n* `@FILE`, `@DIR`, `@LINE`, `@COLUMN`, `@FILE_LINE` - source\n  location.\n* `@LOCATION` - file, line, and current type+method; good for logging.\n* `@VEXE`, `@VEXEROOT` - path to the V compiler and its directory.\n* `@VHASH`, `@VCURRENTHASH` - compiler commit hashes.\n* `@VMOD_FILE`, `@VMODHASH`, `@VMODROOT` - nearest `v.mod` info.\n* `@BUILD_DATE`, `@BUILD_TIME`, `@BUILD_TIMESTAMP` - build time (UTC).\n  Override with the `SOURCE_DATE_EPOCH` env var for reproducible\n  builds.\n* `@OS`, `@CCOMPILER`, `@BACKEND`, `@PLATFORM` - build environment.\n\n### Compile-time type groups\nComptime type groups combine multiple types into a higher-level type\nfor use in generic or comptime `$if` checks:\n* `$int` - all integer types\n* `$float` - all float types\n* `$array` - all array types\n* `$map` - all map types\n* `$struct` - all struct types\n* `$enum` - all enum types\n* `$alias` - all type aliases\n* `$sumtype` - all sum types\n* `$function` - all function types\n* `$interface` - all interface types\n* `$option` - all option types\n\nExample: `$if field.typ is $int { ... }`\n\n### Comptime changes and testing\nComptime logic lives primarily in `vlib/v/types/checker_comptime.v` and\n`vlib/v/transform/comptime.v`. Changes here require a rebuild of\n`./v` and should be tested with `./v -silent test vlib/v/tests/` plus\nany comptime-specific tests. See the decision table and Testing.\n\n## Run Programs\n* Compile and run: `./v run file.v`.\n* Just compile: `./v file.v` (creates executable).\n* With debug info: `./v -g run file.v`.\n* Debug run (C line numbers): `./v -keepc -cg run file.v`.\n* Example: `./v run examples/hello_world.v`.\n\n## Testing\nRun:\n* File (shows test output): `./v path/to/file_test.v`.\n* File (test runner report only): `./v test path/to/file_test.v`.\n* Dir: `./v -silent test path/to/dir/`.\n* Dir with statistics/metrics: `./v -stats test path/to/dir/`.\n* Compiler: `./v -silent vlib/v/compiler_errors_test.v`.\n* Fix outputs (only when intended):\n  `VAUTOFIX=1 ./v -silent vlib/v/compiler_errors_test.v`.\n* All: `./v test-all`.\n  Ask before running `./v test-all` unless explicitly requested.\n\nWhen:\n* Rule of thumb: for localized changes, run the smallest relevant\n  tests. If any trigger below matches, run the listed tests.\n* Minimum tests listed are the floor, not the ceiling; add targeted\n  tests for cross-cutting changes.\n* If unsure which tests apply, ask the user before proceeding.\n* If in doubt, prefer the smallest targeted test and ask.\n* Run all tests that apply. Start with the smallest targeted tests;\n  add slow tests as needed. Order does not matter.\n* Compiler changes (`vlib/v/` or `cmd/v/`):\n  Run `./v -silent vlib/v/compiler_errors_test.v`,\n  `./v -silent test vlib/v/`.\n* Parser-only changes (`vlib/v/parser/`):\n  Run `./v -silent test vlib/v/parser/`.\n* Checker-only changes (`vlib/v/types`):\n  Run `./v -silent test vlib/v/types/`.\n* vlib changes: Run nearest `*_test.v` or\n  `./v -silent test vlib/path/`.\n* Tool changes (`cmd/tools/`): Run tool-specific tests. If none exist,\n  run the smallest relevant `*_test.v` that exercises the tool.\n  Note: `cmd/v/` is compiler scope, not tools.\n  Examples: `cmd/tools/vfmt` -> `cmd/tools/vfmt_test.v` and\n  `vlib/v/gen/v/gen_test.v`.\n  `cmd/tools/vdoc` -> `cmd/tools/vdoc/vdoc_test.v`.\n* Diagnostic/output changes:\n  Run `./v -silent vlib/v/slow_tests/inout/compiler_test.v`.\n* C codegen changes: Run `./v -silent test vlib/v/gen/c/`.\n  Consider a stricter validation pass:\n  `./v -cstrict -cc clang -silent test vlib/v/`.\n* REPL changes: Run `./v -silent vlib/v/slow_tests/repl/repl_test.v`.\n* Broad refactors: Run `./v -silent test-all`.\n* Backend-specific changes: run the smallest relevant tests for the\n  affected backend. JS/native/WASM backends are incomplete, so avoid\n  broad `-b <backend> test vlib/` runs. Prefer targeted `*_test.v`\n  files or small test dirs with `-b js|native|wasm`.\n\nIf time-constrained, prioritize\n`./v -silent vlib/v/compiler_errors_test.v` and the smallest targeted tests.\nRun `vlib/v/slow_tests/inout/compiler_test.v` and the tests under\n`vlib/v/gen/c/` when output or codegen changes are likely.\nSee `TESTS.md` for more guidance on test selection and output\nexpectations.\nSee `CONTRIBUTING.md` for broader workflow guidance.\n\nConcrete triggers:\n* `vlib/v/slow_tests/inout/compiler_test.v` when error text or output\n  formatting changes, or changes in checker/parser error reporting.\n* `./v -silent test vlib/v/gen/c/` for changes under `vlib/v/gen/c/`\n  or C codegen output paths.\n\nTypes:\n* Standard: `*_test.v` files with `test_` functions.\n* Output: `.vv` source + `.out` expected output in\n  `vlib/v/slow_tests/inout/`.\n  Example: `./v -silent vlib/v/slow_tests/inout/compiler_test.v`.\n* `vlib/v/tests/**` may use `.run.out` expectations; run with\n  `./v -silent test vlib/v/tests`.\n\nDocs-only guidance: see Reporting.\nIf time-boxed, run at least the smallest relevant test and note\nskipped coverage in the summary.\n\n### Useful env variables and flags while testing\n* `VAUTOFIX=1` - Auto-update .out files when tests fail (run twice).\n  Use only when a behavior change is intended.\n* `VTEST_ONLY=glob_pattern` - Run only tests matching pattern.\n* `VTEST_HIDE_OK=1` - Hide successful tests, show only failures.\n* `./v -silent test path/to/dir/` - Show only failed tests (if any), and a summary report.\n* `-cc tcc` can speed test builds when TCC is available.\n* Output expectations: update `.out` files only when behavior changes\n  are intended; note the rationale in the summary.\n\n## Debug\n* See what the C compiler is doing:\n  * `-showcc` prints the C compile command.\n  * `-show-c-output` prints the C compiler output.\n* Build a temporary compiler for debug flags:\n  * `./v -o ./w -d trace_checker cmd/v`\n  * Then run: `./w file.v`\n* Keep and inspect generated C:\n  * `-keepc -cg` is the common combo.\n  * `-printfn <name> -o file.c` emits only the named C function to standard\n    output. The `name` uses the `modulename__fnname` format (e.g.\n    `main__main`). This flag can be repeated to print multiple\n    functions. Methods/generics may use more complex C names; use\n    `-keepc` to confirm exact symbols.\n* Trace stages:\n  `-d trace_scanner|trace_parser|trace_checker|trace_gen`.\n  These flags can help diagnose a problem when a stage stops earlier\n  than expected.\n* Time stages: `-d time_parsing|time_checking`.\n* V panics: `-keepc -g`.\n* C segfaults: `-keepc -cg -cc clang`.\n* JS/native/wasm: prefer small, focused test files with\n  `-b js|native|wasm` and `-show-c-output` where applicable; avoid\n  broad `-b <backend> test vlib/`.\n* Quick code location technique:\n  When debugging code generation issues, add unique comment tags to\n  relevant code generation points (e.g., `/*tom51*/`) in the compiler\n  source. Rebuild and generate C code, then search the generated file for these\n  tags to quickly map generated C code back to the exact compiler source\n  location. This is especially useful when multiple code paths generate\n  similar-looking output and you need to identify which path is actually used.\n  Example:\n  ```v\n  // In vlib/v/gen/c/stmt.v:\n  g.write('builtin___option_ok/*tom51*/(&(${styp}[]) { ')\n\n  // After rebuild, search generated C code for \"tom51\":\n  // builtin___option_ok/*tom51*/(&(int[]) { ... });\n  ```\n  Remember to remove these debug tags after fixing the issue.\n\n## Compiler Performance\n\nFor the fixed self-compilation benchmark, results, and agent regression policy, read\n[vself_speed.md](vself_speed.md). Every compiler fix must preserve or improve that efficiency;\ninvestigate instruction-count regressions before merging.\n\nUse **retired CPU instructions per fixed workload and per parsed source line** as the primary\ncompiler optimization metrics; lower is better. On macOS arm64,\n`/usr/bin/time -l` reports `instructions retired` from hardware counters. Under matched conditions,\nthis is the most reliable local measure of compiler work: it is less sensitive than elapsed time to\nCPU frequency and scheduling. Also retain elapsed time, cycles, and peak memory; fewer instructions\ndo not guarantee lower latency or memory use. Compare counts on the same machine and architecture.\n\nThe instruction counter is scoped to the measured process; unrelated processes do not add their\ninstructions to it. An idle host is not required for instruction comparisons. Counts are dynamic,\nnot a static property of the binary: synchronization, retries, and runtime paths can vary. Use\nserial paired measurements to check that variation. Record host load as context for elapsed time\nand cycles; use controlled load when making latency claims.\n\nFor instructions per line, divide by physical lines in the `.v` and `.vh` files actually parsed,\nincluding builtins, imports, blank lines, and comments. Collect `parsed .v lines` plus\n`parsed .vh lines` in a separate unmeasured `-v` run with the same input flags. Preserve that\ninventory and verify that both compilers process the same files and line count. Keep the fixture\nfixed: padding files or changing source complexity must not count as an optimization. Retain total\ninstructions alongside instructions/line; establish a new baseline when the fixture changes.\nInstructions per second depends on hardware, scheduling, and instruction mix; do not use it as a\ncompiler-efficiency score. See [vself_speed.md](vself_speed.md) for the self-compilation fixture.\n\nBuild a production compiler outside the measured run, then measure compilation to C:\n\n```sh\nenv -u VFLAGS -u VEXE VJOBS=1 SOURCE_DATE_EPOCH=0 V_MACOS_V3_NO_FALLBACK=1 \\\n  ./v -prod -cc clang self || exit 1\nbenchmark_out=$(mktemp -d \"${TMPDIR:-/tmp}/v-perf.XXXXXX\")\nenv -u VFLAGS -u VEXE VJOBS=1 SOURCE_DATE_EPOCH=0 V_MACOS_V3_NO_FALLBACK=1 \\\n  /usr/bin/time -l -o \"$benchmark_out/time.txt\" \\\n  ./v -nocache -o \"$benchmark_out/sha3.c\" /absolute/path/to/sha3.v \\\n  > \"$benchmark_out/stdout.txt\" 2> \"$benchmark_out/stderr.txt\"\nbenchmark_status=$?\nprintf '%s\\n' \"$benchmark_status\" > \"$benchmark_out/exit-status.txt\"\ncat \"$benchmark_out/time.txt\"\n```\n\nReplace the source path with the fixed benchmark fixture. Read the integer on the\n`instructions retired` line. The `-prod` above optimizes the compiler itself; the timed command uses\nthe normal input compilation mode. Keep input flags identical between baseline and candidate.\n`-o ...c` includes parsing, checking, transforms, and C generation, while excluding the external C\ncompiler and execution of the generated program. `-check-syntax` measures a different workload;\nlabel it separately and verify which stages that compiler version executes. `-nocache` disables\ncompiler caching; it does not flush the operating system's filesystem cache.\n`V_MACOS_V3_NO_FALLBACK=1` prevents silent legacy-compiler fallback when measuring V3 on macOS.\n\nFor a baseline/candidate comparison:\n\n1. Build both compilers with the same production build recipe and C toolchain. Verify correctness,\n   diagnostics, and generated C before measuring. For a change intended to preserve output, require\n   identical C, allowing only explicitly recorded build metadata differences. Validate generated\n   C when changing the fixture or compiler representation; matching invalid C is not sufficient.\n2. Use the same host, OS, fixture, standard library, flags, environment, and filesystem placement.\n   Keep executable, source, and output paths identical between roles where possible; record any\n   remaining differences. Set `VJOBS=1` and `SOURCE_DATE_EPOCH=0`, and disable tracing/profiling.\n   Clear unintended compiler environment overrides, including `VFLAGS` and `VEXE`; record the\n   effective environment. Measure only successfully rebuilt compilers, never a stale binary.\n   Run only one benchmark process at a time. Other builds or tests on the host do not invalidate\n   process instruction counts; do not pause unrelated work or wait for an idle host. Record that\n   elapsed time and cycles collected under uncontrolled load are contextual measurements.\n3. Run one warmup per role, then **ten measured pairs**, alternating baseline/candidate order:\n   AB, BA, AB, BA, and so on. Record all 22 runs, excluding both warmups from statistics.\n   Use a distinct timing/stdout/stderr log for every run and the same generated C output path.\n4. Require exit status zero and exactly one positive `instructions retired` counter in every sample.\n   Preserve failed or incomplete runs, but do not report them as a completed comparison. If the\n   counter is missing or zero, report it as unavailable; elapsed time is not an instruction count.\n5. Report each role's median instructions/line and total instructions, the fixed parsed-line count,\n   percentage change between medians, median paired instruction difference, and how many pairs\n   made the candidate worse. Report memory and elapsed time alongside instructions. Retain all\n   raw logs, exact commands/environment, compiler commits, binary hashes,\n   fixture hashes, and output verification so the result can be reproduced.\n\nDo not select the fastest sample or compare medians from different measurement sessions as proof of\na small improvement. Investigate inconsistent pair results before accepting an optimization. For\nend-to-end build latency or parallel scaling, measure that workload separately with its intended\nworker count and external C compiler, and state that scope explicitly.\n\n## Compiler Architecture\nThe V compiler has the following stages, orchestrated by `v.driver`:\n`v.scanner` -> `v.parser`/`v.flat` -> `v.types` -> `v.transform` ->\n`v.markused` -> `v.gen.c`.\nAdditional backends and lowerings include FastC, WebAssembly, SSA, MIR,\ninstruction selection, and native ARM64 generation.\n\n### Key Directories\n* `vlib/`: Standard library (changes here can affect the compiler\n  itself).\n* `cmd/v/v.v`: Compiler entry.\n* `cmd/tools/`: vfmt, vdoc, vup, vquest, etc.\n* `examples/`: Example programs.\n* `thirdparty/`: Bundled C libraries (tcc, mbedtls, sokol, etc.).\n* `vlib/v/`: Compiler modules.\n  * scanner/ - Tokenizer\n  * token/ - Token definitions\n  * parser/ - Produces a flat AST\n  * flat/ - Compact AST storage\n  * types/ - Name resolution and type checking\n  * transform/ - AST simplification\n  * markused/ - Dead code eliminator\n  * gen/c/ - C code generation (primary backend, known as cgen)\n  * gen/fastc/ - Scanner-to-C fast path\n  * gen/arm64/ - Native ARM64 backend\n  * gen/wasm/ - WebAssembly backend\n\n### Test Locations\n* `vlib/v/tests/`: Compiler feature tests (including comptime).\n* `vlib/v/slow_tests/`: Output-matching and slow tests for the\n  compiler.\n* `vlib/v/slow_tests/inout/` - Output comparison tests\n  (.vv + .out pairs)\n* `vlib/v/parser/tests/` - Parser error tests\n* `vlib/v/checker/tests/` - Checker error tests\n* `vlib/v/gen/c/` - C codegen unit and integration tests\n* `examples/compiletime/` - Comptime usage examples (including\n  `reflection.v`)\n\n## Error Reporting (checker/parser)\nScanner and parser diagnostics carry `v.token.Pos` spans. Type-checker\ndiagnostics are collected by `v.types.TypeChecker`; shared diagnostic\nformatting and message types live in `vlib/v/errors/`.\n\n## Option/Result Types\n* Syntax: `?Type` (optional, can be none) vs `!Type` (result, can\n  error).\n* Common bugs: Unwrapping in if guards, struct init with option fields,\n  ternaries with options.\n* When changing option/result behavior, add a regression test in\n  `vlib/v/tests/`.\n* Tests: Search `vlib/v/tests/` for option/result test files.\n* Pitfall: Options in ternaries, SQL statements, and fixed arrays need\n  special handling in cgen.\n\n## Tools\n* Note: if a rule overlaps with Testing, follow Testing.\n* Format: `./v fmt -w <file>` for touched `.v` and `.vsh` files.\n  Format only touched files unless explicitly asked to reformat broader\n  scope.\n* Treat new files as touched for formatting and markdown checks.\n* Check *all* files are formatted: `./v -silent test-fmt`.\n  Run only when asked or when validating the full tree.\n* Check markdown: `./v check-md file.md` for touched `.md` files\n  (required before commits).\n* Code style checker: `./v vet vlib/v`\n  Run only when asked or when making broad checker changes (more than\n  3 files in `vlib/v/types/`).\n* Module docs: `./v doc -readme -all -l module_name`.\n* Search: `rg pattern` (or `git grep`); list files: `rg --files`.\n* Auto-format hook: `./v git-fmt-hook install`.\n\n## Commits and PRs\n* See `CONTRIBUTING.md` for full commit message conventions and PR\n  workflow.\n* Keep commits focused: one logical change per commit.\n* If your branch has diverged and a rebase or merge is needed, do not\n  force-push or drop hunks without asking. State the conflict and let\n  the user decide the resolution strategy. When in doubt, prefer\n  `git rebase` over `git merge` for a linear history, but always ask\n  if the situation is ambiguous.\n\n## Environment Variables\n* VFLAGS: Pass flags to all V invocations\n  (e.g., `VFLAGS='-g' ./v test-all`).\n* VEXE: Path to V compiler executable (useful in CI/scripts).\n* TMPDIR: Controls where `.tmp.c` files are written (V uses\n  `TMPDIR/v/`).\n* SOURCE_DATE_EPOCH: Override build timestamp for reproducible builds\n  (affects `@BUILD_DATE`, `@BUILD_TIME`, `@BUILD_TIMESTAMP`).\n* V2-specific: `V2CC`, `V2CFLAGS`, `V2VERBOSE` (for v2 development).\n\n## Gotchas\n* Core modules (`builtin`, `strings`, `os`, `strconv`, `time`) can\n  affect the compiler because it is a V program. Rebuild `./v` when\n  they change.\n* Module names must match their directory name; mismatches cause silent\n  import failures. See Modules and Imports.\n* Some V programs and tools hardcode `os.execute('v ...')` in their\n  source. The `./v` workflow does not protect against these; if you\n  encounter unexpected behavior from such calls, check whether the\n  code is invoking the system `v` instead of `./v` and adjust\n  accordingly.\n* Stale `./v` can cause confusing failures; rebuild if behavior\n  seems off.\n* Output tests require exact matches; whitespace changes break tests.\n* Formatting reminders: follow Code Style for `fmt` and `check-md`.\n* C compilation errors? Check generated C with `-keepc`\n  (creates `/tmp/*.tmp.c`; see also TMPDIR in Environment Variables).\n* Compile-time code (`$if`, `$for`, `$embed_file`, etc.) is a common\n  source of AI mistakes. See Compile-Time Code and Reflection for\n  correct usage and pitfalls.\n* Broken compiler? See Safety for the recovery sequence.\n* Consult `CONTRIBUTING.md` and `TESTS.md` for:\n  * New tests or test infra changes.\n  * Output tests or `.out` updates.\n  * Broad refactors or compiler-wide changes.\n  * Unsure which slow test runner applies to your change.\n","isInternal":false,"tokens":9403,"sizeBytes":37161},{"name":"main.md","path":"cmd/tools/vdoc/testdata/attributes/main.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vdoc/testdata/attributes/main.md","title":"Attributes Skill","category":"anthropic-skill","format":"markdown","content":"# Attributes Skill\n\nPath: `cmd/tools/vdoc/testdata/attributes/main.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vdoc/testdata/attributes/main.md)","tokens":300,"sizeBytes":191},{"name":"main.md","path":"cmd/tools/vdoc/testdata/enum_helpers/main.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vdoc/testdata/enum_helpers/main.md","title":"Enum_helpers Skill","category":"anthropic-skill","format":"markdown","content":"# Enum_helpers Skill\n\nPath: `cmd/tools/vdoc/testdata/enum_helpers/main.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vdoc/testdata/enum_helpers/main.md)","tokens":300,"sizeBytes":197},{"name":"main.md","path":"cmd/tools/vdoc/testdata/output_formats/main.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vdoc/testdata/output_formats/main.md","title":"Output_formats Skill","category":"anthropic-skill","format":"markdown","content":"# Output_formats Skill\n\nPath: `cmd/tools/vdoc/testdata/output_formats/main.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vdoc/testdata/output_formats/main.md)","tokens":300,"sizeBytes":203},{"name":"CHANNELS.md","path":"vlib/v/skills/v-concurrency/references/CHANNELS.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-concurrency/references/CHANNELS.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `vlib/v/skills/v-concurrency/references/CHANNELS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-concurrency/references/CHANNELS.md)","tokens":300,"sizeBytes":207},{"name":"PATTERNS.md","path":"vlib/v/skills/v-concurrency/references/PATTERNS.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-concurrency/references/PATTERNS.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `vlib/v/skills/v-concurrency/references/PATTERNS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-concurrency/references/PATTERNS.md)","tokens":300,"sizeBytes":207},{"name":"SKILL.md","path":"vlib/v/skills/v-concurrency/SKILL.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-concurrency/SKILL.md","title":"v-concurrency","category":"anthropic-skill","format":"markdown","content":"---\nname: v-concurrency\ndescription: V's concurrency primitives - spawn, the sync module (channels, Mutex, RwMutex, WaitGroup, Once, Pool, select), and parallel.amap for parallel map and run. Use when writing code with goroutines, threads, channels, locks, shared mutable state, a WaitGroup, or a race, and when reviewing V code that touches them. Covers how to wait for spawned work and why a detached spawn is a bug. Does not cover the mutability rules a lock protects (see v-lang), the build and test loop (see v-workflow), web servers (see v-veb), scripting a task in V (see v-scripts), or the wider command surface (see v-tools).\nlicense: MIT\n---\n\n# Concurrency in V\n\nV has three levels: `parallel.amap` when you have a collection to process, `spawn`\nplus a `WaitGroup` when you have a fixed set of tasks, and `sync` channels and\nlocks when the tasks have to talk to each other.\n\n## Resource Routing\n\n- `references/CHANNELS.md` - Read when passing work or results between threads, or\n  when a producer must not block.\n- `references/PATTERNS.md` - Read for worker pools, fan-out, a single-writer\n  cache, and the shapes that deadlock.\n\n## Reach for these first\n\n| Situation | Use |\n| --- | --- |\n| Process a slice, collect results | `parallel.amap(items, worker)` |\n| Process a slice, discard results | `parallel.run(items, worker)` |\n| A fixed number of independent tasks | `spawn f()` plus `sync.WaitGroup` |\n| Hand work to a pool | a channel plus a fixed set of consumers |\n| Share mutable state | `sync.Mutex`, and keep the critical section small |\n| Initialise once, lazily | `sync.Once` |\n| Reuse objects | `sync.Pool` |\n| Wait for any of several channels | `select` |\n\nReaching for `spawn` when `amap` would do is the most common mistake. `spawn`\nmakes you wait for the work; `amap` does it for you.\n\n## parallel: do not hand-roll this\n\n```v ignore\nimport arrays.parallel\n\nfn test_it_doubles_every_number() {\n\titems := [1, 2, 3, 4]\n\tgot := parallel.amap(items, fn (n int) int {\n\t\treturn n * 2\n\t})\n\tassert got == [2, 4, 6, 8]\n}\n```\n\n`amap` preserves order and returns a slice; `run` discards results. Both take an\noptional `Params` whose `workers` defaults to `runtime.nr_jobs()`, which reads\n`VJOBS`.\n\nThis is worth preferring over a loop with a `WaitGroup` even when the loop is\nshort: there is no way to forget to wait.\n\n## spawn needs a WaitGroup\n\n`spawn` starts a thread and returns immediately. Nothing waits for it, so a\nprogram that ends, or a function that returns, can leave it mid-write.\n\n```v ignore\nimport sync\n\nfn fetch_all(urls []string) []string {\n\tmut wg := sync.new_waitgroup()\n\tmut results := []string{len: urls.len}\n\tfor url, i in urls {\n\t\twg.add(1)\n\t\tspawn fn () {\n\t\t\tresults[i] = fetch(url)\n\t\t\twg.done()\n\t\t}\n\t}\n\twg.wait()\n\treturn results\n}\n```\n\nEvery rule that matters here is easy to get wrong:\n\n- **`wg.add(1)` before the spawn, `wg.done()` inside it.** A `done()` without a\n  matching `add()` panics.\n- **All `wg.go()` / `spawn` before `wg.wait()`.** The doc comment says so\n  explicitly: adding after waiting is a race.\n- **`wait()` or nothing.** A detached spawn is a bug unless it is genuinely\n  fire-and-forget, and V is not a language where that is the default answer.\n- **Never let a spawned function panic.** It takes the whole process down.\n\n`sync.WaitGroup.go(f)` bundles the `add` and the thread start, which removes two\nof the four mistakes:\n\n```v ignore\nmut wg := sync.new_waitgroup()\nwg.go(fn () { work() })\nwg.wait()\n```\n\n## Sharing state needs a lock\n\nA struct shared between threads needs a `Mutex`, and the rule is to hold it for\nthe shortest possible span:\n\n```v ignore\nimport sync\n\nstruct Counter {\nmut:\n\tmu    sync.Mutex\n\ttotal int\n}\n\nfn (mut c Counter) add(n int) {\n\tlock c.mu {\n\t\tc.total += n\n\t}\n}\n```\n\nDo not hold a lock across a channel operation, a `spawn`, or anything that can\nblock. Two locks taken in different orders by two threads deadlock, so pick one\norder and hold to it.\n\n`RwMutex` is for a structure read far more often than it is written. If the write\nrate is not clearly lower, `Mutex` is simpler and fast enough.\n\n## Channels are the typed default\n\nA channel moves one value type between threads and gives you a buffer:\n\n```v ignore\nmut jobs := chan Job{cap: 16}\nmut results := chan Result{cap: 16}\n\nspawn fn () {\n\tfor job in jobs {\n\t\tresults <- run(job)\n\t}\n\tclose(results)\n}\n\nfor r in results {\n\tprintln(r)\n}\n```\n\nNote `chan Job{cap: 16}` — the capacity is part of the type, and a buffered channel\nlets a producer get ahead instead of blocking on every send.\n\nSee `references/CHANNELS.md` for closing, `select` and timeouts.\n\n## Atomics for counters\n\nFor a single value — a counter, a flag, a pointer — a lock is more than needed:\n\n```v ignore\nimport sync.stdatomic\n\nmut hits := stdatomic.new_atomic(0u64)\nhits.add(1u64)\ntotal := hits.load()\n```\n\nThe type is fixed when the atomic is created, and `load`, `store` and `add` need\n`mut`. There are also plain functions taking a pointer (`stdatomic.add_u64(&x, 1)`)\nfor the cases where an `AtomicVal` field is awkward.\n\nUse atomics for one or two values. The moment you need two of them to change\ntogether there is no transaction, and you want a `Mutex`:\n\n```v ignore\n// Two values that must stay consistent with each other.\nlock c.mu {\n    c.total += n\n    c.checks += 1\n}\n```\n\n## Validation\n\nConcurrency bugs are timing bugs, and reasoning about them is unreliable. Run\nunder the race detector and read what it reports:\n\n```bash\nv -race run main.v\nv -race test dir/\n```\n\nA clean race run does not prove the absence of races, but a reported one is\nreal. Never disable a report without explaining it. See `v-workflow` for the rest\nof the loop.\n\n## Related Skills\n\n- **Mutability**: see [v-lang](../v-lang/SKILL.md) for `mut` receivers and the\n  value-versus-pointer difference a lock protects.\n- **The build loop**: see [v-workflow](../v-workflow/SKILL.md) for `-race`,\n  `-prod` and the other flags.\n- **Web servers**: see [v-veb](../v-veb/SKILL.md) when the shared state is a\n  request counter and `veb` already gives you the locking idiom.","frontmatter":{"name":"v-concurrency","description":"V's concurrency primitives - spawn, the sync module (channels, Mutex, RwMutex, WaitGroup, Once, Pool, select), and parallel.amap for parallel map and run. Use when writing code with goroutines, threads, channels, locks, shared mutable state, a WaitGroup, or a race, and when reviewing V code that touches them. Covers how to wait for spawned work and why a detached spawn is a bug. Does not cover the mutability rules a lock protects (see v-lang), the build and test loop (see v-workflow), web servers (see v-veb), scripting a task in V (see v-scripts), or the wider command surface (see v-tools).","license":"MIT"},"isInternal":false,"tokens":1611,"sizeBytes":6069},{"name":"COMPTIME.md","path":"vlib/v/skills/v-lang/references/COMPTIME.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-lang/references/COMPTIME.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `vlib/v/skills/v-lang/references/COMPTIME.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-lang/references/COMPTIME.md)","tokens":300,"sizeBytes":193},{"name":"MUTABILITY.md","path":"vlib/v/skills/v-lang/references/MUTABILITY.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-lang/references/MUTABILITY.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `vlib/v/skills/v-lang/references/MUTABILITY.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-lang/references/MUTABILITY.md)","tokens":300,"sizeBytes":197},{"name":"OPTION-RESULT.md","path":"vlib/v/skills/v-lang/references/OPTION-RESULT.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-lang/references/OPTION-RESULT.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `vlib/v/skills/v-lang/references/OPTION-RESULT.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-lang/references/OPTION-RESULT.md)","tokens":300,"sizeBytes":203},{"name":"SUMTYPES.md","path":"vlib/v/skills/v-lang/references/SUMTYPES.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-lang/references/SUMTYPES.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `vlib/v/skills/v-lang/references/SUMTYPES.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-lang/references/SUMTYPES.md)","tokens":300,"sizeBytes":193},{"name":"SKILL.md","path":"vlib/v/skills/v-lang/SKILL.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-lang/SKILL.md","title":"v-lang","category":"anthropic-skill","format":"markdown","content":"---\nname: v-lang\ndescription: The V language rules that make code which looks right fail to compile or behave wrongly - ?T versus !T, sum types and match exhaustiveness, mut receivers, module names matching directories, comptime $ forms, and explicit type conversion. Use when writing or reviewing any V code that uses an Option, a Result, a sum type, a match, generics, or a custom flag, and when a V compiler error names an option, a result, exhaustiveness, mutability, or a module. Does not cover the build and test loop (see v-workflow), writing tests (see v-testing), reading a V project through the MCP server (see v-mcp), scripting in V (see v-scripts), or the wider command surface (see v-tools).\nlicense: MIT\n---\n\n# The V rules that bite\n\nMost mistakes an agent makes in V are not algorithmic. They are these rules, and\neach one fails in a way that does not look like the mistake.\n\n## Resource Routing\n\n- `references/OPTION-RESULT.md` - Read when deciding whether a function returns\n  `?T` or `!T`, or when an `or {}` block is not doing what you expected.\n- `references/SUMTYPES.md` - Read when modelling a closed set of states, or when\n  `match` reports a non-exhaustive match.\n- `references/COMPTIME.md` - Read when a symbol only exists on some platforms, or\n  when reaching for reflection or `$embed_file`.\n- `references/MUTABILITY.md` - Read when an assignment is rejected, or when a\n  mutation to a field does not reach the caller.\n\n## Quick Reference\n\n| You want | Write | Not |\n| --- | --- | --- |\n| A value that may be absent | `fn f() ?int` | `return 0` as a sentinel |\n| An operation that can fail | `fn f() !int` | `return -1` |\n| Unwrap, no default | `x or { return error('...') }` | `x or { 0 }` on a result |\n| Unwrap, with a default | `x or { 0 }` on an option | `x or { return }` on an option |\n| Branch on a closed set | `enum` + exhaustive `match` | a string constant |\n| Mutate an argument | `fn f(mut x T)` | `x = ...` silently failing |\n| Mutate the caller's struct | `fn (mut t &T)` | `fn (mut t T)` (a copy) |\n| Run code at compile time | `$if`, `$for` | a runtime `if` |\n\n## `?T` is not `!T`\n\n`?T` holds a value or `none`. `!T` holds a value or an error. They unwrap\ndifferently, and using one where the other belongs is a type error rather than a\nlogic error.\n\n```v ignore\n// An option: absence is part of the answer.\nfn find_port(cfg Config) ?int {\n    if !cfg.has_port {\n        return none\n    }\n    return cfg.port\n}\n\n// A result: the operation can fail, and the failure carries why.\nfn read_port(path string) !int {\n    lines := os.read_lines(path) or {\n        return error('cannot read ${path}: ${err.msg()}')\n    }\n    ...\n}\n```\n\nThe rule that catches people: inside `or { ... }` on a **result**, the block must\nproduce that result's error value, so it is `return error(...)` and not a bare\n`return`. On an **option**, `or { default }` supplies a value.\n\n**Default**: return `!T` for anything that touches the outside world, and `?T` for\na lookup that is legitimately absent. Full detail, including how to propagate and\nwhen a default is honest, in `references/OPTION-RESULT.md`.\n\n## Sum types and match\n\nV enums plus `match` are the way to model a closed set. `match` over an enum is\nchecked for exhaustiveness, which is the point: adding a variant turns every\nincomplete `match` into a compile error instead of a silent fallthrough.\n\nThe diagnostic is exact: `non-exhaustive match expression without `else``. Two\nways out — handle every variant, or add an `else` branch and accept that a new\nvariant changes nothing.\n\n```v ignore\nenum Status {\n    pending\n    running\n    done\n    failed\n}\n\nfn label(s Status) string {\n    return match s {\n        .pending { 'queued' }\n        .running { 'in progress' }\n        .done { 'finished' }\n        .failed { 'could not finish' }\n    }\n}\n```\n\nVariants are shared: `type Job = Status | string` is a sum type, and a `match`\nover it must handle both sides. See `references/SUMTYPES.md`.\n\n## mut is not optional\n\nFunction arguments are immutable. A receiver is immutable unless it is declared\n`mut`. This is the single most common V compile error, and it always has the same\ncause: the author assumed mutation was the default.\n\n```v ignore\n// Rejected: `app` is immutable.\n// fn serve(app App) { app.port = 8080 }\n\n// Accepted: the argument is mutable.\nfn serve(mut app App) { app.port = 8080 }\n\n// Accepted, and it reaches the caller: the receiver is a pointer.\nfn serve(app &App) { app.port = 8080 }\n\n// Accepted: the field itself is declared mutable.\nstruct Server {\nmut:\n    port int\n}\n```\n\n`mut` on a **value** receiver mutates a copy, so the caller's struct is\nunchanged. `mut` on a **pointer** receiver mutates the caller's. That difference\nis silent at the call site, which is why `references/MUTABILITY.md` exists.\n\n## Module names must match directories\n\n`module foo` must live in a directory named `foo`. A mismatch imports cleanly and\nthen resolves to nothing, with no error pointing at the name. The `module` line\ncarries no hierarchy — the directory nesting supplies it.\n\nThis is why a project built by `v new` has `src/main.v` with `module main` rather\nthan `module src`.\n\n## Comptime is `$`, and it excludes branches\n\nAnything starting with `$` runs while the compiler runs. The branches of an\n`$if` are **excluded entirely** on a platform that does not match, which is the\nonly reason a platform-specific import inside one is safe.\n\n```v ignore\n$if windows {\n    import sys.windows as wsys\n}\n$if linux {\n    import sys.linux\n}\n```\n\nA runtime `if` that mentions a platform-specific symbol will not compile on the\nother platforms, because there is no exclusion. Available in `$if`: `windows`,\n`linux`, `macos`, `js`, `freebsd`, `android`, `debug`, `prod`, and your own\n`$d custom_flag ? { ... }` — note the `?`.\n\n## No implicit conversions\n\n`int` is 32 bit and `i64` is 64. `f64` and `int` do not mix. Nothing converts\nimplicitly, which is deliberate: a silent narrowing is a bug you find later.\n\nBe explicit at the boundary, and prefer `strconv` over hand-rolled parsing.\n\n## Validation\n\nAfter changing V code, prove it rather than reading it again:\n\n```bash\nv -check path/to/file.v        # type-check only, no binary\nv fmt -verify path/to/file.v  # would the formatter change it?\n```\n\nFor library code rather than a `main` module, `-check` alone fails with *\"project\nmust include a `main` module\"*; use `v -check -shared path/to/file.v`.\n\nRun both before you report the change done. See `v-workflow` for the full loop.\n\n## Related Skills\n\n- **The build and test loop**: see [v-workflow](../v-workflow/SKILL.md) for\n  `v.mod`, dependencies, when `./v self` is required, and the flags that go\n  before the subcommand.\n- **Writing tests**: see [v-testing](../v-testing/SKILL.md) when the change needs\n  a `_test.v` file, or when `?T` and `!T` need asserting.\n- **Asking the compiler**: see [v-mcp](../v-mcp/SKILL.md) when you want the\n  declarations, references or diagnostics of a file without reading it, or when\n  you want to edit through an AST-aware rename.\n- **Memory and GC**: see [v-memory](../v-memory/SKILL.md) for GC modes, ownership\n  checking and `unsafe`.","frontmatter":{"name":"v-lang","description":"The V language rules that make code which looks right fail to compile or behave wrongly - ?T versus !T, sum types and match exhaustiveness, mut receivers, module names matching directories, comptime $ forms, and explicit type conversion. Use when writing or reviewing any V code that uses an Option, a Result, a sum type, a match, generics, or a custom flag, and when a V compiler error names an option, a result, exhaustiveness, mutability, or a module. Does not cover the build and test loop (see v-workflow), writing tests (see v-testing), reading a V project through the MCP server (see v-mcp), scripting in V (see v-scripts), or the wider command surface (see v-tools).","license":"MIT"},"isInternal":false,"tokens":1886,"sizeBytes":7173},{"name":"EDITING.md","path":"vlib/v/skills/v-mcp/references/EDITING.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-mcp/references/EDITING.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `vlib/v/skills/v-mcp/references/EDITING.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-mcp/references/EDITING.md)","tokens":300,"sizeBytes":189},{"name":"TOOLS.md","path":"vlib/v/skills/v-mcp/references/TOOLS.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-mcp/references/TOOLS.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `vlib/v/skills/v-mcp/references/TOOLS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-mcp/references/TOOLS.md)","tokens":300,"sizeBytes":185},{"name":"SKILL.md","path":"vlib/v/skills/v-mcp/SKILL.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-mcp/SKILL.md","title":"v-mcp","category":"anthropic-skill","format":"markdown","content":"---\nname: v-mcp\ndescription: Using the V MCP server, `v mcp serve`, to read and change V code through the compiler itself - the AST, declarations, references, diagnostics, stdlib docs, an AST-aware rename, a guarded edit and the formatter. Use when an agent needs to know what a V file declares, whether V code compiles, where a symbol is used, or what a stdlib function takes, and to make a change safely across files. Does not cover the language rules themselves (see v-lang), the build and test loop (see v-workflow), writing test cases (see v-testing), scripting a task in V (see v-scripts), or the command surface outside the MCP server (see v-tools).\nlicense: MIT\n---\n\n# The V MCP server\n\n`v mcp serve` speaks MCP over stdio, or over HTTP with `--http`. It is the V\ncompiler exposed as tools: it calls the parser, the checker and the formatter **in\nprocess**, so it answers correctly about code that does not compile yet.\n\n```json\n{\n  \"mcpServers\": {\n    \"v\": { \"command\": \"v\", \"args\": [\"mcp\", \"serve\"] }\n  }\n}\n```\n\n## Resource Routing\n\n- `references/TOOLS.md` - Read for the arguments of a specific tool, or when you\n  need the tool that answers a particular question.\n- `references/EDITING.md` - Read before changing anything through the server. It\n  covers the guards and why each one exists.\n\n## Quick Reference\n\n| Question | Tool |\n| --- | --- |\n| What project is this? | `v_project_info` |\n| What modules can I import? | `v_modules` |\n| What files are here? | `v_files` |\n| What does this file declare? | `v_symbols` |\n| What is at the cursor? | `v_symbol_at` |\n| Where is this used? | `v_references` |\n| What does this stdlib call take? | `v_stdlib_doc` |\n| Does it compile? | `v_check` |\n| Do the tests pass? | `v_test_run` |\n| What are this app's routes? | `v_veb_routes` |\n| Rename a symbol | `v_rename_symbol` |\n| Change a known range | `v_edit_replace` |\n| Format a file | `v_format` |\n\n## A working order\n\n1. **`v_project_info` once per session.** It names the module, the compiler, the\n   root every path is relative to, and whether this is the V checkout itself.\n2. **`v_symbols`** to learn what a file declares. It carries the doc comments, so\n   it usually answers \"what is this file for\" without reading the body.\n3. **`v_check` after every change.** It is the only authoritative answer to \"does\n   it compile\", and it returns records with file, line and column.\n4. **`v_test_run`** for behaviour. `v_check` cannot catch a wrong result.\n\n## Prefer asking over reading\n\nThe whole point of the server is that it knows more than a file read does. Reach\nfor it before guessing:\n\n- a signature you are unsure of: `v_stdlib_doc`, not an invented one\n- whether an edit is safe: `v_references`, then `v_rename_symbol`\n- what a file contains: `v_symbols`, not a full read\n- what broke: `v_check`, not a re-read\n\nAn invented stdlib signature costs a compile cycle at best. A hand-written rename\nmisses the comment that mentions the name and the call in a file you never opened.\n\n## Editing is guarded by default\n\nThree tools write. None of them writes unless asked twice:\n\n- `v_rename_symbol` defaults to `dry_run: true` and lists every position it would\n  change.\n- `v_format` defaults to `write: false` and reports `before` and `after`.\n- `v_edit_replace` requires `expected_old`: you read the range and pass it back\n  verbatim, so it refuses to write over a change you did not see.\n\nRead the plan, then pass the flag that applies it. See `references/EDITING.md`.\n\n`--read-only` does not register these three at all, so a session started that way\ncannot write by accident.\n\n## Before guessing, ask\n\n- **A language rule**: the `v-lang` skill covers the parts agents get wrong, most\n  of all `?T` versus `!T` and comptime code.\n- **A build or module question**: the `v-workflow` skill, and `v_modules` for what\n  is importable right now.\n- **The code's shape**: `v_ast`, in the same JSON `v ast -p` prints.\n\n## When the tools cannot answer\n\n`v_run`, `v_test_run`, `v_doctor` and `v_eval` start the compiler. They are\nslower than the in-process tools and unavailable when the compiler cannot be\nstarted. A tool that could not start the compiler says so explicitly — it reports\n`started: false` with an error rather than an exit code, so a sandbox that cannot\nspawn processes is never mistaken for a failed check.\n\nIf you see that, the answer is about the environment, not about the code. Do not\nstart changing the code to satisfy a tool that never ran.\n\n## Validation\n\nThe server is the checker. Do not finish a change without it:\n\n```json\n{\"name\": \"v_check\", \"arguments\": {\"path\": \"src/main.v\"}}\n```\n\nThen format what you touched with `v_format`, and run the tests with\n`v_test_run`. See [v-workflow](../v-workflow/SKILL.md) for the equivalent shell\ncommands.\n\n## Related Skills\n\n- **The language rules**: see [v-lang](../v-lang/SKILL.md) for `?T` versus `!T`,\n  sum types, `mut` and comptime code.\n- **The build loop**: see [v-workflow](../v-workflow/SKILL.md) for `v.mod`,\n  dependencies, flags and when `./v self` is required.\n- **Tests**: see [v-testing](../v-testing/SKILL.md) for what to assert.\n- **Web apps**: see [v-veb](../v-veb/SKILL.md) for veb, and use `v_veb_routes` to\n  read the routes of an app you have not opened.","frontmatter":{"name":"v-mcp","description":"Using the V MCP server, `v mcp serve`, to read and change V code through the compiler itself - the AST, declarations, references, diagnostics, stdlib docs, an AST-aware rename, a guarded edit and the formatter. Use when an agent needs to know what a V file declares, whether V code compiles, where a symbol is used, or what a stdlib function takes, and to make a change safely across files. Does not cover the language rules themselves (see v-lang), the build and test loop (see v-workflow), writing test cases (see v-testing), scripting a task in V (see v-scripts), or the command surface outside the MCP server (see v-tools).","license":"MIT"},"isInternal":false,"tokens":1385,"sizeBytes":5248},{"name":"OWNERSHIP.md","path":"vlib/v/skills/v-memory/references/OWNERSHIP.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-memory/references/OWNERSHIP.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `vlib/v/skills/v-memory/references/OWNERSHIP.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-memory/references/OWNERSHIP.md)","tokens":300,"sizeBytes":199},{"name":"UNSAFE.md","path":"vlib/v/skills/v-memory/references/UNSAFE.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-memory/references/UNSAFE.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `vlib/v/skills/v-memory/references/UNSAFE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-memory/references/UNSAFE.md)","tokens":300,"sizeBytes":193},{"name":"SKILL.md","path":"vlib/v/skills/v-memory/SKILL.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-memory/SKILL.md","title":"v-memory","category":"anthropic-skill","format":"markdown","content":"---\nname: v-memory\ndescription: V's memory model - GC modes (-gc boehm, -gc none, -prealloc), the optional -ownership checking that catches use-after-move, what unsafe unlocks, and the C interop rules. Use when choosing a GC mode, when a program leaks or grows without bound, when deciding whether unsafe is worth it, when passing a V pointer to C, or when a move or borrow error appears. Does not cover general language rules (see v-lang), data structure choice (see v-lang's structs and maps section), profiling (see v-workflow), scripting a task in V (see v-scripts), or the wider command surface (see v-tools).\nlicense: MIT\n---\n\n# Memory in V\n\nThe default needs no thought: the Boehm collector traces and frees what is no\nlonger reachable. Every question below only arises when you leave that default.\n\n## Resource Routing\n\n- `references/OWNERSHIP.md` - Read when `-ownership` is in play, or when you want\n  a use-after-move caught at compile time rather than at 3am.\n- `references/UNSAFE.md` - Read before writing any `unsafe` block, and before\n  passing a V pointer into C.\n\n## Quick Reference\n\n| Situation | Mode |\n| --- | --- |\n| An ordinary program | default (Boehm GC) — do nothing |\n| A short-lived batch job, or a self-contained binary | `-gc none` |\n| A compiler or batch tool | `-prealloc` |\n| You want use-after-move caught at compile time | `-ownership` |\n| Talking to a C library | `unsafe`, and keep it in one place |\n| Tuning, before guessing | `-stats run` |\n\n**Default**: leave the GC alone. `-gc none` and `-prealloc` are not performance\nswitches; they remove the collector and move the responsibility to you.\n\n## The three modes\n\n```bash\nv run main.v                    # Boehm tracing GC\nv -gc none -prod main.v         # no collector at all\nv -prealloc -prod main.v        # arena allocation\n```\n\n| Mode | Frees | Cost |\n| --- | --- | --- |\n| Boehm (default) | unreachable memory | tracing pauses; memory is not returned promptly |\n| `-gc none` | nothing, unless you do | you must free; no cycles; no finalisers |\n| `-prealloc` | everything, at exit | only good for one-shot single-threaded work |\n\n`-gc none` means no cycle collection, so a reference cycle leaks. For a program\nthat runs once and exits — a compiler, a code generator, a batch script — that\ntrade is usually worth it.\n\n`-prealloc` is documented as suitable only for short-lived, single-threaded,\nbatch-like programs. Do not reach for it in a server.\n\n**Race builds are the exception to all of it.** `-race` does not work with `-gc\nnone` or `-prealloc`: ThreadSanitizer has to see every allocation and free, which\na collector or an arena hides. So a program you want to race-test is a program you\nmust run under the default GC. It also needs `clang` or `gcc` with the\nThreadSanitizer runtime, and prefers `clang`, because gcc does not instrument\ncopies of whole struct values such as strings and arrays, so it misses races on\nthem.\n\n## Check before you optimise\n\n```bash\nv -stats run main.v\n```\n\nThat reports allocations. `-gc none` on a program that allocates heavily and\nexits quickly can be a large win; on a long-running one it is a leak generator.\nMeasure first.\n\n## Ownership: catching a move at compile time\n\nV has an optional ownership system, off by default, enabled with `-ownership`. It\ntracks owned values and rejects using one after it has been moved:\n\n```v ignore\nfn main() {\n\ts1 := 'hello'.to_owned()\n\ts2 := s1          // s1 is moved into s2\n\tprintln(s1)       // error: use of moved value: `s1`\n}\n```\n\nStrings created with `.to_owned()` or `.clone()`, ordinary string slices, and the\n`Owned` / `Copy` / `Drop` struct markers participate. Literals and primitives are\nunaffected; arbitrary structs, maps and slices are not tracked as owned on their\nown. `doc/ownership.md` is the reference; `references/OWNERSHIP.md` here is the summary.\n\n```bash\nv -ownership file.v        # check and compile\n```\n\nIt also defines a `ownership` compile-time flag, so `$if ownership ? { ... }` and\n`*_d_ownership.v` files select the enabled behaviour.\n\n## unsafe\n\n`unsafe` exists for C interop and for a handful of low-level primitives. It is not\na way to make the compiler stop complaining.\n\nThe rule: one `unsafe` block, as small as possible, with a comment saying why it is\nsound. If a function needs two, split it so each has one.\n\n```v ignore\nimport unsafe\n\n// Fixed-size stack buffer; the size must be a compile-time constant.\npub fn (b &Buf) fill(c byte) {\n\tunsafe {\n\t\tb.data = char(c)\n\t\tb.len = b.size\n\t}\n}\n```\n\nSee `references/UNSAFE.md` for what it does not check and what that costs.\n\n## C interop\n\nV's own rule is the one to follow: avoid `C.` and `JS.` in plain `.v` files and put\nthe interop in `.c.v` or `.js.v` files. Then the compiler can tell which code is\nplatform-specific, and `-Wimpure-v` catches an interop call that escaped.\n\n```v ignore\n// buffer_windows.c.v\nmodule main\n\nfn windows_only() int {\n\tunsafe {\n\t\treturn C.GetLastError()\n\t}\n}\n```\n\nA V pointer handed to C must not be freed or moved by the caller, and the C side\nmust not retain it after the V object goes out of scope. See `references/UNSAFE.md`.\n\n## Validation\n\n```bash\nv -ownership file.v            # catch a move at compile time\nv -stats run main.v            # measure before changing the mode\nv -check main.v                # plain, portable V\n```\n\nFor library code add `-shared`. See [v-workflow](../v-workflow/SKILL.md).\n\n## Related Skills\n\n- **The language rules**: see [v-lang](../v-lang/SKILL.md) for `mut` and for when\n  to reach for a struct rather than a map, which changes how much is allocated.\n- **The build loop**: see [v-workflow](../v-workflow/SKILL.md) for `-prod`,\n  `-stats` and the other flags this skill uses.\n- **Concurrency**: see [v-concurrency](../v-concurrency/SKILL.md) for the shared\n  state whose lifetime a lock has to protect.\n","frontmatter":{"name":"v-memory","description":"V's memory model - GC modes (-gc boehm, -gc none, -prealloc), the optional -ownership checking that catches use-after-move, what unsafe unlocks, and the C interop rules. Use when choosing a GC mode, when a program leaks or grows without bound, when deciding whether unsafe is worth it, when passing a V pointer to C, or when a move or borrow error appears. Does not cover general language rules (see v-lang), data structure choice (see v-lang's structs and maps section), profiling (see v-workflow), scripting a task in V (see v-scripts), or the wider command surface (see v-tools).","license":"MIT"},"isInternal":false,"tokens":1485,"sizeBytes":5843},{"name":"SUBCOMMANDS.md","path":"vlib/v/skills/v-package/references/SUBCOMMANDS.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-package/references/SUBCOMMANDS.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `vlib/v/skills/v-package/references/SUBCOMMANDS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-package/references/SUBCOMMANDS.md)","tokens":300,"sizeBytes":205},{"name":"SKILL.md","path":"vlib/v/skills/v-package/SKILL.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-package/SKILL.md","title":"v-package","category":"anthropic-skill","format":"markdown","content":"---\nname: v-package\ndescription: \"Manage V packages: install, update, search, remove and link local modules.\"\nlicense: MIT\n---\n\n# V packages with vpm\n\nUse this when adding dependencies, resolving a missing package, or working with\n`v install`, `v update`, `v search` or `v link`. For building and testing, see\n`v-workflow`; for language rules, see `v-lang`.\n\nInvoke package commands as `v <subcommand>`, such as `v install <module>`.\nThe frontend dispatches these commands to the `vpm` tool; `v pm` is not a package\nsubcommand.\n\n```bash\nv install <module>   # install a package\nv update             # update the installed packages\nv outdated           # which installed packages have a newer version\nv list               # what is installed\nv remove <module>    # remove a package\nv show <module>      # information about a package\nv search <keyword>   # search vpm.vlang.io\nv why <module>       # why a module is in the dependency graph\nv link               # symlink the current project into the modules directory\nv unlink             # remove that symlink\nv upgrade            # upgrade all outdated modules\n```\n\n## Where modules go\n\n`~/.vmodules`, or `$VMODULES` when it is set. A module is stored under its name\nwith `.` replaced by the path separator, so `prantlf.json` becomes\n`~/.vmodules/prantlf/json`.\n\n## Installing\n\n`v install <module>` installs a package into the modules directory. It does not\nadd the package to the project's `v.mod`; add the dependency there explicitly.\nRun `v install` without package arguments from the project directory to install\nthe dependencies already declared in `v.mod`.\n\nGit packages include their file contents in the initial clone, so checkout can\ncomplete without a second network request for missing file blobs. Submodules are\ninstalled recursively.\n\n## Removing\n\n`v remove <module>` refuses two cases rather than guessing: a module outside the\nmodules directory, and one that was not installed by vpm. For a linked module the\nanswer is `v unlink` first.\n\n## Linking a local module\n\n`v link` symlinks the current project into the modules directory, so a change\nthere is seen without reinstalling. `v unlink` removes the symlink.\n\n## Resource Routing\n\n- `references/SUBCOMMANDS.md` - Read when a subcommand is missing, ignored, or\n  behaves differently from this summary.\n\n## Quick Reference\n\n```bash\nv install <module>\nv update\nv outdated\nv list\nv remove <module>\nv show <module>\nv search <keyword>\nv why <module>\nv link\nv unlink\nv upgrade\n```\n","frontmatter":{"name":"v-package","description":"Manage V packages: install, update, search, remove and link local modules.","license":"MIT"},"isInternal":false,"tokens":591,"sizeBytes":2491},{"name":"TEMPLATES.md","path":"vlib/v/skills/v-project/references/TEMPLATES.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-project/references/TEMPLATES.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `vlib/v/skills/v-project/references/TEMPLATES.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-project/references/TEMPLATES.md)","tokens":300,"sizeBytes":201},{"name":"SKILL.md","path":"vlib/v/skills/v-project/SKILL.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-project/SKILL.md","title":"v-project","category":"anthropic-skill","format":"markdown","content":"---\nname: v-project\ndescription: Create V projects with v new or v init and choose a binary, library or web template.\nlicense: MIT\n---\n\n# Creating a V project\n\nUse this when starting a project, scaffolding a module, or choosing a template.\nFor building and testing, see `v-workflow`; for language rules, see `v-lang`; for\nother commands, see `v-tools`.\n\nTwo commands, one decision: `v new` makes a directory, `v init` works in the one\nyou are already in.\n\n## The decision\n\n- `v new <name>` — creates `<name>/` and puts the project in it.\n- `v init` — puts the project in the current directory. Use this when the\n  directory already exists.\n\nBoth take the same three template flags, and both create a `v.mod`.\n\n## Put template flags before the project name\n\nTemplate flags belong to the `new` or `init` subcommand. For `new`, put them\nafter `new` and before the project name:\n\n```bash\nv new myapp           # executable, the default\nv new --lib mylib     # library\nv new --web myapp     # veb web app\n```\n\n`v new mylib --lib` fails with *\"too many arguments\"*: flag parsing stops at the\nproject name, so the later flag is read as another argument.\n\n## What each template makes\n\n| Flag | Entry point | Module | Extra |\n| --- | --- | --- | --- |\n| `--bin` (default) | `main.v` | `main` | — |\n| `--lib` | `<name>.v` | `<name>` | `tests/<fn>_test.v` |\n| `--web` | `main.v` | `main` | `assets/main.css`, `templates/index.html` |\n\nAll three also create `v.mod`, `.editorconfig`, `.gitattributes` and\n`.gitignore`.\n\n## v.mod\n\n```\nModule {\n\tname: 'myapp'\n\tdescription: ''\n\tversion: '0.0.0'\n\tlicense: 'MIT'\n\tdependencies: []\n}\n```\n\nCreating one starts a prompt for the description, version and license. **The\nprompt only runs when stdin is a terminal.** With piped or redirected stdin,\nthe defaults are used. A non-interactive\n`v new` requires the project name; `v init` derives it from the current directory.\nFor example:\n\n```bash\nv new myapp < /dev/null\n```\n\nIf git is installed and the directory is not already a git project, `git init`\nruns as part of setup.\n\n## After it exists\n\n```bash\ncd myapp\nv run .          # build and run an executable or web project\nv -check .       # type-check only\nv test .         # run the tests\n```\n\nThe module name is `main` for executable and web templates. For a library, the\nmodule name is the project name.\n\n## Resource Routing\n\n- `references/TEMPLATES.md` - Read when choosing between `--bin`, `--lib` and\n  `--web`, or when a template's contents need checking against the current\n  compiler.\n\n## Quick Reference\n\n```bash\nv new <name>          # executable project in <name>/\nv new --lib <name>    # library project\nv new --web <name>    # veb project\nv init                # project in the current directory\nv init --lib          # library in the current directory\n```\n","frontmatter":{"name":"v-project","description":"Create V projects with v new or v init and choose a binary, library or web template.","license":"MIT"},"isInternal":false,"tokens":743,"sizeBytes":2808},{"name":"SKILL.md","path":"vlib/v/skills/v-scripts/SKILL.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-scripts/SKILL.md","title":"v-scripts","category":"anthropic-skill","format":"markdown","content":"---\nname: v-scripts\ndescription: Writing V as a scripting language - `.vsh` script mode, its implicit `import os` and unqualified `os` calls, running a script directly, the executable-cache contract that governs rebuilds, compiling a script without running it, and shebang scripts with no file extension. Use when a task is a build, release, maintenance or one-off automation job that would otherwise be bash, when deciding between a script and a small program, or when a `.vsh` file behaves differently from a `.v` file. Does not cover the language rules (see v-lang), the build and test loop (see v-workflow), writing test cases (see v-testing), or the wider command surface (see v-tools).\nlicense: MIT\n---\n\n# V scripts\n\nA file ending in `.vsh` is parsed in **script mode**. It is still V — the same\ntypes, the same modules, the same compiler — but two rules make it read like a\nshell script, and both are worth knowing before writing one, because they are\nwhy a script can fail in a way a program would not.\n\n## What script mode changes\n\n**`os` is imported for you.** A `.vsh` file gets an implicit `import os` when it\ndoes not already import `os` itself. If it does import `os` explicitly, that\nimport wins and keeps its own alias and selected symbols; the implicit one is\nnever added on top.\n\n**`os` functions are called unqualified.** In script mode an unqualified\n`read_file` resolves to `os.read_file`, so the usual `os.` prefix is optional:\n\n```v ignore\nprintln('hello')\ncontent := read_file('data.txt') or { panic(err) }\n```\n\nThat resolution reaches public `os` functions and constants (for example\n`args`). A local or parameter of the same name shadows it, so naming a variable\n`args` or `exists` changes what the bare name means — and that shadowing is\nscoped to the script, not to a `.v` file compiled beside it.\n\nNeither rule applies to a plain `.v` file. If a name resolves in a `.vsh` but not\nin a `.v` file, this is why.\n\n## Running and building scripts\n\nRunning the file runs the script; there is no separate interpreter to install.\n\n```sh\nv script.vsh                 # build if needed, then run\nv run script.vsh             # rebuild and run, keep no executable\nv build script.vsh           # compile to an executable, do not run it\nv -skip-running script.vsh   # the same thing\n```\n\nOn Unix-like systems a script that starts with this shebang runs by its path,\nonce it is executable (`chmod +x script.vsh`, then `./script.vsh`):\n\n```sh ignore\n#!/usr/bin/env -S v\n```\n\nFor `.vsh` scripts on systems whose `/usr/bin/env` has no `-S` (BusyBox,\nOpenBSD), put `cmd/tools/vrun` on the PATH and start the script with\n`#!/usr/bin/env vrun` instead; it runs the script with `v run`.\n\n`v script.vsh` follows **V's executable-cache contract**: it builds the script\nas `script` (the name without `.vsh`) next to the source and reuses that binary\non the next run, which is what makes repeated invocation cheap. It rebuilds when\nthe script is newer than the binary, or when an imported module changed. The\n`#!/usr/bin/env -S v` shebang goes through the same cache.\n\n`v run script.vsh` skips the cache: it rebuilds on every run and leaves no\nexecutable behind, and so do the `#!/usr/bin/env -S v run` and `vrun` shebangs.\nThe cached binary has to be deleted by hand: `v clean` handles `.v` files and\ndirectories, not `.vsh` scripts.\n\n## Scripts with no extension\n\nA file with a fully custom name and a shebang runs as a script when it starts\nwith:\n\n```sh ignore\n#!/usr/bin/env -S v -raw-vsh-tmp-prefix tmp\n```\n\n`tmp` is the prefix for the built executable, which is kept as\n`tmp.<filename>` beside the script. **Caution:** that name is overwritten if it\nalready exists. To rebuild every time instead of caching, use\n`#!/usr/bin/env -S v -raw-vsh-tmp-prefix tmp run`.\n\nThis runs in `crun` mode. V's own documentation recommends it for scripts that go\non the PATH and advises against it for build or deploy scripts.\n\n## Choosing between a script and a program\n\nReach for a script when the job is sequencing external tools, moving files,\nwatching a directory, or wrapping a build step. Reach for a program when there is\ndomain logic to test, when it needs a data structure, or when it will be imported.\n\nThe test suite exists to keep that boundary honest: if a `.vsh` has grown logic\nworth asserting on, move it into a `_test.v` alongside a real program. Script\nmode does not make a script untestable — it compiles like anything else.\n\n## Worked examples in the tree\n\nThese scripts rely on script mode — no `import os`, unqualified `os` calls,\nstatements at the top level — and are worth reading before writing one:\n\n- `examples/v_script.vsh` — creates, lists and removes files with `mkdir`, `ls`\n  and `rm`\n- `.github/problem-matchers/register_all.vsh` — reads `getenv`, then walks a\n  folder with `walk_ext`\n\nA larger `.vsh` can also be written like a program, with `import os` and a\n`fn main`. The scripts bundled with the skills are written that way, for example\n`vlib/v/skills/v-workflow/scripts/check.vsh`; their `#!/usr/bin/env -S v run`\nshebang leaves no binary in the skill folder.\n\n## Related Skills\n\n- **The commands around a script**: see [v-tools](../v-tools/SKILL.md) for\n  `v time`, `v retry`, `v repeat` and `v check-md`.\n- **The build and test loop**: see [v-workflow](../v-workflow/SKILL.md).\n- **The language rules**: see [v-lang](../v-lang/SKILL.md); a script is still V.","frontmatter":{"name":"v-scripts","description":"Writing V as a scripting language - `.vsh` script mode, its implicit `import os` and unqualified `os` calls, running a script directly, the executable-cache contract that governs rebuilds, compiling a script without running it, and shebang scripts with no file extension. Use when a task is a build, release, maintenance or one-off automation job that would otherwise be bash, when deciding between a script and a small program, or when a `.vsh` file behaves differently from a `.v` file. Does not cover the language rules (see v-lang), the build and test loop (see v-workflow), writing test cases (see v-testing), or the wider command surface (see v-tools).","license":"MIT"},"isInternal":false,"tokens":1375,"sizeBytes":5410},{"name":"COVERAGE.md","path":"vlib/v/skills/v-testing/references/COVERAGE.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-testing/references/COVERAGE.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `vlib/v/skills/v-testing/references/COVERAGE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-testing/references/COVERAGE.md)","tokens":300,"sizeBytes":199},{"name":"PATTERNS.md","path":"vlib/v/skills/v-testing/references/PATTERNS.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-testing/references/PATTERNS.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `vlib/v/skills/v-testing/references/PATTERNS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-testing/references/PATTERNS.md)","tokens":300,"sizeBytes":199},{"name":"TROUBLESHOOTING.md","path":"vlib/v/skills/v-testing/references/TROUBLESHOOTING.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-testing/references/TROUBLESHOOTING.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `vlib/v/skills/v-testing/references/TROUBLESHOOTING.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-testing/references/TROUBLESHOOTING.md)","tokens":300,"sizeBytes":213},{"name":"SKILL.md","path":"vlib/v/skills/v-testing/SKILL.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-testing/SKILL.md","title":"v-testing","category":"anthropic-skill","format":"markdown","content":"---\nname: v-testing\ndescription: Writing and running V tests - _test.v layout, test_ function naming, the assert builtin, fixtures and cleanup with os.vtmp_dir and defer, and the VTEST_ONLY and VTEST_ONLY_FN filters for a subset. Use when adding or changing a V test, when a test does not compile or does not run, when choosing what to assert, or when the suite is slow and only part of it is needed. Does not cover the rest of the build loop (see v-workflow), the option and result semantics a test has to assert (see v-lang), reading a project through the MCP server (see v-mcp), scripting in V (see v-scripts), or the wider command surface (see v-tools).\nlicense: MIT\n---\n\n# Writing and running V tests\n\nA V test is a `test_` prefixed function in a `_test.v` file. There is no test\nframework and no test object: the function takes no parameters, and `assert` is a\nbuiltin.\n\n## Resource Routing\n\n- `references/PATTERNS.md` - Read when writing a fixture, a table-driven test, or\n  anything that needs a temporary file and cleanup.\n- `references/TROUBLESHOOTING.md` - Read when a test does not compile, does not\n  run, or does not fail the way it should.\n- `references/COVERAGE.md` - Read when measuring which lines and functions a run\n  reached, or generating the tests that are missing.\n\n## Quick Reference\n\n| Need | Do |\n| --- | --- |\n| A test | `fn test_the_thing() { ... }` in `foo_test.v` |\n| Compare | `assert got == want, 'message'` |\n| Test an option | `assert f() == none` |\n| Test a result | `assert f() == none` — it fails with the error text |\n| A temporary file | `os.join_path(os.vtmp_dir(), 'name_${stamp}')` |\n| Cleanup | `defer { os.rm(path) or {} }` |\n| Only some files | `VTEST_ONLY='http' v test dir/` |\n| Only some functions | `VTEST_ONLY_FN='test_login' v test dir/` |\n| Hide passing tests | `v -silent test dir/` |\n| Timings | `v -stats test dir/` |\n| Line coverage | `v -no-skip-unused -coverage vcov/ test dir/` then `v cover vcov/` |\n| Untested functions | `v cover vcov/ --uncovered` |\n| Missing test bodies | `v cover vcov/ --write-tests --filter myapp/` |\n\n## Layout\n\n`foo.v` is tested by `foo_test.v` beside it. A test file holds only `test_`\nfunctions, imports, and helpers.\n\n```v ignore\n// foo_test.v\nimport foo\n\nfn test_it_adds() {\n\tassert add(2, 3) == 5\n}\n\nfn test_it_does_not_mutate_its_argument() {\n\tmut n := 3\n\tincrement(n)\n\tassert n == 3, 'increment changed its argument to ${n}'\n}\n```\n\n**A test function takes no parameters.** Not `*testing.T`, not a context. If you\nare porting a Go or Rust test, this is the first thing to change.\n\nA test file in a library directory does **not** declare `module main`. It imports\nwhat it needs, like any other file. Only a test file that is the `main` module of\nits project says `module main`.\n\n## Assertions say what they compared\n\nA bare `assert` failure prints `assertion failed` and a line number. That is not\nenough to act on.\n\n```v ignore\n// Weak: `assertion failed` and nothing else.\nassert lookup(users, id) == expected\n\n// Strong: the message is the difference.\ngot := lookup(users, id)\nassert got == expected, 'lookup(${id}) = ${got}, want ${expected}'\n```\n\nPut **got before want**. It reads in the order the mind works, and it makes a\nfailing suite scannable.\n\nAssert on values, never on message text, which is free to change:\n\n```v ignore\n// Brittle.\nassert read_config(x) == none\n\n// Correct: absence is the contract.\nif cfg := read_config(x) {\n    assert false, 'expected no config, got ${cfg}'\n}\n```\n\n## Running a subset\n\nThe full suite is slow. Filter it rather than paying for all of it:\n\n```bash\nVTEST_ONLY='http' v test dir/          # only files whose path contains http\nVTEST_ONLY_FN='test_login' v test dir/ # only functions matching\nv -silent test dir/                    # hide passing tests\nv -stats test dir/                     # add timings\n```\n\nBoth filters accept a comma-separated list. File filters use path fragments;\nfunction filters use glob patterns. If a filter matches nothing, the raw runner\nreports zero tests and exits successfully. The wrapper uses the normal reporter\nfor its child runs and preserves other `VFLAGS` options. It reports an empty\nselection as a failure, including when no filter is set:\n\n```bash\nv run scripts/run-tests.vsh dir/ --file http --fn 'test_login*'\n```\n\nSee `references/TROUBLESHOOTING.md` for other causes of tests not running.\n\n## Measuring coverage\n\n```bash\nv -no-skip-unused -coverage vcov/ test ./   # record what the run entered\nv cover vcov/                                # one percentage per file\n```\n\n`-no-skip-unused` is not optional. Without it a function nothing calls is\ndropped from the build, and the report cannot tell \"not tested\" from \"not\nbuilt\".\n\nLine coverage cannot name a function no test calls when that function's body\nholds no statement a line point would record. Two flags answer it:\n\n```bash\nv cover vcov/ --uncovered    # one `path:line function` per untested function\nv cover vcov/ --write-tests --filter myapp/   # the test bodies it is missing\n```\n\nThe generator writes a starting point, not a test — a call with zero values and\na `// TODO:` line, because it cannot know what the function should return. It\nappends to an existing `_test.v` and never touches a test function that is\nalready there. `--dry-run` shows the plan.\n\nSee [references/COVERAGE.md](references/COVERAGE.md) for the whole surface,\nincluding `--functions`, `--json` and what `--write-tests` refuses to do.\n\n## Fixtures\n\n\nThere is no test object to hang a fixture on, so a test writes to `os.vtmp_dir()`\nand registers the removal with `defer`. A unique name per test keeps parallel runs\nfrom colliding.\n\n```v ignore\nfn test_it_reads_a_config() {\n\tstamp := os.getpid()\n\tpath := os.join_path(os.vtmp_dir(), 'cfg_${stamp}.json')\n\tos.write_file(path, '{\"port\": 8080}') or {\n\t\tassert false, 'could not write ${path}: ${err.msg()}'\n\t}\n\tdefer {\n\t\tos.rm(path) or {}\n\t}\n\t...\n}\n```\n\nSee `references/PATTERNS.md` for table-driven tests, sub-suites and helpers.\n\n## Validation\n\nA test that has never failed is not known to test anything. Break it on purpose\nonce, watch it fail for the right reason, then put it back.\n\n```bash\nVTEST_ONLY_FN='test_the_new_thing' v test path/to/file_test.v\nv -silent test path/to/dir/\n```\n\nThen `v fmt -verify path/to/file_test.v`. See [v-workflow](../v-workflow/SKILL.md).\n\n## Related Skills\n\n- **The build loop**: see [v-workflow](../v-workflow/SKILL.md) for `-check`,\n  `fmt -verify`, `vet` and the flags that gate a change.\n- **Option and Result semantics**: see [v-lang](../v-lang/SKILL.md) for what to\n  assert when a function returns `?T` or `!T`.\n- **Through the MCP server**: see [v-mcp](../v-mcp/SKILL.md) when `v_test_run` can\n  report what passed and what the failures said, without parsing runner output.\n","frontmatter":{"name":"v-testing","description":"Writing and running V tests - _test.v layout, test_ function naming, the assert builtin, fixtures and cleanup with os.vtmp_dir and defer, and the VTEST_ONLY and VTEST_ONLY_FN filters for a subset. Use when adding or changing a V test, when a test does not compile or does not run, when choosing what to assert, or when the suite is slow and only part of it is needed. Does not cover the rest of the build loop (see v-workflow), the option and result semantics a test has to assert (see v-lang), reading a project through the MCP server (see v-mcp), scripting in V (see v-scripts), or the wider command surface (see v-tools).","license":"MIT"},"isInternal":false,"tokens":1789,"sizeBytes":6789},{"name":"SKILL.md","path":"vlib/v/skills/v-tools/SKILL.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-tools/SKILL.md","title":"v-tools","category":"anthropic-skill","format":"markdown","content":"---\nname: v-tools\ndescription: The parts of the V command surface that are easy to miss - inspecting code (`v ast`, `v where`, `v mod why`), shrinking a compiler error (`v reduce`), project hygiene (`v missdoc`, `v check-md`, `v bump`, `v git-fmt-hook`), embedding binaries (`v bin2v`), shell integration (`v complete`), timing and repeating commands (`v time`, `v repeat`, `v retry`), and compiler upkeep (`v up`, `v clean`, `v env`, `v tracev`). Use when a task is about the toolchain rather than the program, when reaching for `v` by habit and a better command exists, or when asked what V can do. Does not cover writing `.vsh` scripts (see v-scripts), the language rules (see v-lang), the build and test loop (see v-workflow), or reading a project through the MCP server (see v-mcp).\nlicense: MIT\n---\n\n# V tools\n\nMost of V's command surface is reached by habit. This is the part that is not:\ncommands that answer a question, shrink a failure, or keep a project honest.\n`v help topics` lists everything; the ones below are the ones worth knowing.\n\n## Look at code before changing it\n\n```sh\nv ast -p file.v         # print the AST as JSON; without -p it writes file.json\nv where fn NAME         # where a symbol is declared; also struct, method, enum, const\nv mod why MODULE        # which import chain pulls a module into the build\n```\n\n`v where` finds declarations only, and searches vlib as well as the project;\n`-mod main` keeps it to the project. Where a symbol is used is answered by the\n`v_references` tool of the MCP server.\n\n`v mod why` is the one that answers a question people otherwise guess at: nothing\nin `v.mod` records what a build actually reaches, so a dependency can outlive the\ncode that needed it.\n\nThrough the MCP server the same ground is covered with less shell work; see\nv-mcp.\n\n## Shrink a compiler error\n\n```sh\nv reduce -m 'text of the error' file.v   # the smallest source that still errors\n```\n\nWithout `-m`, `v reduce` only looks for a C compilation error, so a checker error\nsuch as `unknown function: nope` needs its text passed in. The reduction lands in\n`rpdc_<file>.v` (`rpdc_bad.v` for `bad.v`) in the current directory. Pair it with\n`v bug`, which opens a prefilled issue, and the report carries a minimal\nreproducer instead of the file you happened to be editing.\n\n## Measure test coverage\n\n```sh\nv -no-skip-unused -coverage vcov/ test ./   # record what the run entered\nv cover vcov/                               # one percentage per file\nv cover vcov/ -H                            # the hottest lines\nv cover vcov/ --lcov out.info               # the format CI tools read\nv cover vcov/ --uncovered                   # `path:line function` per untested function\nv cover vcov/ --functions                   # every function, covered or not\nv cover vcov/ --json report.json            # machine-readable, both levels\nv cover vcov/ --write-tests --filter dir/   # the test bodies it is missing\n```\n\nTwo steps: the run records, `v cover` reads. Delete the directory between runs —\nthe counters are appended, so a stale one reports coverage that no longer\nexists.\n\n`--write-tests` generates a `test_` function per untested function, beside its\nsource, as a starting point and not a test: a call with zero values and a\n`// TODO:` line. It appends to an existing `_test.v`, leaves a test function\nthat is already there alone, and refuses without `--filter`, because a coverage\ndirectory holds every file a run reached.\n\nSee [v-testing](../v-testing/SKILL.md) for the details.\n\n## Project hygiene\n\n```sh\nv missdoc src/          # public functions with no documentation comment\nv check-md README.md    # are the ```v blocks in this markdown correct?\nv bump --patch          # or --minor / --major; bumps the version in v.mod\nv git-fmt-hook install  # format on commit\n```\n\n`v check-md` is worth knowing about specifically: it compiles the V examples in a\nmarkdown file, which catches a snippet that stopped compiling long after anyone\nlooked at the prose. `v missdoc` takes `-p` to include private functions and `-t`\nto include function tags.\n\n## Embed binary files\n\n```sh\nv bin2v -m main -w assets.v assets/logo.png assets/font.ttf\n```\n\nWrites each file into `assets.v` as a byte-array constant (`logo_png`, with its\nlength in `logo_png_len`), so a program carries its images and fonts instead of\nreading them from disk at runtime. Without `-w` the source goes to stdout. The\nconstants are private, so generate them into the module that uses them. For a\nsingle file, `$embed_file('assets/logo.png')` does the same at compile time.\n\n## Shell integration\n\n```sh\nv complete setup bash   # print the completion setup script; bash, fish, zsh, powershell\n```\n\nLoad it from the shell's startup file; for bash, add\n`source /dev/stdin <<<\"$(v complete setup bash)\"` to `~/.bashrc`.\n\n## Time, repeat, retry\n\n```sh\nv time CMD              # how long CMD took, and what it exited with\nv repeat CMD...         # repeat commands and collect statistics\nv retry CMD             # rerun until it succeeds, for up to 10 retries or 10 minutes\n```\n\n`v retry` is the one to reach for when a flaky network call or a busy port is the\nthing in the way; `-r` and `-t` change its limits. `v time` is the one when the\nquestion is which of two approaches is actually cheaper.\n\n## Compiler upkeep\n\n```sh\nv up                    # update V itself\nv clean                 # remove what a default build leaves behind\nv env                   # the environment variables that steer the compiler\nv tracev                # a tracing build of the compiler\n```\n\n`v env` is the first thing to read when the compiler or one of its tools behaves\ndifferently on another machine, because most of that behaviour is an environment\nvariable. `v help build-c` documents the C-backend flags — `-cc`, `-glibc`,\n`-musl` — when the C toolchain itself is the thing in question.\n\n## Other commands worth knowing\n\n```sh\nv ls                    # install and run the V language server\nv repl                  # the V REPL\nv watch file.v          # rebuild when the sources it needs change\nv crun file.v           # build and run, keeping the executable\nv tool NAME             # run a tool module by name\nv share file.v          # send a .v file to the V Playground\nv download URL          # fetch a file over http or https\nv symlink               # put v on PATH\n```\n\n`v download -RD URL` also runs the downloaded file with `v run` and deletes it\nafterwards. That executes remote code, so read the file first.\n\nPackage management is `v search`, `v show`, `v list`, `v outdated`, `v update`,\n`v upgrade` and `v remove`, over the VPM registry.\n\n## The command that installs this skill\n\n```sh\nv skills list           # what is bundled, and what is installed\nv skills add v-tools    # install one\nv skills update         # refresh what a new V revision changed\n```\n\n## Related Skills\n\n- **Scripts**: see [v-scripts](../v-scripts/SKILL.md) for `.vsh` script mode and\n  its executable cache.\n- **The build and test loop**: see [v-workflow](../v-workflow/SKILL.md).\n- **The language rules**: see [v-lang](../v-lang/SKILL.md).\n- **Reading a project through the compiler**: see [v-mcp](../v-mcp/SKILL.md) for\n  declarations, references and diagnostics without shell commands.","frontmatter":{"name":"v-tools","description":"The parts of the V command surface that are easy to miss - inspecting code (`v ast`, `v where`, `v mod why`), shrinking a compiler error (`v reduce`), project hygiene (`v missdoc`, `v check-md`, `v bump`, `v git-fmt-hook`), embedding binaries (`v bin2v`), shell integration (`v complete`), timing and repeating commands (`v time`, `v repeat`, `v retry`), and compiler upkeep (`v up`, `v clean`, `v env`, `v tracev`). Use when a task is about the toolchain rather than the program, when reaching for `v` by habit and a better command exists, or when asked what V can do. Does not cover writing `.vsh` scripts (see v-scripts), the language rules (see v-lang), the build and test loop (see v-workflow), or reading a project through the MCP server (see v-mcp).","license":"MIT"},"isInternal":false,"tokens":1791,"sizeBytes":7253},{"name":"MIDDLEWARE.md","path":"vlib/v/skills/v-veb/references/MIDDLEWARE.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-veb/references/MIDDLEWARE.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `vlib/v/skills/v-veb/references/MIDDLEWARE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-veb/references/MIDDLEWARE.md)","tokens":300,"sizeBytes":195},{"name":"ROUTES.md","path":"vlib/v/skills/v-veb/references/ROUTES.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-veb/references/ROUTES.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `vlib/v/skills/v-veb/references/ROUTES.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-veb/references/ROUTES.md)","tokens":300,"sizeBytes":187},{"name":"SKILL.md","path":"vlib/v/skills/v-veb/SKILL.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-veb/SKILL.md","title":"v-veb","category":"anthropic-skill","format":"markdown","content":"---\nname: v-veb\ndescription: Writing V web applications with veb - the App and Context structs, route attributes with path parameters, ctx.html and ctx.json, templates with $tmpl and $veb.html, middleware with Middleware.use and route_use, shared state with shared and lock, and static file mounting. Use when creating or changing a veb server, adding a route or handler, returning JSON or HTML, adding middleware, or serving files. Does not cover concurrency in general (see v-concurrency), the language rules (see v-lang), the build loop (see v-workflow), scripting a task in V (see v-scripts), or the wider command surface (see v-tools).\nlicense: MIT\n---\n\n# veb, V's web framework\n\nA veb server is two structs and a `main`. Handlers are methods on `App`, and every\nrequest runs with the whole `App` shared across threads.\n\n## Resource Routing\n\n- `references/ROUTES.md` - Read when adding a route, a path parameter, or a\n  response body.\n- `references/MIDDLEWARE.md` - Read when adding cross-cutting behaviour, shared\n  state, or static files.\n\n## Quick Reference\n\n| Need | Do |\n| --- | --- |\n| A route | `@['/path']` above a `pub fn (mut app App) handler(mut ctx Context) veb.Result` |\n| A path parameter | `@['/users/:id']` and an extra `id string` argument |\n| A specific method | `@[post]` or `@['/upload'; post]` |\n| HTML | `return $veb.html()` or `return $veb.html('page.html')` |\n| JSON | `return ctx.json(payload)` |\n| Plain text | `return ctx.text('...')` |\n| Middleware | `app.Middleware.use(handler: f)` |\n| Middleware for a path | `app.Middleware.route_use('/admin/:path...', handler: f)` |\n| Shared state | `field shared T`, mutated inside `lock app.state { }` |\n| Serve files | `app.mount_static_folder_at(...)` |\n| Serve a folder at root | `app.handle_static(directory_path, root)` |\n\n## The shape of a server\n\n```v ignore\nimport veb\n\nconst port = 8080\n\npub struct App {}\n\nstruct Context {\n\tveb.Context\n}\n\nfn main() {\n\tmut app := &App{}\n\tveb.run[App, Context](mut app, port)\n}\n\n@['/']\npub fn (mut app App) index(mut ctx Context) veb.Result {\n\treturn ctx.text('hello')\n}\n\n@[post]\npub fn (mut app App) submit(mut ctx Context) veb.Result {\n\treturn ctx.json({ok: true})\n}\n```\n\nThree things to notice:\n\n- `App` is `pub` and `Context` embeds `veb.Context`. Both are type arguments to\n  `veb.run`, which is how veb knows your types.\n- A handler takes `mut ctx Context` and returns `veb.Result`. Return the result of\n  a `ctx` method rather than constructing one.\n- `mut app` in the signature means the method may mutate `App`. A read-only handler\n  takes `app &App`, and veb accepts that too.\n\n## Routes\n\nThe attribute is a list of paths. Without a leading `/`, it is an HTTP method with\nthe default path.\n\n```v ignore\n@['/users/:user']\npub fn (mut app App) user_endpoint(mut ctx Context, user string) veb.Result {\n\treturn ctx.json({user: user})\n}\n\n@[post]\npub fn (mut app App) create(mut ctx Context) veb.Result { ... }\n\n@['/upload'; post]\npub fn (mut app App) upload(mut ctx Context) veb.Result { ... }\n```\n\nA `:name` in the path becomes an argument **after** `ctx`. A `:name...` at the end\ncaptures the rest of the path.\n\nSee `references/ROUTES.md` for matching rules and ordering.\n\n## Shared state\n\n`App` is shared across request threads, so a mutable field needs to say so, and\nevery write needs a lock:\n\n```v ignore\npub struct App {\nmut:\n\tstate shared State\n}\n\nstruct State {\nmut:\n\thits int\n}\n\npub fn (mut app App) index(mut ctx Context) veb.Result {\n\tlock app.state {\n\t\tapp.state.hits++\n\t}\n\treturn ctx.text('${app.state.hits}')\n}\n```\n\n`shared` is what makes the field shared rather than per-request. Omitting it, or\nwriting without the `lock`, is a data race — and it is the mistake that matters\nmost in a veb app.\n\n## Middleware\n\nMiddleware wraps requests. Global for the app, or scoped to a path:\n\n```v ignore\nstruct App {\n\tveb.Middleware[Context]\n}\n\nfn main() {\n\tmut app := &App{}\n\t// Chained, evaluated in order.\n\tapp.Middleware.use(handler: other_func1)\n\tapp.Middleware.use(handler: add_headers)\n\t// Only for matching paths.\n\tapp.Middleware.route_use('/admin/:path...', handler: check_auth)\n\tveb.run[App, Context](mut app, http_port)\n}\n```\n\nA middleware handler takes `mut ctx Context` and returns a `bool`. Return `true` to\ncontinue the chain; return `false` to stop it, which is how a middleware short\ncircuits a request.\n\nSee `references/MIDDLEWARE.md`.\n\n## Templates\n\n`$veb.html()` renders the templates directory; `$veb.html('name.html')` renders one\nfile; `$tmpl` renders a string in memory, which is how a layout is composed with a\npage:\n\n```v ignore\n@['/']\npub fn (app &App) index(mut ctx Context) veb.Result {\n\tcontent := $tmpl('templates/index.html')\n\tbase := $tmpl('templates/base.html')\n\treturn ctx.html(base)\n}\n```\n\n## Validation\n\nA veb app is checked the same way as any V code, and `-check` catches most of it —\nincluding a handler whose signature does not match what veb expects:\n\n```bash\nv -check -shared path/to/veb_app.v     # library-shaped: needs -shared\nv fmt -verify path/to/veb_app.v\nv run path/to/main.v\n```\n\nSee [v-workflow](../v-workflow/SKILL.md) for the full loop.\n\n## Related Skills\n\n- **Concurrency**: see [v-concurrency](../v-concurrency/SKILL.md) for what\n  `shared` means and the lock discipline that goes with it.\n- **The build loop**: see [v-workflow](../v-workflow/SKILL.md) for `-check`,\n  `-shared`, `fmt -verify` and the other flags.\n- **The language rules**: see [v-lang](../v-lang/SKILL.md) for `mut` and for\n  `?T` versus `!T` when a handler has to return an error.","frontmatter":{"name":"v-veb","description":"Writing V web applications with veb - the App and Context structs, route attributes with path parameters, ctx.html and ctx.json, templates with $tmpl and $veb.html, middleware with Middleware.use and route_use, shared state with shared and lock, and static file mounting. Use when creating or changing a veb server, adding a route or handler, returning JSON or HTML, adding middleware, or serving files. Does not cover concurrency in general (see v-concurrency), the language rules (see v-lang), the build loop (see v-workflow), scripting a task in V (see v-scripts), or the wider command surface (see v-tools).","license":"MIT"},"isInternal":false,"tokens":1493,"sizeBytes":5534},{"name":"COMPILER-WORKFLOW.md","path":"vlib/v/skills/v-workflow/references/COMPILER-WORKFLOW.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-workflow/references/COMPILER-WORKFLOW.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `vlib/v/skills/v-workflow/references/COMPILER-WORKFLOW.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-workflow/references/COMPILER-WORKFLOW.md)","tokens":300,"sizeBytes":219},{"name":"FLAGS.md","path":"vlib/v/skills/v-workflow/references/FLAGS.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-workflow/references/FLAGS.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `vlib/v/skills/v-workflow/references/FLAGS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-workflow/references/FLAGS.md)","tokens":300,"sizeBytes":195},{"name":"V-MOD.md","path":"vlib/v/skills/v-workflow/references/V-MOD.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-workflow/references/V-MOD.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `vlib/v/skills/v-workflow/references/V-MOD.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-workflow/references/V-MOD.md)","tokens":300,"sizeBytes":195},{"name":"SKILL.md","path":"vlib/v/skills/v-workflow/SKILL.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-workflow/SKILL.md","title":"v-workflow","category":"anthropic-skill","format":"markdown","content":"---\nname: v-workflow\ndescription: How to build, type-check, format, vet, test and ship V code, and how the v.mod module system resolves imports. Use before declaring a V change finished, when a build or test command is needed, when a dependency will not resolve, when working in the V compiler's own source tree, or when a v.mod needs reading or writing. Covers flag placement, the difference between -check and a real build, and when ./v self is required. Does not cover the language rules themselves (see v-lang), writing test cases (see v-testing), working through the MCP server (see v-mcp), scripting a task in V (see v-scripts), or the wider command surface (see v-tools).\nlicense: MIT\n---\n\n# The V build loop\n\nV's compiler is fast and its checker is the authority. A V change is not finished\nuntil the checker, the formatter and the tests have all said so.\n\n## Resource Routing\n\n- `references/FLAGS.md` - Read when a flag is ignored or has to be moved, or when\n  choosing between `-prod`, `-g`, `-cflags` and `-ldflags`.\n- `references/V-MOD.md` - Read when a dependency will not resolve, or when\n  deciding between `v install`, `v link` and `v unlink`.\n- `references/COMPILER-WORKFLOW.md` - Read only when the change is inside the V\n  compiler's own tree, where the rules above are not enough.\n\n## Flags go before the subcommand\n\nThis is the first thing that goes wrong. Every compiler option comes **before**\nthe command; anything after it is passed to the command, not the compiler.\n\n```bash\nv -prod run main.v      # correct: -prod is a compiler option\nv run main.v -prod      # wrong: main.v receives -prod as an argument\n```\n\n`VFLAGS` sets options for every invocation, which is how CI pins a C compiler:\n\n```bash\nexport VFLAGS='-cc gcc'\n```\n\n## The loop\n\nRun these in order. Each one is cheaper than the one after it.\n\n```bash\nv -check path/to/file.v        # type-check only, no binary produced\nv fmt -verify path/to/file.v   # would the formatter change it?\nv -stats test path/to/file_test.v\nv vet -W path/to/file.v        # suspicious constructs, failures as errors\n```\n\nFor **library** code rather than a `main` module, `-check` and `vet` need\n`-shared`, or they fail with *\"project must include a `main` module\"*:\n\n```bash\nv -check -shared vlib/v/skills/\n```\n\n**Validation**: `v fmt -verify` and `v -check` are the two that gate everything\nelse. Run them on every file you touched before reporting the change done. A\ngreen test run does not imply a formatted file, and a formatted file does not\nimply it compiles.\n\n## Quick Reference\n\n| Question | Command |\n| --- | --- |\n| Does it type-check? | `v -check file.v` |\n| Is it formatted? | `v fmt -verify file.v` |\n| Format it | `v fmt -w file.v` |\n| Does it pass? | `v -stats test dir/` |\n| Only some tests | `VTEST_ONLY='pattern' v test file_test.v` |\n| Does the formatter agree with CI? | `v -silent test-fmt` |\n| Anything suspicious? | `v vet -W file.v` |\n| What does this symbol resolve to? | `v where Symbol` |\n| Does this still work? | `v doctor` |\n| Add a dependency | `v install module.name` |\n\n## Errors worth reading properly\n\nV's diagnostics carry a file, a line and a column, and the column is usually\nexact:\n\n```\nmain.v:12:5: error: unknown function: nope\n   12 |     nope()\n      |     ~~~~\n```\n\nThe caret span is the whole expression. Read the message first, then look at the\nspan — the span is frequently wider than you expect, because it covers the call\nrather than the name.\n\nDo not fix a V error by reading the file again. Fix it from the span.\n\n## The V compiler's own tree\n\nThe rules change when the change is inside the compiler itself. A compiler change\nmeans a rebuild before anything else, or you test a stale binary:\n\n```bash\n./v self                      # rebuild ./v from the current sources\n./v -silent test vlib/v/      # the compiler's own tests\n./v -silent vlib/v/compiler_errors_test.v\n```\n\n`vlib/v/` and `cmd/v/` changes additionally mean the test runner, the formatter\nand the language server should be tested too. See `references/COMPILER-WORKFLOW.md`\nfor the full list and what each one catches.\n\n## Related Skills\n\n- **The language rules**: see [v-lang](../v-lang/SKILL.md) when the compiler\n  rejects an option, a result, a match or an assignment.\n- **Writing tests**: see [v-testing](../v-testing/SKILL.md) for assertion forms,\n  fixtures and running a subset.\n- **Reading the project**: see [v-mcp](../v-mcp/SKILL.md) to get declarations,\n  references and diagnostics without reading files, and to edit through an\n  AST-aware rename.\n- **Dependencies and modules**: `references/V-MOD.md` here covers the module\n  system; [vpm](https://vlang.io/vpm.html) documents what is on the registry.","frontmatter":{"name":"v-workflow","description":"How to build, type-check, format, vet, test and ship V code, and how the v.mod module system resolves imports. Use before declaring a V change finished, when a build or test command is needed, when a dependency will not resolve, when working in the V compiler's own source tree, or when a v.mod needs reading or writing. Covers flag placement, the difference between -check and a real build, and when ./v self is required. Does not cover the language rules themselves (see v-lang), writing test cases (see v-testing), working through the MCP server (see v-mcp), scripting a task in V (see v-scripts), or the wider command surface (see v-tools).","license":"MIT"},"isInternal":false,"tokens":1202,"sizeBytes":4698},{"name":"README.md","path":"cmd/tools/fast/README.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/fast/README.md","title":"fast Documentation","category":"plugin-manifest","format":"markdown","content":"# fast Documentation\n\nPath: `cmd/tools/fast/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/fast/README.md)","tokens":300,"sizeBytes":157},{"name":"README.md","path":"cmd/tools/README.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/README.md","title":"tools Documentation","category":"plugin-manifest","format":"markdown","content":"# tools Documentation\n\nPath: `cmd/tools/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/README.md)","tokens":300,"sizeBytes":148},{"name":"README.md","path":"cmd/tools/scripting/README.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/scripting/README.md","title":"scripting Documentation","category":"plugin-manifest","format":"markdown","content":"# scripting Documentation\n\nPath: `cmd/tools/scripting/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/scripting/README.md)","tokens":300,"sizeBytes":172},{"name":"README.md","path":"cmd/tools/testing/README.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/testing/README.md","title":"testing Documentation","category":"plugin-manifest","format":"markdown","content":"# testing Documentation\n\nPath: `cmd/tools/testing/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/testing/README.md)","tokens":300,"sizeBytes":166},{"name":"README.md","path":"cmd/tools/vcreate/README.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vcreate/README.md","title":"vcreate Documentation","category":"plugin-manifest","format":"markdown","content":"# vcreate Documentation\n\nPath: `cmd/tools/vcreate/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vcreate/README.md)","tokens":300,"sizeBytes":166},{"name":"README.md","path":"cmd/tools/vdoc/testdata/output_formats/README.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vdoc/testdata/output_formats/README.md","title":"output_formats Documentation","category":"plugin-manifest","format":"markdown","content":"# output_formats Documentation\n\nPath: `cmd/tools/vdoc/testdata/output_formats/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vdoc/testdata/output_formats/README.md)","tokens":300,"sizeBytes":215},{"name":"README.md","path":"cmd/tools/vdoc/testdata/readme_in_project_root/README.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vdoc/testdata/readme_in_project_root/README.md","title":"readme_in_project_root Documentation","category":"plugin-manifest","format":"markdown","content":"# readme_in_project_root Documentation\n\nPath: `cmd/tools/vdoc/testdata/readme_in_project_root/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vdoc/testdata/readme_in_project_root/README.md)","tokens":300,"sizeBytes":239},{"name":"README.md","path":"cmd/tools/vgenerate/README.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vgenerate/README.md","title":"vgenerate Documentation","category":"plugin-manifest","format":"markdown","content":"# vgenerate Documentation\n\nPath: `cmd/tools/vgenerate/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vgenerate/README.md)","tokens":300,"sizeBytes":172},{"name":"README.md","path":"cmd/tools/vmcp/README.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vmcp/README.md","title":"vmcp Documentation","category":"plugin-manifest","format":"markdown","content":"# vmcp Documentation\n\nPath: `cmd/tools/vmcp/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vmcp/README.md)","tokens":300,"sizeBytes":157},{"name":"README.md","path":"cmd/tools/vpbgen/README.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vpbgen/README.md","title":"vpbgen Documentation","category":"plugin-manifest","format":"markdown","content":"# vpbgen Documentation\n\nPath: `cmd/tools/vpbgen/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vpbgen/README.md)","tokens":300,"sizeBytes":163},{"name":"README.md","path":"cmd/tools/vpm/README.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vpm/README.md","title":"vpm Documentation","category":"plugin-manifest","format":"markdown","content":"# vpm Documentation\n\nPath: `cmd/tools/vpm/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vpm/README.md)","tokens":300,"sizeBytes":154},{"name":"README.md","path":"cmd/tools/vpm/test_utils/README.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vpm/test_utils/README.md","title":"test_utils Documentation","category":"plugin-manifest","format":"markdown","content":"# test_utils Documentation\n\nPath: `cmd/tools/vpm/test_utils/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vpm/test_utils/README.md)","tokens":300,"sizeBytes":183},{"name":"README.md","path":"cmd/tools/vskills/README.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vskills/README.md","title":"vskills Documentation","category":"plugin-manifest","format":"markdown","content":"# vskills Documentation\n\nPath: `cmd/tools/vskills/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vskills/README.md)","tokens":300,"sizeBytes":166},{"name":"README.md","path":"cmd/tools/vvet/README.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vvet/README.md","title":"vvet Documentation","category":"plugin-manifest","format":"markdown","content":"# vvet Documentation\n\nPath: `cmd/tools/vvet/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vvet/README.md)","tokens":300,"sizeBytes":157},{"name":"README.md","path":"examples/cpu_features/SSE_and_MMX_Extensions/README.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/examples/cpu_features/SSE_and_MMX_Extensions/README.md","title":"SSE_and_MMX_Extensions Documentation","category":"plugin-manifest","format":"markdown","content":"# SSE_and_MMX_Extensions Documentation\n\nPath: `examples/cpu_features/SSE_and_MMX_Extensions/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/examples/cpu_features/SSE_and_MMX_Extensions/README.md)","tokens":300,"sizeBytes":235},{"name":"README.md","path":"vlib/x/async/tools/README.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/x/async/tools/README.md","title":"tools Documentation","category":"plugin-manifest","format":"markdown","content":"# tools Documentation\n\nPath: `vlib/x/async/tools/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/x/async/tools/README.md)","tokens":300,"sizeBytes":166},{"name":"README.md","path":"vlib/x/executor/tools/README.md","rawUrl":"https://raw.githubusercontent.com/vlang/v/HEAD/vlib/x/executor/tools/README.md","title":"tools Documentation","category":"plugin-manifest","format":"markdown","content":"# tools Documentation\n\nPath: `vlib/x/executor/tools/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/x/executor/tools/README.md)","tokens":300,"sizeBytes":172}],"systemPromptSnippet":"<yakaai_skills repo=\"vlang/v\">\n<!-- File: AGENTS.md (Tokens: ~9403 | Category: root-instruction) -->\n<!-- V_AGENTS.md  v1.1  2026-02-04 -->\n\n# V Repo Guide\n\nPractical quick reference for the V compiler, standard library, and tools.\nWritten for AI coding agents; useful for humans too.\n\n## Contents\n* Quick Start\n* Top Rules\n* Agent Rules\n* Safety (Do Not Brick the Repo)\n* Divergences From Repo Docs\n* Quick Decisions\n* Common Workflow\n* Reporting\n* Prerequisites\n* Build & Rebuild\n* Run Programs\n* Testing\n* Code Style\n* Modules and Imports\n* C/JS Interop Hygiene\n* Environment-Specific Code (files and `$if`)\n* Compile-Time Code and Reflection\n* Debug\n* Compiler Performance\n* Compiler Architecture\n* Key Directories\n* Test Locations\n* Error Reporting (checker/parser)\n* Option/Result Types\n* Tools\n* Commits and PRs\n* Environment Variables\n* Gotchas\n\n## Quick Start\nGet operational from the repo root in three steps:\n\n1. Build once (only if `./v` is missing): `make`\n2. Rebuild the working compiler in place: `./v self`\n3. Use `./v` for everything:\n   * Run a file: `./v run examples/hello_world.v`\n   * Run tests: `./v -silent test vlib/v/`\n   * Format: `./v fmt -w path/to/file.v`\n\nThen read Top Rules and Agent Rules before making changes.\n\n## Top Rules\n* Use `./v` for building, running, and testing.\n* Put all V flags immediately after `./v` and before the\n  subcommand/file, e.g. `./v -g run file.v`\n  (not `./v run file.v -g`); flags after the subcommand are passed\n  to that subcommand.\n* Rebuild `./v` after compiler or core module changes\n  (see Build & Rebuild).\n* Run the smallest relevant tests; see Testing for triggers and\n  minimums.\n* Ask before large refactors or wide file touches (see Agent Rules).\n* Do not stash or modify unrelated files unless explicitly instructed.\n* This guide assumes agents run locally, not in CI; CI notes are\n  informational only.\n* When a summary is required, include behavior change, tests run, and\n  touched file paths.\n* If instructions overlap, prefer Build & Rebuild, Testing, and\n  Reporting.\n* If duplicates drift, treat Build & Rebuild, Testing, and Reporting as\n  canonical and align other sections to them.\n\n## Agent Rules\n\n### Repo root\nAll commands assume the repo root as the working directory. The default\nlocation is `/opt/v`, but this may differ in your environment. If a\ncommand fails due to missing paths, verify with `pwd` and adjust\naccordingly. Use a per-command workdir only when a task requires a\nsubdir.\n\n### Tone and output\n* Be concise by default. If the user asks for depth, provide it while\n  keeping structure tight.\n* Keep output easy to scan: short sections, bullets when listing,\n  commands in backticks, no filler.\n* Use a strict, operational tone unless higher-priority instructions\n  override it.\n* Ask only when required. If information is missing, ask a direct\n  question.\n\n### File access and edit scope\n* You may read and edit all files in the V repo without asking for\n  permission. This is file access, not change scope; scope is\n  constrained below. Reading is always OK.\n  Edits to `ci/` or `Dockerfile*` still require an explicit ask.\n* Run build, test, and format commands without asking for permission.\n  These are validation steps, not code changes. Only ask about edit\n  scope, not about running `fmt`, targeted tests, or `check-md`.\n* Only modify files required for the user request; avoid unrelated\n  refactors. If duplication is harmful, small refactors to remove it\n  are OK only when needed. Only refactor duplication in code you are\n  already touching, and only when it directly supports the request or\n  fixes a bug there. \"Touching\" means files already modified for the\n  request.\n* Avoid unrelated file changes; call them out if present.\n* Avoid touching `thirdparty/` unless explicitly requested. If changes\n  are needed there, ask for approval before proceeding.\n\n### When to ask\n* Ask before large refactors or wide file touches across multiple repo-root\n  directories like `cmd/`, `vlib/`, `doc/`, and `examples/`.\n  Exception: docs-only changes across many files are OK without asking;\n  call them out in the summary.\n* Ask before large behavioral changes within a single subsystem or file,\n  even if the file count is small. Examples: changes to parser rules,\n  checker resolution, codegen output shape, diagnostic text/ordering, or\n  tool CLI behavior. Large means user-visible changes in CLI flags,\n  output, diagnostics, or codegen shape. If unsure whether a change is\n  \"large,\" ask.\n* Ask before touching `ci/` or `Dockerfile*` unless explicitly\n  requested. If changes are needed there, confirm whether local\n  validation is expected or if CI-only coverage is acceptable.\n\n### Bootstrap\n* Bootstrap/compiler usage rules: see Top Rules and Build & Rebuild.\n\n### Completeness and summaries\n* If you cannot complete a requested step, state the blocker and partial\n  progress (what was attempted and what remains).\n* After substantial work, provide a short summary and list touched file\n  paths. Substantial work means any behavioral change, or changes in\n  more than one file. Always include tests run (or \"Not run\" with\n  reason) in the summary.\n* Never change tests just to silence failures. Update expectations only\n  when behavior changes are intended, and note the rationale in the\n  summary.\n\n### New files and deeper guidance\n* New file checklist: format with `./v fmt -w`, add doc comments for\n  any public functions, run `./v check-md` for markdown files, and\n  keep Markdown lines <= 100 chars. Add or update tests when introducing\n  a new public API.\n* For deeper edge cases, consult `CONTRIBUTING.md` and `TESTS.md`.\n\n## Safety (Keep the Compiler Current)\n* Rebuild the working `./v` binary in place with `./v self` after compiler\n  changes. Overwriting `./v` is the intended workflow.\n* Keep generated C when debugging backend issues: add `-keepc`.\n* Avoid hidden/bidirectional Unicode characters in source/markdown\n  files.\n* If the compiler becomes unusable, recover with:\n  1. `git stash`\n  2. `make`\n  3. `git stash apply`\n  Check `git status` before stashing to avoid hiding unrelated work.\n  Do not stash unless explicitly instructed or the compiler is bricked.\n\n## Divergences From Repo Docs\nThe repo docs use `v` in examples. In this environment, use `./v` for all\nbuilds, runs, tests, and in-place self-rebuilds.\n* `TESTS.md` suggests `v test-all` before PRs; ask before running\n  `./v test-all`.\nThese overrides keep commands anchored to this checkout.\n\n## Quick Decisions\n* For rebuild and test choices, follow Build & Rebuild and Testing.\n* Follow Common Workflow for the default execution order.\n* For broader workflow guidance, see `CONTRIBUTING.md`.\n\n### Compact decision table (rebuild/tests)\nUse this table to pick the minimum rebuild/tests quickly. See Build &\nRebuild and Testing for full details and edge cases.\nCommands omit `./v` for brevity; assume the `./v` prefix.\nThis table is the minimum set; check Testing for additional triggers.\nREPL and backend changes have additional triggers in Testing.\nNote: `cmd/v/` is compiler scope; treat changes there as compiler\nchanges.\nWhen in doubt, ask before proceeding.\nIf you read only one section for tests, **read Testing**.\n\n| Change area | Rebuild? | Minimum tests to run |\n| --- | --- | --- |\n| Docs only (`.md`) | No | `check-md file.md` |\n| Compiler (`vlib/v/`, `cmd/v/`) | Yes | `-silent vlib/v/compiler_errors_test.v`; `test vlib/v/` |\n| Core modules (builtin/strings/os/strconv/time) | Yes | Smallest relevant tests |\n| Parser-only (`vlib/v/parser/`) | Yes | `test vlib/v/parser/` |\n| Checker-only (`vlib/v/types/`) | Yes | `test vlib/v/types/` |\n| Comptime (`vlib/v/types/`, `vlib/v/transform/`) | Yes | `test vlib/v/tests/`; comptime-related tests |\n| vlib (non-compiler) | No | Nearest `*_test.v` or `test vlib/path/` |\n| Tools (`cmd/tools/`) | No | Tool-specific test; else nearest `*_test.v` |\n| Diagnostic/output changes | Yes | `vlib/v/slow_tests/inout/compiler_test.v` |\n| C codegen (`vlib/v/gen/c/`) | Yes | `test vlib/v/gen/c/` |\n\n## Common Workflow\n0. Before work: `git status`; ensure `./v` exists; rebuild if needed.\n   If `./v` is missing, see Quick Start or Build & Rebuild.\n1. Edit the relevant files.\n2. If compiler sources or core modules changed, rebuild `./v` with `./v self`\n   (see Build & Rebuild).\n3. Format touched `.v`/`.vsh` files and run `./v check-md` on\n   touched markdown.\n4. Run the smallest relevant tests for the change scope (see Testing).\n\nSee Build & Rebuild for rebuild triggers and flags.\n\n## Reporting\n* When a summary is required (see Agent Rules), it must include:\n  * Behavior changes (or \"No behavior change\").\n  * Tests run (or \"Not run\" with a reason).\n  * Touched file paths.\n  * Note unrelated changes if present.\n  * Note doc updates if public behavior/tool output changed.\n* If public behavior or tool output changes, update relevant docs\n  (README.md, `doc/`, `tutorials/`) and note it.\n  For example: README.md for top-level CLI usage, `doc/` for\n  compiler/tool docs, `tutorials/` for learning material.\n* If you add or change a public API, update module docs or README and\n  note it. Public API includes stdlib functions/types and user-visible\n  compiler flags, diagnostics, tool CLI behavior (including `cmd/tools`\n  and stdlib CLI tools), or output formats.\n* Update `CHANGELOG.md` or `ROADMAP.md` only when explicitly requested.\n* Public behavior includes compiler output, diagnostics, user-facing\n  CLI, and stdlib API. This includes developer-facing flags, error\n  codes, or output ordering changes.\n* Internal refactors with no public behavior change do not require doc\n  updates.\n* If unsure whether behavior is public, ask the user before updating\n  docs.\n* Acceptable reasons for not running tests: docs-only change, no\n  relevant tests, or environment constraints. Be specific.\n* Docs-only changes: run `./v check-md file.md`; no other tests\n  required unless a test explicitly reads those docs.\n  No rebuild is needed unless compiler or core modules changed.\n* For docs-only, explicitly mention `check-md` in the summary.\n* If you update `.out` files, state the rationale in the summary.\n* Do not update `.out` files unless a behavior change is intended;\n  otherwise treat mismatches as regressions.\n* Example summary line: `Behavior change: none` or\n  `Behavior change: fixed X`.\n* Behavior change includes output, API, error messages, and test\n  expectations.\n* If blocked, note what was attempted and why it failed.\n* If tooling/network restrictions block a step, state the restriction\n  and the closest viable alternative.\n* Use `git status` to confirm touched files before reporting.\n\n## Prerequisites\n* Toolchain: `make`, `git`, and a C compiler (`clang` or `gcc`).\n* Windows: use `makev.bat` for the initial build.\n\n## Build & Rebuild\n* Initial build (only if `./v` is missing): `make`\n  (Windows: `makev.bat`).\n* Rebuild `./v` in place: `./v self`\n  * This intentionally overwrites the active compiler with one built from the\n    current checkout.\n* If `./v` is missing, run `make` first, then build `./v`.\n* This section is the source of truth for rebuild triggers.\n  If a rule appears elsewhere, defer to this section.\n* Rebuild triggers:\n  * Compiler sources in `vlib/v/` or `cmd/v/`.\n  * Core modules: `builtin`, `strings`, `os`, `strconv`, `time`.\n* If `./v` exists but compiler sources changed, still rebuild `./v`\n  before tests.\n* If unsure whether compiler/core changes happened, rebuild `./v`.\n* Common flags:\n  * `-g` debug info (V line numbers).\n  * `-cg` debug info (C line numbers); often combined with `-keepc`.\n  * `-keepc` keep generated C file(s).\n  * `-prod` optimized build.\n  * `-o file` output path/name.\n  * `-cc clang` pick a C compiler.\n  * `-cstrict` be stricter about the generated C.\n  * `-b js|native|wasm` select a backend.\n  * `-os <os>` cross-compile target selection (when supported).\n\n## Code Style\n* Comments: add succinct comments only when code is not self-explanatory.\n  Do not delete existing comments unless they are incorrect; you may fix\n  grammar or spelling without changing meaning.\n  Add V doc comments right before each new or modified public function\n  or method.\n  The V doc comments should start with the name of the fn,\n  example: `// the_name does ...`\n* Copy pasta: avoid copy pasta. If there's duplicate logic, move to a\n  function only when it is required for the request and within code you\n  are already touching.\n* Avoid using `unsafe{ code }` blocks where possible, and minimize\n  their scope. `unsafe` is acceptable for low-level interop (e.g. C\n  pointer casts, manual memory management) but should never wrap\n  ordinary V logic. When `unsafe` is required, keep the block as small\n  as possible and add a comment explaining why it is necessary.\n* Keep Markdown lines <= 100 chars (the checker is strict).\n  Apply to touched lines in modified Markdown files.\n* Avoid hidden/bidirectional Unicode characters in source/markdown\n  files.\n* Non-V files: keep existing formatting; only reformat if required by\n  the change.\n* Treat `.vv` files in `vlib/v/slow_tests/inout/` as fixtures; avoid\n  formatting unless a behavior change is intended and output\n  expectations are updated.\n* Formatting and check commands are in Tools.\n\n## Modules and Imports\n* Module names must match their directory name (no hierarchy in the\n  `module` line). Mismatches cause silent import failures.\n* Imports follow the folder hierarchy (e.g. `import abc.def`).\n\n## C/JS Interop Hygiene\n* Avoid using `C.` or `JS.` symbols in plain `.v` files.\n  * Use `.c.v` / `.js.v` files.\n  * Enable `-Wimpure-v` to catch accidental impurity.\n* Do not refactor existing `C.`/`JS.` uses in `.v` files unless\n  required. If you must change those lines, prefer moving the interop\n  code to `.c.v`/`.js.v`.\n* For C interop, use correct `const_` prefixes in C function\n  redeclarations when needed (helps with `-cstrict` and C static\n  analysis tooling).\n\n## Environment-Specific Code (files and `$if`)\nV supports environment-specific file suffixes. Prefer them when the\nwhole file is platform/backend specific.\n\nCommon patterns:\n* Backend splits:\n  * `*.c.v` (C backend), `*.js.v` (JS backend), `*.native.v` (native\n    backend), `*.wasm.v` (WASM backend)\n* OS splits:\n  * `*_windows.c.v`, `*_linux.c.v`, `*_nix.c.v`, with `*_default.c.v`\n    as fallback\n* Flag splits:\n  * `*_d_customflag.v` is included only with `-d customflag`\n  * `*_notd_customflag.v` is included only when that flag is NOT passed\n\nNotes:\n* Do not use combinatorial suffixes like `_d_flag_linux.c.v`; use\n  `_d_flag.v` plus `$if linux {}` inside the file.\n* For smaller platform differences, use compile-time `$if` blocks:\n  * `$if windows { ... } $else { ... }`\n* See Compile-Time Code and Reflection for the full `$if` reference.\n\n## Compile-Time Code and Reflection\nV uses `$` as a prefix for compile-time (comptime) operations. These\nare evaluated by the compiler, not at runtime. AI agents frequently\nconfuse comptime and runtime constructs; this section clarifies the\nboundaries.\n\n### Compile-time `$if`\n`$if` evaluates conditions at compile time. It is not a runtime `if`.\nUse it for platform, compiler, and custom-flag checks:\n* `$if windows { ... } $else $if linux { ... } $else { ... }`\n* `$if debug { ... }` (enabled by `-g` or `-cg`)\n* `$if prod { ... }` (enabled by `-prod`)\n* `$if custom_flag ? { ... }` (enabled by `-d custom_flag`)\n\nFull list of builtin `$if` options: see the table at\n<https://docs.vlang.io/conditional-compilation.html>.\n\nCommon mistakes:\n* Using runtime `if` where `$if` is needed for platform-specific code.\n  Runtime `if` will fail to compile if it references platform-specific\n  symbols; `$if` excludes the block entirely on non-matching platforms.\n* Forgetting the `?` suffix for custom flags: `$if myflag ? { ... }`.\n  Without `?`, the compiler treats it as a builtin option and silently\n  ignores it.\n\n### Compile-time `$for`\n`$for` iterates over type metadata at compile time. It works with:\n* `StructType.fields` - iterate struct fields\n* `StructType.methods` - iterate struct methods\n* `EnumType.values` - iterate enum values\n* `StructType.attributes` - iterate struct attributes\n* `SumType.variants` - iterate sum type variants\n* `method.params` - iterate method parameters\n\nInside `$for` blocks, use `$if` to branch on field/method types:\n```v oksyntax\nfn main() {\n\t$for field in MyStruct.fields {\n\t\t$if field.typ is string {\n\t\t\tprintln(field.name)\n\t\t}\n\t}\n}\n```\n\nAlso works with generics: `T.fields`, `param.fields` where\n`fn gen[T](param T) {}`.\n\nCommon mistakes:\n* Using runtime `for` to iterate struct fields. This does not work;\n  V has no runtime reflection. Always use `$for`.\n* Calling `obj.$method()` outside a `$for m in Type.methods` block.\n  The `$method()` call is only valid inside a comptime method\n  iteration.\n* Assuming comptime `$for` produces a runtime loop. It does not; the\n  compiler unrolls it into concrete code for each field/method/variant.\n\n### Compile-time functions\n\nOnly the following `$`-prefixed functions are supported:\n* `$embed_file('path')` - embed a file's contents into the binary.\n  Paths can be absolute, relative to the source file, or use pseudo\n  variables like `@VEXEROOT` or `@VMODROOT`.\n* `$tmpl('path')` - compile a V template file (used by veb).\n* `$env('VAR')` - read an environment variable at compile time.\n* `$d('ident', default)` - read a `-d ident=value` compile-time\n  define, with a default fallback.\n* `$res('path')` - embed a resource (Android).\n* `$compile_error('msg')` - emit a compile-time error.\n* `$compile_warn('msg')` - emit a compile-time warning.\n* `$pkgconfig('name')` - query pkg-config at compile time.\n* `$typeof(expr)` - compatibility alias for `typeof(expr)`.\n* `$sizeof(Type)` - compatibility alias for `sizeof(Type)`.\n* `$isreftype(expr)` - compatibility alias for `isreftype(expr)`.\n* `$__offsetof(Struct, field_name)` - compatibility alias for\n  `__offsetof(Struct, field_name)`.\n* `$dump(expr)` - compatibility alias for `dump(expr)`.\n\nCommon mistakes:\n* Prefer the unprefixed `typeof`, `sizeof`, `isreftype`, `__offsetof`,\n  and `dump` forms in new code. Their `$`-prefixed compatibility aliases\n  are also accepted.\n* Assuming `$embed_file` returns a string. It returns an\n  `EmbedFileData` object; use `.to_string()` or `.to_bytes()`.\n* Using `$env` where a runtime `os.getenv` is appropriate, or vice\n  versa. `$env` is baked in at compile time and cannot change at\n  runtime.\n\n### Compile-time pseudo variables\nThese are `@`-prefixed identifiers substituted at compile time:\n* `@FN` - current function name.\n* `@METHOD` - `ReceiverType.MethodName`.\n* `@MOD` - current module name.\n* `@STRUCT` - current struct name.\n* `@FILE`, `@DIR`, `@LINE`, `@COLUMN`, `@FILE_LINE` - source\n  location.\n* `@LOCATION` - file, line, and current type+method; good for logging.\n* `@VEXE`, `@VEXEROOT` - path to the V compiler and its directory.\n* `@VHASH`, `@VCURRENTHASH` - compiler commit hashes.\n* `@VMOD_FILE`, `@VMODHASH`, `@VMODROOT` - nearest `v.mod` info.\n* `@BUILD_DATE`, `@BUILD_TIME`, `@BUILD_TIMESTAMP` - build time (UTC).\n  Override with the `SOURCE_DATE_EPOCH` env var for reproducible\n  builds.\n* `@OS`, `@CCOMPILER`, `@BACKEND`, `@PLATFORM` - build environment.\n\n### Compile-time type groups\nComptime type groups combine multiple types into a higher-level type\nfor use in generic or comptime `$if` checks:\n* `$int` - all integer types\n* `$float` - all float types\n* `$array` - all array types\n* `$map` - all map types\n* `$struct` - all struct types\n* `$enum` - all enum types\n* `$alias` - all type aliases\n* `$sumtype` - all sum types\n* `$function` - all function types\n* `$interface` - all interface types\n* `$option` - all option types\n\nExample: `$if field.typ is $int { ... }`\n\n### Comptime changes and testing\nComptime logic lives primarily in `vlib/v/types/checker_comptime.v` and\n`vlib/v/transform/comptime.v`. Changes here require a rebuild of\n`./v` and should be tested with `./v -silent test vlib/v/tests/` plus\nany comptime-specific tests. See the decision table and Testing.\n\n## Run Programs\n* Compile and run: `./v run file.v`.\n* Just compile: `./v file.v` (creates executable).\n* With debug info: `./v -g run file.v`.\n* Debug run (C line numbers): `./v -keepc -cg run file.v`.\n* Example: `./v run examples/hello_world.v`.\n\n## Testing\nRun:\n* File (shows test output): `./v path/to/file_test.v`.\n* File (test runner report only): `./v test path/to/file_test.v`.\n* Dir: `./v -silent test path/to/dir/`.\n* Dir with statistics/metrics: `./v -stats test path/to/dir/`.\n* Compiler: `./v -silent vlib/v/compiler_errors_test.v`.\n* Fix outputs (only when intended):\n  `VAUTOFIX=1 ./v -silent vlib/v/compiler_errors_test.v`.\n* All: `./v test-all`.\n  Ask before running `./v test-all` unless explicitly requested.\n\nWhen:\n* Rule of thumb: for localized changes, run the smallest relevant\n  tests. If any trigger below matches, run the listed tests.\n* Minimum tests listed are the floor, not the ceiling; add targeted\n  tests for cross-cutting changes.\n* If unsure which tests apply, ask the user before proceeding.\n* If in doubt, prefer the smallest targeted test and ask.\n* Run all tests that apply. Start with the smallest targeted tests;\n  add slow tests as needed. Order does not matter.\n* Compiler changes (`vlib/v/` or `cmd/v/`):\n  Run `./v -silent vlib/v/compiler_errors_test.v`,\n  `./v -silent test vlib/v/`.\n* Parser-only changes (`vlib/v/parser/`):\n  Run `./v -silent test vlib/v/parser/`.\n* Checker-only changes (`vlib/v/types`):\n  Run `./v -silent test vlib/v/types/`.\n* vlib changes: Run nearest `*_test.v` or\n  `./v -silent test vlib/path/`.\n* Tool changes (`cmd/tools/`): Run tool-specific tests. If none exist,\n  run the smallest relevant `*_test.v` that exercises the tool.\n  Note: `cmd/v/` is compiler scope, not tools.\n  Examples: `cmd/tools/vfmt` -> `cmd/tools/vfmt_test.v` and\n  `vlib/v/gen/v/gen_test.v`.\n  `cmd/tools/vdoc` -> `cmd/tools/vdoc/vdoc_test.v`.\n* Diagnostic/output changes:\n  Run `./v -silent vlib/v/slow_tests/inout/compiler_test.v`.\n* C codegen changes: Run `./v -silent test vlib/v/gen/c/`.\n  Consider a stricter validation pass:\n  `./v -cstrict -cc clang -silent test vlib/v/`.\n* REPL changes: Run `./v -silent vlib/v/slow_tests/repl/repl_test.v`.\n* Broad refactors: Run `./v -silent test-all`.\n* Backend-specific changes: run the smallest relevant tests for the\n  affected backend. JS/native/WASM backends are incomplete, so avoid\n  broad `-b <backend> test vlib/` runs. Prefer targeted `*_test.v`\n  files or small test dirs with `-b js|native|wasm`.\n\nIf time-constrained, prioritize\n`./v -silent vlib/v/compiler_errors_test.v` and the smallest targeted tests.\nRun `vlib/v/slow_tests/inout/compiler_test.v` and the tests under\n`vlib/v/gen/c/` when output or codegen changes are likely.\nSee `TESTS.md` for more guidance on test selection and output\nexpectations.\nSee `CONTRIBUTING.md` for broader workflow guidance.\n\nConcrete triggers:\n* `vlib/v/slow_tests/inout/compiler_test.v` when error text or output\n  formatting changes, or changes in checker/parser error reporting.\n* `./v -silent test vlib/v/gen/c/` for changes under `vlib/v/gen/c/`\n  or C codegen output paths.\n\nTypes:\n* Standard: `*_test.v` files with `test_` functions.\n* Output: `.vv` source + `.out` expected output in\n  `vlib/v/slow_tests/inout/`.\n  Example: `./v -silent vlib/v/slow_tests/inout/compiler_test.v`.\n* `vlib/v/tests/**` may use `.run.out` expectations; run with\n  `./v -silent test vlib/v/tests`.\n\nDocs-only guidance: see Reporting.\nIf time-boxed, run at least the smallest relevant test and note\nskipped coverage in the summary.\n\n### Useful env variables and flags while testing\n* `VAUTOFIX=1` - Auto-update .out files when tests fail (run twice).\n  Use only when a behavior change is intended.\n* `VTEST_ONLY=glob_pattern` - Run only tests matching pattern.\n* `VTEST_HIDE_OK=1` - Hide successful tests, show only failures.\n* `./v -silent test path/to/dir/` - Show only failed tests (if any), and a summary report.\n* `-cc tcc` can speed test builds when TCC is available.\n* Output expectations: update `.out` files only when behavior changes\n  are intended; note the rationale in the summary.\n\n## Debug\n* See what the C compiler is doing:\n  * `-showcc` prints the C compile command.\n  * `-show-c-output` prints the C compiler output.\n* Build a temporary compiler for debug flags:\n  * `./v -o ./w -d trace_checker cmd/v`\n  * Then run: `./w file.v`\n* Keep and inspect generated C:\n  * `-keepc -cg` is the common combo.\n  * `-printfn <name> -o file.c` emits only the named C function to standard\n    output. The `name` uses the `modulename__fnname` format (e.g.\n    `main__main`). This flag can be repeated to print multiple\n    functions. Methods/generics may use more complex C names; use\n    `-keepc` to confirm exact symbols.\n* Trace stages:\n  `-d trace_scanner|trace_parser|trace_checker|trace_gen`.\n  These flags can help diagnose a problem when a stage stops earlier\n  than expected.\n* Time stages: `-d time_parsing|time_checking`.\n* V panics: `-keepc -g`.\n* C segfaults: `-keepc -cg -cc clang`.\n* JS/native/wasm: prefer small, focused test files with\n  `-b js|native|wasm` and `-show-c-output` where applicable; avoid\n  broad `-b <backend> test vlib/`.\n* Quick code location technique:\n  When debugging code generation issues, add unique comment tags to\n  relevant code generation points (e.g., `/*tom51*/`) in the compiler\n  source. Rebuild and generate C code, then search the generated file for these\n  tags to quickly map generated C code back to the exact compiler source\n  location. This is especially useful when multiple code paths generate\n  similar-looking output and you need to identify which path is actually used.\n  Example:\n  ```v\n  // In vlib/v/gen/c/stmt.v:\n  g.write('builtin___option_ok/*tom51*/(&(${styp}[]) { ')\n\n  // After rebuild, search generated C code for \"tom51\":\n  // builtin___option_ok/*tom51*/(&(int[]) { ... });\n  ```\n  Remember to remove these debug tags after fixing the issue.\n\n## Compiler Performance\n\nFor the fixed self-compilation benchmark, results, and agent regression policy, read\n[vself_speed.md](vself_speed.md). Every compiler fix must preserve or improve that efficiency;\ninvestigate instruction-count regressions before merging.\n\nUse **retired CPU instructions per fixed workload and per parsed source line** as the primary\ncompiler optimization metrics; lower is better. On macOS arm64,\n`/usr/bin/time -l` reports `instructions retired` from hardware counters. Under matched conditions,\nthis is the most reliable local measure of compiler work: it is less sensitive than elapsed time to\nCPU frequency and scheduling. Also retain elapsed time, cycles, and peak memory; fewer instructions\ndo not guarantee lower latency or memory use. Compare counts on the same machine and architecture.\n\nThe instruction counter is scoped to the measured process; unrelated processes do not add their\ninstructions to it. An idle host is not required for instruction comparisons. Counts are dynamic,\nnot a static property of the binary: synchronization, retries, and runtime paths can vary. Use\nserial paired measurements to check that variation. Record host load as context for elapsed time\nand cycles; use controlled load when making latency claims.\n\nFor instructions per line, divide by physical lines in the `.v` and `.vh` files actually parsed,\nincluding builtins, imports, blank lines, and comments. Collect `parsed .v lines` plus\n`parsed .vh lines` in a separate unmeasured `-v` run with the same input flags. Preserve that\ninventory and verify that both compilers process the same files and line count. Keep the fixture\nfixed: padding files or changing source complexity must not count as an optimization. Retain total\ninstructions alongside instructions/line; establish a new baseline when the fixture changes.\nInstructions per second depends on hardware, scheduling, and instruction mix; do not use it as a\ncompiler-efficiency score. See [vself_speed.md](vself_speed.md) for the self-compilation fixture.\n\nBuild a production compiler outside the measured run, then measure compilation to C:\n\n```sh\nenv -u VFLAGS -u VEXE VJOBS=1 SOURCE_DATE_EPOCH=0 V_MACOS_V3_NO_FALLBACK=1 \\\n  ./v -prod -cc clang self || exit 1\nbenchmark_out=$(mktemp -d \"${TMPDIR:-/tmp}/v-perf.XXXXXX\")\nenv -u VFLAGS -u VEXE VJOBS=1 SOURCE_DATE_EPOCH=0 V_MACOS_V3_NO_FALLBACK=1 \\\n  /usr/bin/time -l -o \"$benchmark_out/time.txt\" \\\n  ./v -nocache -o \"$benchmark_out/sha3.c\" /absolute/path/to/sha3.v \\\n  > \"$benchmark_out/stdout.txt\" 2> \"$benchmark_out/stderr.txt\"\nbenchmark_status=$?\nprintf '%s\\n' \"$benchmark_status\" > \"$benchmark_out/exit-status.txt\"\ncat \"$benchmark_out/time.txt\"\n```\n\nReplace the source path with the fixed benchmark fixture. Read the integer on the\n`instructions retired` line. The `-prod` above optimizes the compiler itself; the timed command uses\nthe normal input compilation mode. Keep input flags identical between baseline and candidate.\n`-o ...c` includes parsing, checking, transforms, and C generation, while excluding the external C\ncompiler and execution of the generated program. `-check-syntax` measures a different workload;\nlabel it separately and verify which stages that compiler version executes. `-nocache` disables\ncompiler caching; it does not flush the operating system's filesystem cache.\n`V_MACOS_V3_NO_FALLBACK=1` prevents silent legacy-compiler fallback when measuring V3 on macOS.\n\nFor a baseline/candidate comparison:\n\n1. Build both compilers with the same production build recipe and C toolchain. Verify correctness,\n   diagnostics, and generated C before measuring. For a change intended to preserve output, require\n   identical C, allowing only explicitly recorded build metadata differences. Validate generated\n   C when changing the fixture or compiler representation; matching invalid C is not sufficient.\n2. Use the same host, OS, fixture, standard library, flags, environment, and filesystem placement.\n   Keep executable, source, and output paths identical between roles where possible; record any\n   remaining differences. Set `VJOBS=1` and `SOURCE_DATE_EPOCH=0`, and disable tracing/profiling.\n   Clear unintended compiler environment overrides, including `VFLAGS` and `VEXE`; record the\n   effective environment. Measure only successfully rebuilt compilers, never a stale binary.\n   Run only one benchmark process at a time. Other builds or tests on the host do not invalidate\n   process instruction counts; do not pause unrelated work or wait for an idle host. Record that\n   elapsed time and cycles collected under uncontrolled load are contextual measurements.\n3. Run one warmup per role, then **ten measured pairs**, alternating baseline/candidate order:\n   AB, BA, AB, BA, and so on. Record all 22 runs, excluding both warmups from statistics.\n   Use a distinct timing/stdout/stderr log for every run and the same generated C output path.\n4. Require exit status zero and exactly one positive `instructions retired` counter in every sample.\n   Preserve failed or incomplete runs, but do not report them as a completed comparison. If the\n   counter is missing or zero, report it as unavailable; elapsed time is not an instruction count.\n5. Report each role's median instructions/line and total instructions, the fixed parsed-line count,\n   percentage change between medians, median paired instruction difference, and how many pairs\n   made the candidate worse. Report memory and elapsed time alongside instructions. Retain all\n   raw logs, exact commands/environment, compiler commits, binary hashes,\n   fixture hashes, and output verification so the result can be reproduced.\n\nDo not select the fastest sample or compare medians from different measurement sessions as proof of\na small improvement. Investigate inconsistent pair results before accepting an optimization. For\nend-to-end build latency or parallel scaling, measure that workload separately with its intended\nworker count and external C compiler, and state that scope explicitly.\n\n## Compiler Architecture\nThe V compiler has the following stages, orchestrated by `v.driver`:\n`v.scanner` -> `v.parser`/`v.flat` -> `v.types` -> `v.transform` ->\n`v.markused` -> `v.gen.c`.\nAdditional backends and lowerings include FastC, WebAssembly, SSA, MIR,\ninstruction selection, and native ARM64 generation.\n\n### Key Directories\n* `vlib/`: Standard library (changes here can affect the compiler\n  itself).\n* `cmd/v/v.v`: Compiler entry.\n* `cmd/tools/`: vfmt, vdoc, vup, vquest, etc.\n* `examples/`: Example programs.\n* `thirdparty/`: Bundled C libraries (tcc, mbedtls, sokol, etc.).\n* `vlib/v/`: Compiler modules.\n  * scanner/ - Tokenizer\n  * token/ - Token definitions\n  * parser/ - Produces a flat AST\n  * flat/ - Compact AST storage\n  * types/ - Name resolution and type checking\n  * transform/ - AST simplification\n  * markused/ - Dead code eliminator\n  * gen/c/ - C code generation (primary backend, known as cgen)\n  * gen/fastc/ - Scanner-to-C fast path\n  * gen/arm64/ - Native ARM64 backend\n  * gen/wasm/ - WebAssembly backend\n\n### Test Locations\n* `vlib/v/tests/`: Compiler feature tests (including comptime).\n* `vlib/v/slow_tests/`: Output-matching and slow tests for the\n  compiler.\n* `vlib/v/slow_tests/inout/` - Output comparison tests\n  (.vv + .out pairs)\n* `vlib/v/parser/tests/` - Parser error tests\n* `vlib/v/checker/tests/` - Checker error tests\n* `vlib/v/gen/c/` - C codegen unit and integration tests\n* `examples/compiletime/` - Comptime usage examples (including\n  `reflection.v`)\n\n## Error Reporting (checker/parser)\nScanner and parser diagnostics carry `v.token.Pos` spans. Type-checker\ndiagnostics are collected by `v.types.TypeChecker`; shared diagnostic\nformatting and message types live in `vlib/v/errors/`.\n\n## Option/Result Types\n* Syntax: `?Type` (optional, can be none) vs `!Type` (result, can\n  error).\n* Common bugs: Unwrapping in if guards, struct init with option fields,\n  ternaries with options.\n* When changing option/result behavior, add a regression test in\n  `vlib/v/tests/`.\n* Tests: Search `vlib/v/tests/` for option/result test files.\n* Pitfall: Options in ternaries, SQL statements, and fixed arrays need\n  special handling in cgen.\n\n## Tools\n* Note: if a rule overlaps with Testing, follow Testing.\n* Format: `./v fmt -w <file>` for touched `.v` and `.vsh` files.\n  Format only touched files unless explicitly asked to reformat broader\n  scope.\n* Treat new files as touched for formatting and markdown checks.\n* Check *all* files are formatted: `./v -silent test-fmt`.\n  Run only when asked or when validating the full tree.\n* Check markdown: `./v check-md file.md` for touched `.md` files\n  (required before commits).\n* Code style checker: `./v vet vlib/v`\n  Run only when asked or when making broad checker changes (more than\n  3 files in `vlib/v/types/`).\n* Module docs: `./v doc -readme -all -l module_name`.\n* Search: `rg pattern` (or `git grep`); list files: `rg --files`.\n* Auto-format hook: `./v git-fmt-hook install`.\n\n## Commits and PRs\n* See `CONTRIBUTING.md` for full commit message conventions and PR\n  workflow.\n* Keep commits focused: one logical change per commit.\n* If your branch has diverged and a rebase or merge is needed, do not\n  force-push or drop hunks without asking. State the conflict and let\n  the user decide the resolution strategy. When in doubt, prefer\n  `git rebase` over `git merge` for a linear history, but always ask\n  if the situation is ambiguous.\n\n## Environment Variables\n* VFLAGS: Pass flags to all V invocations\n  (e.g., `VFLAGS='-g' ./v test-all`).\n* VEXE: Path to V compiler executable (useful in CI/scripts).\n* TMPDIR: Controls where `.tmp.c` files are written (V uses\n  `TMPDIR/v/`).\n* SOURCE_DATE_EPOCH: Override build timestamp for reproducible builds\n  (affects `@BUILD_DATE`, `@BUILD_TIME`, `@BUILD_TIMESTAMP`).\n* V2-specific: `V2CC`, `V2CFLAGS`, `V2VERBOSE` (for v2 development).\n\n## Gotchas\n* Core modules (`builtin`, `strings`, `os`, `strconv`, `time`) can\n  affect the compiler because it is a V program. Rebuild `./v` when\n  they change.\n* Module names must match their directory name; mismatches cause silent\n  import failures. See Modules and Imports.\n* Some V programs and tools hardcode `os.execute('v ...')` in their\n  source. The `./v` workflow does not protect against these; if you\n  encounter unexpected behavior from such calls, check whether the\n  code is invoking the system `v` instead of `./v` and adjust\n  accordingly.\n* Stale `./v` can cause confusing failures; rebuild if behavior\n  seems off.\n* Output tests require exact matches; whitespace changes break tests.\n* Formatting reminders: follow Code Style for `fmt` and `check-md`.\n* C compilation errors? Check generated C with `-keepc`\n  (creates `/tmp/*.tmp.c`; see also TMPDIR in Environment Variables).\n* Compile-time code (`$if`, `$for`, `$embed_file`, etc.) is a common\n  source of AI mistakes. See Compile-Time Code and Reflection for\n  correct usage and pitfalls.\n* Broken compiler? See Safety for the recovery sequence.\n* Consult `CONTRIBUTING.md` and `TESTS.md` for:\n  * New tests or test infra changes.\n  * Output tests or `.out` updates.\n  * Broad refactors or compiler-wide changes.\n  * Unsure which slow test runner applies to your change.\n\n<!-- File: cmd/tools/vdoc/testdata/attributes/main.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Attributes Skill\n\nPath: `cmd/tools/vdoc/testdata/attributes/main.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vdoc/testdata/attributes/main.md)\n\n<!-- File: cmd/tools/vdoc/testdata/enum_helpers/main.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Enum_helpers Skill\n\nPath: `cmd/tools/vdoc/testdata/enum_helpers/main.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vdoc/testdata/enum_helpers/main.md)\n\n<!-- File: cmd/tools/vdoc/testdata/output_formats/main.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Output_formats Skill\n\nPath: `cmd/tools/vdoc/testdata/output_formats/main.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/cmd/tools/vdoc/testdata/output_formats/main.md)\n\n<!-- File: vlib/v/skills/v-concurrency/references/CHANNELS.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `vlib/v/skills/v-concurrency/references/CHANNELS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-concurrency/references/CHANNELS.md)\n\n<!-- File: vlib/v/skills/v-concurrency/references/PATTERNS.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `vlib/v/skills/v-concurrency/references/PATTERNS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-concurrency/references/PATTERNS.md)\n\n<!-- File: vlib/v/skills/v-concurrency/SKILL.md (Tokens: ~1611 | Category: anthropic-skill) -->\n---\nname: v-concurrency\ndescription: V's concurrency primitives - spawn, the sync module (channels, Mutex, RwMutex, WaitGroup, Once, Pool, select), and parallel.amap for parallel map and run. Use when writing code with goroutines, threads, channels, locks, shared mutable state, a WaitGroup, or a race, and when reviewing V code that touches them. Covers how to wait for spawned work and why a detached spawn is a bug. Does not cover the mutability rules a lock protects (see v-lang), the build and test loop (see v-workflow), web servers (see v-veb), scripting a task in V (see v-scripts), or the wider command surface (see v-tools).\nlicense: MIT\n---\n\n# Concurrency in V\n\nV has three levels: `parallel.amap` when you have a collection to process, `spawn`\nplus a `WaitGroup` when you have a fixed set of tasks, and `sync` channels and\nlocks when the tasks have to talk to each other.\n\n## Resource Routing\n\n- `references/CHANNELS.md` - Read when passing work or results between threads, or\n  when a producer must not block.\n- `references/PATTERNS.md` - Read for worker pools, fan-out, a single-writer\n  cache, and the shapes that deadlock.\n\n## Reach for these first\n\n| Situation | Use |\n| --- | --- |\n| Process a slice, collect results | `parallel.amap(items, worker)` |\n| Process a slice, discard results | `parallel.run(items, worker)` |\n| A fixed number of independent tasks | `spawn f()` plus `sync.WaitGroup` |\n| Hand work to a pool | a channel plus a fixed set of consumers |\n| Share mutable state | `sync.Mutex`, and keep the critical section small |\n| Initialise once, lazily | `sync.Once` |\n| Reuse objects | `sync.Pool` |\n| Wait for any of several channels | `select` |\n\nReaching for `spawn` when `amap` would do is the most common mistake. `spawn`\nmakes you wait for the work; `amap` does it for you.\n\n## parallel: do not hand-roll this\n\n```v ignore\nimport arrays.parallel\n\nfn test_it_doubles_every_number() {\n\titems := [1, 2, 3, 4]\n\tgot := parallel.amap(items, fn (n int) int {\n\t\treturn n * 2\n\t})\n\tassert got == [2, 4, 6, 8]\n}\n```\n\n`amap` preserves order and returns a slice; `run` discards results. Both take an\noptional `Params` whose `workers` defaults to `runtime.nr_jobs()`, which reads\n`VJOBS`.\n\nThis is worth preferring over a loop with a `WaitGroup` even when the loop is\nshort: there is no way to forget to wait.\n\n## spawn needs a WaitGroup\n\n`spawn` starts a thread and returns immediately. Nothing waits for it, so a\nprogram that ends, or a function that returns, can leave it mid-write.\n\n```v ignore\nimport sync\n\nfn fetch_all(urls []string) []string {\n\tmut wg := sync.new_waitgroup()\n\tmut results := []string{len: urls.len}\n\tfor url, i in urls {\n\t\twg.add(1)\n\t\tspawn fn () {\n\t\t\tresults[i] = fetch(url)\n\t\t\twg.done()\n\t\t}\n\t}\n\twg.wait()\n\treturn results\n}\n```\n\nEvery rule that matters here is easy to get wrong:\n\n- **`wg.add(1)` before the spawn, `wg.done()` inside it.** A `done()` without a\n  matching `add()` panics.\n- **All `wg.go()` / `spawn` before `wg.wait()`.** The doc comment says so\n  explicitly: adding after waiting is a race.\n- **`wait()` or nothing.** A detached spawn is a bug unless it is genuinely\n  fire-and-forget, and V is not a language where that is the default answer.\n- **Never let a spawned function panic.** It takes the whole process down.\n\n`sync.WaitGroup.go(f)` bundles the `add` and the thread start, which removes two\nof the four mistakes:\n\n```v ignore\nmut wg := sync.new_waitgroup()\nwg.go(fn () { work() })\nwg.wait()\n```\n\n## Sharing state needs a lock\n\nA struct shared between threads needs a `Mutex`, and the rule is to hold it for\nthe shortest possible span:\n\n```v ignore\nimport sync\n\nstruct Counter {\nmut:\n\tmu    sync.Mutex\n\ttotal int\n}\n\nfn (mut c Counter) add(n int) {\n\tlock c.mu {\n\t\tc.total += n\n\t}\n}\n```\n\nDo not hold a lock across a channel operation, a `spawn`, or anything that can\nblock. Two locks taken in different orders by two threads deadlock, so pick one\norder and hold to it.\n\n`RwMutex` is for a structure read far more often than it is written. If the write\nrate is not clearly lower, `Mutex` is simpler and fast enough.\n\n## Channels are the typed default\n\nA channel moves one value type between threads and gives you a buffer:\n\n```v ignore\nmut jobs := chan Job{cap: 16}\nmut results := chan Result{cap: 16}\n\nspawn fn () {\n\tfor job in jobs {\n\t\tresults <- run(job)\n\t}\n\tclose(results)\n}\n\nfor r in results {\n\tprintln(r)\n}\n```\n\nNote `chan Job{cap: 16}` — the capacity is part of the type, and a buffered channel\nlets a producer get ahead instead of blocking on every send.\n\nSee `references/CHANNELS.md` for closing, `select` and timeouts.\n\n## Atomics for counters\n\nFor a single value — a counter, a flag, a pointer — a lock is more than needed:\n\n```v ignore\nimport sync.stdatomic\n\nmut hits := stdatomic.new_atomic(0u64)\nhits.add(1u64)\ntotal := hits.load()\n```\n\nThe type is fixed when the atomic is created, and `load`, `store` and `add` need\n`mut`. There are also plain functions taking a pointer (`stdatomic.add_u64(&x, 1)`)\nfor the cases where an `AtomicVal` field is awkward.\n\nUse atomics for one or two values. The moment you need two of them to change\ntogether there is no transaction, and you want a `Mutex`:\n\n```v ignore\n// Two values that must stay consistent with each other.\nlock c.mu {\n    c.total += n\n    c.checks += 1\n}\n```\n\n## Validation\n\nConcurrency bugs are timing bugs, and reasoning about them is unreliable. Run\nunder the race detector and read what it reports:\n\n```bash\nv -race run main.v\nv -race test dir/\n```\n\nA clean race run does not prove the absence of races, but a reported one is\nreal. Never disable a report without explaining it. See `v-workflow` for the rest\nof the loop.\n\n## Related Skills\n\n- **Mutability**: see [v-lang](../v-lang/SKILL.md) for `mut` receivers and the\n  value-versus-pointer difference a lock protects.\n- **The build loop**: see [v-workflow](../v-workflow/SKILL.md) for `-race`,\n  `-prod` and the other flags.\n- **Web servers**: see [v-veb](../v-veb/SKILL.md) when the shared state is a\n  request counter and `veb` already gives you the locking idiom.\n\n<!-- File: vlib/v/skills/v-lang/references/COMPTIME.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `vlib/v/skills/v-lang/references/COMPTIME.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-lang/references/COMPTIME.md)\n\n<!-- File: vlib/v/skills/v-lang/references/MUTABILITY.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `vlib/v/skills/v-lang/references/MUTABILITY.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-lang/references/MUTABILITY.md)\n\n<!-- File: vlib/v/skills/v-lang/references/OPTION-RESULT.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `vlib/v/skills/v-lang/references/OPTION-RESULT.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-lang/references/OPTION-RESULT.md)\n\n<!-- File: vlib/v/skills/v-lang/references/SUMTYPES.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `vlib/v/skills/v-lang/references/SUMTYPES.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-lang/references/SUMTYPES.md)\n\n<!-- File: vlib/v/skills/v-lang/SKILL.md (Tokens: ~1886 | Category: anthropic-skill) -->\n---\nname: v-lang\ndescription: The V language rules that make code which looks right fail to compile or behave wrongly - ?T versus !T, sum types and match exhaustiveness, mut receivers, module names matching directories, comptime $ forms, and explicit type conversion. Use when writing or reviewing any V code that uses an Option, a Result, a sum type, a match, generics, or a custom flag, and when a V compiler error names an option, a result, exhaustiveness, mutability, or a module. Does not cover the build and test loop (see v-workflow), writing tests (see v-testing), reading a V project through the MCP server (see v-mcp), scripting in V (see v-scripts), or the wider command surface (see v-tools).\nlicense: MIT\n---\n\n# The V rules that bite\n\nMost mistakes an agent makes in V are not algorithmic. They are these rules, and\neach one fails in a way that does not look like the mistake.\n\n## Resource Routing\n\n- `references/OPTION-RESULT.md` - Read when deciding whether a function returns\n  `?T` or `!T`, or when an `or {}` block is not doing what you expected.\n- `references/SUMTYPES.md` - Read when modelling a closed set of states, or when\n  `match` reports a non-exhaustive match.\n- `references/COMPTIME.md` - Read when a symbol only exists on some platforms, or\n  when reaching for reflection or `$embed_file`.\n- `references/MUTABILITY.md` - Read when an assignment is rejected, or when a\n  mutation to a field does not reach the caller.\n\n## Quick Reference\n\n| You want | Write | Not |\n| --- | --- | --- |\n| A value that may be absent | `fn f() ?int` | `return 0` as a sentinel |\n| An operation that can fail | `fn f() !int` | `return -1` |\n| Unwrap, no default | `x or { return error('...') }` | `x or { 0 }` on a result |\n| Unwrap, with a default | `x or { 0 }` on an option | `x or { return }` on an option |\n| Branch on a closed set | `enum` + exhaustive `match` | a string constant |\n| Mutate an argument | `fn f(mut x T)` | `x = ...` silently failing |\n| Mutate the caller's struct | `fn (mut t &T)` | `fn (mut t T)` (a copy) |\n| Run code at compile time | `$if`, `$for` | a runtime `if` |\n\n## `?T` is not `!T`\n\n`?T` holds a value or `none`. `!T` holds a value or an error. They unwrap\ndifferently, and using one where the other belongs is a type error rather than a\nlogic error.\n\n```v ignore\n// An option: absence is part of the answer.\nfn find_port(cfg Config) ?int {\n    if !cfg.has_port {\n        return none\n    }\n    return cfg.port\n}\n\n// A result: the operation can fail, and the failure carries why.\nfn read_port(path string) !int {\n    lines := os.read_lines(path) or {\n        return error('cannot read ${path}: ${err.msg()}')\n    }\n    ...\n}\n```\n\nThe rule that catches people: inside `or { ... }` on a **result**, the block must\nproduce that result's error value, so it is `return error(...)` and not a bare\n`return`. On an **option**, `or { default }` supplies a value.\n\n**Default**: return `!T` for anything that touches the outside world, and `?T` for\na lookup that is legitimately absent. Full detail, including how to propagate and\nwhen a default is honest, in `references/OPTION-RESULT.md`.\n\n## Sum types and match\n\nV enums plus `match` are the way to model a closed set. `match` over an enum is\nchecked for exhaustiveness, which is the point: adding a variant turns every\nincomplete `match` into a compile error instead of a silent fallthrough.\n\nThe diagnostic is exact: `non-exhaustive match expression without `else``. Two\nways out — handle every variant, or add an `else` branch and accept that a new\nvariant changes nothing.\n\n```v ignore\nenum Status {\n    pending\n    running\n    done\n    failed\n}\n\nfn label(s Status) string {\n    return match s {\n        .pending { 'queued' }\n        .running { 'in progress' }\n        .done { 'finished' }\n        .failed { 'could not finish' }\n    }\n}\n```\n\nVariants are shared: `type Job = Status | string` is a sum type, and a `match`\nover it must handle both sides. See `references/SUMTYPES.md`.\n\n## mut is not optional\n\nFunction arguments are immutable. A receiver is immutable unless it is declared\n`mut`. This is the single most common V compile error, and it always has the same\ncause: the author assumed mutation was the default.\n\n```v ignore\n// Rejected: `app` is immutable.\n// fn serve(app App) { app.port = 8080 }\n\n// Accepted: the argument is mutable.\nfn serve(mut app App) { app.port = 8080 }\n\n// Accepted, and it reaches the caller: the receiver is a pointer.\nfn serve(app &App) { app.port = 8080 }\n\n// Accepted: the field itself is declared mutable.\nstruct Server {\nmut:\n    port int\n}\n```\n\n`mut` on a **value** receiver mutates a copy, so the caller's struct is\nunchanged. `mut` on a **pointer** receiver mutates the caller's. That difference\nis silent at the call site, which is why `references/MUTABILITY.md` exists.\n\n## Module names must match directories\n\n`module foo` must live in a directory named `foo`. A mismatch imports cleanly and\nthen resolves to nothing, with no error pointing at the name. The `module` line\ncarries no hierarchy — the directory nesting supplies it.\n\nThis is why a project built by `v new` has `src/main.v` with `module main` rather\nthan `module src`.\n\n## Comptime is `$`, and it excludes branches\n\nAnything starting with `$` runs while the compiler runs. The branches of an\n`$if` are **excluded entirely** on a platform that does not match, which is the\nonly reason a platform-specific import inside one is safe.\n\n```v ignore\n$if windows {\n    import sys.windows as wsys\n}\n$if linux {\n    import sys.linux\n}\n```\n\nA runtime `if` that mentions a platform-specific symbol will not compile on the\nother platforms, because there is no exclusion. Available in `$if`: `windows`,\n`linux`, `macos`, `js`, `freebsd`, `android`, `debug`, `prod`, and your own\n`$d custom_flag ? { ... }` — note the `?`.\n\n## No implicit conversions\n\n`int` is 32 bit and `i64` is 64. `f64` and `int` do not mix. Nothing converts\nimplicitly, which is deliberate: a silent narrowing is a bug you find later.\n\nBe explicit at the boundary, and prefer `strconv` over hand-rolled parsing.\n\n## Validation\n\nAfter changing V code, prove it rather than reading it again:\n\n```bash\nv -check path/to/file.v        # type-check only, no binary\nv fmt -verify path/to/file.v  # would the formatter change it?\n```\n\nFor library code rather than a `main` module, `-check` alone fails with *\"project\nmust include a `main` module\"*; use `v -check -shared path/to/file.v`.\n\nRun both before you report the change done. See `v-workflow` for the full loop.\n\n## Related Skills\n\n- **The build and test loop**: see [v-workflow](../v-workflow/SKILL.md) for\n  `v.mod`, dependencies, when `./v self` is required, and the flags that go\n  before the subcommand.\n- **Writing tests**: see [v-testing](../v-testing/SKILL.md) when the change needs\n  a `_test.v` file, or when `?T` and `!T` need asserting.\n- **Asking the compiler**: see [v-mcp](../v-mcp/SKILL.md) when you want the\n  declarations, references or diagnostics of a file without reading it, or when\n  you want to edit through an AST-aware rename.\n- **Memory and GC**: see [v-memory](../v-memory/SKILL.md) for GC modes, ownership\n  checking and `unsafe`.\n\n<!-- File: vlib/v/skills/v-mcp/references/EDITING.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `vlib/v/skills/v-mcp/references/EDITING.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-mcp/references/EDITING.md)\n\n<!-- File: vlib/v/skills/v-mcp/references/TOOLS.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `vlib/v/skills/v-mcp/references/TOOLS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-mcp/references/TOOLS.md)\n\n<!-- File: vlib/v/skills/v-mcp/SKILL.md (Tokens: ~1385 | Category: anthropic-skill) -->\n---\nname: v-mcp\ndescription: Using the V MCP server, `v mcp serve`, to read and change V code through the compiler itself - the AST, declarations, references, diagnostics, stdlib docs, an AST-aware rename, a guarded edit and the formatter. Use when an agent needs to know what a V file declares, whether V code compiles, where a symbol is used, or what a stdlib function takes, and to make a change safely across files. Does not cover the language rules themselves (see v-lang), the build and test loop (see v-workflow), writing test cases (see v-testing), scripting a task in V (see v-scripts), or the command surface outside the MCP server (see v-tools).\nlicense: MIT\n---\n\n# The V MCP server\n\n`v mcp serve` speaks MCP over stdio, or over HTTP with `--http`. It is the V\ncompiler exposed as tools: it calls the parser, the checker and the formatter **in\nprocess**, so it answers correctly about code that does not compile yet.\n\n```json\n{\n  \"mcpServers\": {\n    \"v\": { \"command\": \"v\", \"args\": [\"mcp\", \"serve\"] }\n  }\n}\n```\n\n## Resource Routing\n\n- `references/TOOLS.md` - Read for the arguments of a specific tool, or when you\n  need the tool that answers a particular question.\n- `references/EDITING.md` - Read before changing anything through the server. It\n  covers the guards and why each one exists.\n\n## Quick Reference\n\n| Question | Tool |\n| --- | --- |\n| What project is this? | `v_project_info` |\n| What modules can I import? | `v_modules` |\n| What files are here? | `v_files` |\n| What does this file declare? | `v_symbols` |\n| What is at the cursor? | `v_symbol_at` |\n| Where is this used? | `v_references` |\n| What does this stdlib call take? | `v_stdlib_doc` |\n| Does it compile? | `v_check` |\n| Do the tests pass? | `v_test_run` |\n| What are this app's routes? | `v_veb_routes` |\n| Rename a symbol | `v_rename_symbol` |\n| Change a known range | `v_edit_replace` |\n| Format a file | `v_format` |\n\n## A working order\n\n1. **`v_project_info` once per session.** It names the module, the compiler, the\n   root every path is relative to, and whether this is the V checkout itself.\n2. **`v_symbols`** to learn what a file declares. It carries the doc comments, so\n   it usually answers \"what is this file for\" without reading the body.\n3. **`v_check` after every change.** It is the only authoritative answer to \"does\n   it compile\", and it returns records with file, line and column.\n4. **`v_test_run`** for behaviour. `v_check` cannot catch a wrong result.\n\n## Prefer asking over reading\n\nThe whole point of the server is that it knows more than a file read does. Reach\nfor it before guessing:\n\n- a signature you are unsure of: `v_stdlib_doc`, not an invented one\n- whether an edit is safe: `v_references`, then `v_rename_symbol`\n- what a file contains: `v_symbols`, not a full read\n- what broke: `v_check`, not a re-read\n\nAn invented stdlib signature costs a compile cycle at best. A hand-written rename\nmisses the comment that mentions the name and the call in a file you never opened.\n\n## Editing is guarded by default\n\nThree tools write. None of them writes unless asked twice:\n\n- `v_rename_symbol` defaults to `dry_run: true` and lists every position it would\n  change.\n- `v_format` defaults to `write: false` and reports `before` and `after`.\n- `v_edit_replace` requires `expected_old`: you read the range and pass it back\n  verbatim, so it refuses to write over a change you did not see.\n\nRead the plan, then pass the flag that applies it. See `references/EDITING.md`.\n\n`--read-only` does not register these three at all, so a session started that way\ncannot write by accident.\n\n## Before guessing, ask\n\n- **A language rule**: the `v-lang` skill covers the parts agents get wrong, most\n  of all `?T` versus `!T` and comptime code.\n- **A build or module question**: the `v-workflow` skill, and `v_modules` for what\n  is importable right now.\n- **The code's shape**: `v_ast`, in the same JSON `v ast -p` prints.\n\n## When the tools cannot answer\n\n`v_run`, `v_test_run`, `v_doctor` and `v_eval` start the compiler. They are\nslower than the in-process tools and unavailable when the compiler cannot be\nstarted. A tool that could not start the compiler says so explicitly — it reports\n`started: false` with an error rather than an exit code, so a sandbox that cannot\nspawn processes is never mistaken for a failed check.\n\nIf you see that, the answer is about the environment, not about the code. Do not\nstart changing the code to satisfy a tool that never ran.\n\n## Validation\n\nThe server is the checker. Do not finish a change without it:\n\n```json\n{\"name\": \"v_check\", \"arguments\": {\"path\": \"src/main.v\"}}\n```\n\nThen format what you touched with `v_format`, and run the tests with\n`v_test_run`. See [v-workflow](../v-workflow/SKILL.md) for the equivalent shell\ncommands.\n\n## Related Skills\n\n- **The language rules**: see [v-lang](../v-lang/SKILL.md) for `?T` versus `!T`,\n  sum types, `mut` and comptime code.\n- **The build loop**: see [v-workflow](../v-workflow/SKILL.md) for `v.mod`,\n  dependencies, flags and when `./v self` is required.\n- **Tests**: see [v-testing](../v-testing/SKILL.md) for what to assert.\n- **Web apps**: see [v-veb](../v-veb/SKILL.md) for veb, and use `v_veb_routes` to\n  read the routes of an app you have not opened.\n\n<!-- File: vlib/v/skills/v-memory/references/OWNERSHIP.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `vlib/v/skills/v-memory/references/OWNERSHIP.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-memory/references/OWNERSHIP.md)\n\n<!-- File: vlib/v/skills/v-memory/references/UNSAFE.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `vlib/v/skills/v-memory/references/UNSAFE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-memory/references/UNSAFE.md)\n\n<!-- File: vlib/v/skills/v-memory/SKILL.md (Tokens: ~1485 | Category: anthropic-skill) -->\n---\nname: v-memory\ndescription: V's memory model - GC modes (-gc boehm, -gc none, -prealloc), the optional -ownership checking that catches use-after-move, what unsafe unlocks, and the C interop rules. Use when choosing a GC mode, when a program leaks or grows without bound, when deciding whether unsafe is worth it, when passing a V pointer to C, or when a move or borrow error appears. Does not cover general language rules (see v-lang), data structure choice (see v-lang's structs and maps section), profiling (see v-workflow), scripting a task in V (see v-scripts), or the wider command surface (see v-tools).\nlicense: MIT\n---\n\n# Memory in V\n\nThe default needs no thought: the Boehm collector traces and frees what is no\nlonger reachable. Every question below only arises when you leave that default.\n\n## Resource Routing\n\n- `references/OWNERSHIP.md` - Read when `-ownership` is in play, or when you want\n  a use-after-move caught at compile time rather than at 3am.\n- `references/UNSAFE.md` - Read before writing any `unsafe` block, and before\n  passing a V pointer into C.\n\n## Quick Reference\n\n| Situation | Mode |\n| --- | --- |\n| An ordinary program | default (Boehm GC) — do nothing |\n| A short-lived batch job, or a self-contained binary | `-gc none` |\n| A compiler or batch tool | `-prealloc` |\n| You want use-after-move caught at compile time | `-ownership` |\n| Talking to a C library | `unsafe`, and keep it in one place |\n| Tuning, before guessing | `-stats run` |\n\n**Default**: leave the GC alone. `-gc none` and `-prealloc` are not performance\nswitches; they remove the collector and move the responsibility to you.\n\n## The three modes\n\n```bash\nv run main.v                    # Boehm tracing GC\nv -gc none -prod main.v         # no collector at all\nv -prealloc -prod main.v        # arena allocation\n```\n\n| Mode | Frees | Cost |\n| --- | --- | --- |\n| Boehm (default) | unreachable memory | tracing pauses; memory is not returned promptly |\n| `-gc none` | nothing, unless you do | you must free; no cycles; no finalisers |\n| `-prealloc` | everything, at exit | only good for one-shot single-threaded work |\n\n`-gc none` means no cycle collection, so a reference cycle leaks. For a program\nthat runs once and exits — a compiler, a code generator, a batch script — that\ntrade is usually worth it.\n\n`-prealloc` is documented as suitable only for short-lived, single-threaded,\nbatch-like programs. Do not reach for it in a server.\n\n**Race builds are the exception to all of it.** `-race` does not work with `-gc\nnone` or `-prealloc`: ThreadSanitizer has to see every allocation and free, which\na collector or an arena hides. So a program you want to race-test is a program you\nmust run under the default GC. It also needs `clang` or `gcc` with the\nThreadSanitizer runtime, and prefers `clang`, because gcc does not instrument\ncopies of whole struct values such as strings and arrays, so it misses races on\nthem.\n\n## Check before you optimise\n\n```bash\nv -stats run main.v\n```\n\nThat reports allocations. `-gc none` on a program that allocates heavily and\nexits quickly can be a large win; on a long-running one it is a leak generator.\nMeasure first.\n\n## Ownership: catching a move at compile time\n\nV has an optional ownership system, off by default, enabled with `-ownership`. It\ntracks owned values and rejects using one after it has been moved:\n\n```v ignore\nfn main() {\n\ts1 := 'hello'.to_owned()\n\ts2 := s1          // s1 is moved into s2\n\tprintln(s1)       // error: use of moved value: `s1`\n}\n```\n\nStrings created with `.to_owned()` or `.clone()`, ordinary string slices, and the\n`Owned` / `Copy` / `Drop` struct markers participate. Literals and primitives are\nunaffected; arbitrary structs, maps and slices are not tracked as owned on their\nown. `doc/ownership.md` is the reference; `references/OWNERSHIP.md` here is the summary.\n\n```bash\nv -ownership file.v        # check and compile\n```\n\nIt also defines a `ownership` compile-time flag, so `$if ownership ? { ... }` and\n`*_d_ownership.v` files select the enabled behaviour.\n\n## unsafe\n\n`unsafe` exists for C interop and for a handful of low-level primitives. It is not\na way to make the compiler stop complaining.\n\nThe rule: one `unsafe` block, as small as possible, with a comment saying why it is\nsound. If a function needs two, split it so each has one.\n\n```v ignore\nimport unsafe\n\n// Fixed-size stack buffer; the size must be a compile-time constant.\npub fn (b &Buf) fill(c byte) {\n\tunsafe {\n\t\tb.data = char(c)\n\t\tb.len = b.size\n\t}\n}\n```\n\nSee `references/UNSAFE.md` for what it does not check and what that costs.\n\n## C interop\n\nV's own rule is the one to follow: avoid `C.` and `JS.` in plain `.v` files and put\nthe interop in `.c.v` or `.js.v` files. Then the compiler can tell which code is\nplatform-specific, and `-Wimpure-v` catches an interop call that escaped.\n\n```v ignore\n// buffer_windows.c.v\nmodule main\n\nfn windows_only() int {\n\tunsafe {\n\t\treturn C.GetLastError()\n\t}\n}\n```\n\nA V pointer handed to C must not be freed or moved by the caller, and the C side\nmust not retain it after the V object goes out of scope. See `references/UNSAFE.md`.\n\n## Validation\n\n```bash\nv -ownership file.v            # catch a move at compile time\nv -stats run main.v            # measure before changing the mode\nv -check main.v                # plain, portable V\n```\n\nFor library code add `-shared`. See [v-workflow](../v-workflow/SKILL.md).\n\n## Related Skills\n\n- **The language rules**: see [v-lang](../v-lang/SKILL.md) for `mut` and for when\n  to reach for a struct rather than a map, which changes how much is allocated.\n- **The build loop**: see [v-workflow](../v-workflow/SKILL.md) for `-prod`,\n  `-stats` and the other flags this skill uses.\n- **Concurrency**: see [v-concurrency](../v-concurrency/SKILL.md) for the shared\n  state whose lifetime a lock has to protect.\n\n<!-- File: vlib/v/skills/v-package/references/SUBCOMMANDS.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `vlib/v/skills/v-package/references/SUBCOMMANDS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-package/references/SUBCOMMANDS.md)\n\n<!-- File: vlib/v/skills/v-package/SKILL.md (Tokens: ~591 | Category: anthropic-skill) -->\n---\nname: v-package\ndescription: \"Manage V packages: install, update, search, remove and link local modules.\"\nlicense: MIT\n---\n\n# V packages with vpm\n\nUse this when adding dependencies, resolving a missing package, or working with\n`v install`, `v update`, `v search` or `v link`. For building and testing, see\n`v-workflow`; for language rules, see `v-lang`.\n\nInvoke package commands as `v <subcommand>`, such as `v install <module>`.\nThe frontend dispatches these commands to the `vpm` tool; `v pm` is not a package\nsubcommand.\n\n```bash\nv install <module>   # install a package\nv update             # update the installed packages\nv outdated           # which installed packages have a newer version\nv list               # what is installed\nv remove <module>    # remove a package\nv show <module>      # information about a package\nv search <keyword>   # search vpm.vlang.io\nv why <module>       # why a module is in the dependency graph\nv link               # symlink the current project into the modules directory\nv unlink             # remove that symlink\nv upgrade            # upgrade all outdated modules\n```\n\n## Where modules go\n\n`~/.vmodules`, or `$VMODULES` when it is set. A module is stored under its name\nwith `.` replaced by the path separator, so `prantlf.json` becomes\n`~/.vmodules/prantlf/json`.\n\n## Installing\n\n`v install <module>` installs a package into the modules directory. It does not\nadd the package to the project's `v.mod`; add the dependency there explicitly.\nRun `v install` without package arguments from the project directory to install\nthe dependencies already declared in `v.mod`.\n\nGit packages include their file contents in the initial clone, so checkout can\ncomplete without a second network request for missing file blobs. Submodules are\ninstalled recursively.\n\n## Removing\n\n`v remove <module>` refuses two cases rather than guessing: a module outside the\nmodules directory, and one that was not installed by vpm. For a linked module the\nanswer is `v unlink` first.\n\n## Linking a local module\n\n`v link` symlinks the current project into the modules directory, so a change\nthere is seen without reinstalling. `v unlink` removes the symlink.\n\n## Resource Routing\n\n- `references/SUBCOMMANDS.md` - Read when a subcommand is missing, ignored, or\n  behaves differently from this summary.\n\n## Quick Reference\n\n```bash\nv install <module>\nv update\nv outdated\nv list\nv remove <module>\nv show <module>\nv search <keyword>\nv why <module>\nv link\nv unlink\nv upgrade\n```\n\n<!-- File: vlib/v/skills/v-project/references/TEMPLATES.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `vlib/v/skills/v-project/references/TEMPLATES.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-project/references/TEMPLATES.md)\n\n<!-- File: vlib/v/skills/v-project/SKILL.md (Tokens: ~743 | Category: anthropic-skill) -->\n---\nname: v-project\ndescription: Create V projects with v new or v init and choose a binary, library or web template.\nlicense: MIT\n---\n\n# Creating a V project\n\nUse this when starting a project, scaffolding a module, or choosing a template.\nFor building and testing, see `v-workflow`; for language rules, see `v-lang`; for\nother commands, see `v-tools`.\n\nTwo commands, one decision: `v new` makes a directory, `v init` works in the one\nyou are already in.\n\n## The decision\n\n- `v new <name>` — creates `<name>/` and puts the project in it.\n- `v init` — puts the project in the current directory. Use this when the\n  directory already exists.\n\nBoth take the same three template flags, and both create a `v.mod`.\n\n## Put template flags before the project name\n\nTemplate flags belong to the `new` or `init` subcommand. For `new`, put them\nafter `new` and before the project name:\n\n```bash\nv new myapp           # executable, the default\nv new --lib mylib     # library\nv new --web myapp     # veb web app\n```\n\n`v new mylib --lib` fails with *\"too many arguments\"*: flag parsing stops at the\nproject name, so the later flag is read as another argument.\n\n## What each template makes\n\n| Flag | Entry point | Module | Extra |\n| --- | --- | --- | --- |\n| `--bin` (default) | `main.v` | `main` | — |\n| `--lib` | `<name>.v` | `<name>` | `tests/<fn>_test.v` |\n| `--web` | `main.v` | `main` | `assets/main.css`, `templates/index.html` |\n\nAll three also create `v.mod`, `.editorconfig`, `.gitattributes` and\n`.gitignore`.\n\n## v.mod\n\n```\nModule {\n\tname: 'myapp'\n\tdescription: ''\n\tversion: '0.0.0'\n\tlicense: 'MIT'\n\tdependencies: []\n}\n```\n\nCreating one starts a prompt for the description, version and license. **The\nprompt only runs when stdin is a terminal.** With piped or redirected stdin,\nthe defaults are used. A non-interactive\n`v new` requires the project name; `v init` derives it from the current directory.\nFor example:\n\n```bash\nv new myapp < /dev/null\n```\n\nIf git is installed and the directory is not already a git project, `git init`\nruns as part of setup.\n\n## After it exists\n\n```bash\ncd myapp\nv run .          # build and run an executable or web project\nv -check .       # type-check only\nv test .         # run the tests\n```\n\nThe module name is `main` for executable and web templates. For a library, the\nmodule name is the project name.\n\n## Resource Routing\n\n- `references/TEMPLATES.md` - Read when choosing between `--bin`, `--lib` and\n  `--web`, or when a template's contents need checking against the current\n  compiler.\n\n## Quick Reference\n\n```bash\nv new <name>          # executable project in <name>/\nv new --lib <name>    # library project\nv new --web <name>    # veb project\nv init                # project in the current directory\nv init --lib          # library in the current directory\n```\n\n<!-- File: vlib/v/skills/v-scripts/SKILL.md (Tokens: ~1375 | Category: anthropic-skill) -->\n---\nname: v-scripts\ndescription: Writing V as a scripting language - `.vsh` script mode, its implicit `import os` and unqualified `os` calls, running a script directly, the executable-cache contract that governs rebuilds, compiling a script without running it, and shebang scripts with no file extension. Use when a task is a build, release, maintenance or one-off automation job that would otherwise be bash, when deciding between a script and a small program, or when a `.vsh` file behaves differently from a `.v` file. Does not cover the language rules (see v-lang), the build and test loop (see v-workflow), writing test cases (see v-testing), or the wider command surface (see v-tools).\nlicense: MIT\n---\n\n# V scripts\n\nA file ending in `.vsh` is parsed in **script mode**. It is still V — the same\ntypes, the same modules, the same compiler — but two rules make it read like a\nshell script, and both are worth knowing before writing one, because they are\nwhy a script can fail in a way a program would not.\n\n## What script mode changes\n\n**`os` is imported for you.** A `.vsh` file gets an implicit `import os` when it\ndoes not already import `os` itself. If it does import `os` explicitly, that\nimport wins and keeps its own alias and selected symbols; the implicit one is\nnever added on top.\n\n**`os` functions are called unqualified.** In script mode an unqualified\n`read_file` resolves to `os.read_file`, so the usual `os.` prefix is optional:\n\n```v ignore\nprintln('hello')\ncontent := read_file('data.txt') or { panic(err) }\n```\n\nThat resolution reaches public `os` functions and constants (for example\n`args`). A local or parameter of the same name shadows it, so naming a variable\n`args` or `exists` changes what the bare name means — and that shadowing is\nscoped to the script, not to a `.v` file compiled beside it.\n\nNeither rule applies to a plain `.v` file. If a name resolves in a `.vsh` but not\nin a `.v` file, this is why.\n\n## Running and building scripts\n\nRunning the file runs the script; there is no separate interpreter to install.\n\n```sh\nv script.vsh                 # build if needed, then run\nv run script.vsh             # rebuild and run, keep no executable\nv build script.vsh           # compile to an executable, do not run it\nv -skip-running script.vsh   # the same thing\n```\n\nOn Unix-like systems a script that starts with this shebang runs by its path,\nonce it is executable (`chmod +x script.vsh`, then `./script.vsh`):\n\n```sh ignore\n#!/usr/bin/env -S v\n```\n\nFor `.vsh` scripts on systems whose `/usr/bin/env` has no `-S` (BusyBox,\nOpenBSD), put `cmd/tools/vrun` on the PATH and start the script with\n`#!/usr/bin/env vrun` instead; it runs the script with `v run`.\n\n`v script.vsh` follows **V's executable-cache contract**: it builds the script\nas `script` (the name without `.vsh`) next to the source and reuses that binary\non the next run, which is what makes repeated invocation cheap. It rebuilds when\nthe script is newer than the binary, or when an imported module changed. The\n`#!/usr/bin/env -S v` shebang goes through the same cache.\n\n`v run script.vsh` skips the cache: it rebuilds on every run and leaves no\nexecutable behind, and so do the `#!/usr/bin/env -S v run` and `vrun` shebangs.\nThe cached binary has to be deleted by hand: `v clean` handles `.v` files and\ndirectories, not `.vsh` scripts.\n\n## Scripts with no extension\n\nA file with a fully custom name and a shebang runs as a script when it starts\nwith:\n\n```sh ignore\n#!/usr/bin/env -S v -raw-vsh-tmp-prefix tmp\n```\n\n`tmp` is the prefix for the built executable, which is kept as\n`tmp.<filename>` beside the script. **Caution:** that name is overwritten if it\nalready exists. To rebuild every time instead of caching, use\n`#!/usr/bin/env -S v -raw-vsh-tmp-prefix tmp run`.\n\nThis runs in `crun` mode. V's own documentation recommends it for scripts that go\non the PATH and advises against it for build or deploy scripts.\n\n## Choosing between a script and a program\n\nReach for a script when the job is sequencing external tools, moving files,\nwatching a directory, or wrapping a build step. Reach for a program when there is\ndomain logic to test, when it needs a data structure, or when it will be imported.\n\nThe test suite exists to keep that boundary honest: if a `.vsh` has grown logic\nworth asserting on, move it into a `_test.v` alongside a real program. Script\nmode does not make a script untestable — it compiles like anything else.\n\n## Worked examples in the tree\n\nThese scripts rely on script mode — no `import os`, unqualified `os` calls,\nstatements at the top level — and are worth reading before writing one:\n\n- `examples/v_script.vsh` — creates, lists and removes files with `mkdir`, `ls`\n  and `rm`\n- `.github/problem-matchers/register_all.vsh` — reads `getenv`, then walks a\n  folder with `walk_ext`\n\nA larger `.vsh` can also be written like a program, with `import os` and a\n`fn main`. The scripts bundled with the skills are written that way, for example\n`vlib/v/skills/v-workflow/scripts/check.vsh`; their `#!/usr/bin/env -S v run`\nshebang leaves no binary in the skill folder.\n\n## Related Skills\n\n- **The commands around a script**: see [v-tools](../v-tools/SKILL.md) for\n  `v time`, `v retry`, `v repeat` and `v check-md`.\n- **The build and test loop**: see [v-workflow](../v-workflow/SKILL.md).\n- **The language rules**: see [v-lang](../v-lang/SKILL.md); a script is still V.\n\n<!-- File: vlib/v/skills/v-testing/references/COVERAGE.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `vlib/v/skills/v-testing/references/COVERAGE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-testing/references/COVERAGE.md)\n\n<!-- File: vlib/v/skills/v-testing/references/PATTERNS.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `vlib/v/skills/v-testing/references/PATTERNS.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-testing/references/PATTERNS.md)\n\n<!-- File: vlib/v/skills/v-testing/references/TROUBLESHOOTING.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `vlib/v/skills/v-testing/references/TROUBLESHOOTING.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-testing/references/TROUBLESHOOTING.md)\n\n<!-- File: vlib/v/skills/v-testing/SKILL.md (Tokens: ~1789 | Category: anthropic-skill) -->\n---\nname: v-testing\ndescription: Writing and running V tests - _test.v layout, test_ function naming, the assert builtin, fixtures and cleanup with os.vtmp_dir and defer, and the VTEST_ONLY and VTEST_ONLY_FN filters for a subset. Use when adding or changing a V test, when a test does not compile or does not run, when choosing what to assert, or when the suite is slow and only part of it is needed. Does not cover the rest of the build loop (see v-workflow), the option and result semantics a test has to assert (see v-lang), reading a project through the MCP server (see v-mcp), scripting in V (see v-scripts), or the wider command surface (see v-tools).\nlicense: MIT\n---\n\n# Writing and running V tests\n\nA V test is a `test_` prefixed function in a `_test.v` file. There is no test\nframework and no test object: the function takes no parameters, and `assert` is a\nbuiltin.\n\n## Resource Routing\n\n- `references/PATTERNS.md` - Read when writing a fixture, a table-driven test, or\n  anything that needs a temporary file and cleanup.\n- `references/TROUBLESHOOTING.md` - Read when a test does not compile, does not\n  run, or does not fail the way it should.\n- `references/COVERAGE.md` - Read when measuring which lines and functions a run\n  reached, or generating the tests that are missing.\n\n## Quick Reference\n\n| Need | Do |\n| --- | --- |\n| A test | `fn test_the_thing() { ... }` in `foo_test.v` |\n| Compare | `assert got == want, 'message'` |\n| Test an option | `assert f() == none` |\n| Test a result | `assert f() == none` — it fails with the error text |\n| A temporary file | `os.join_path(os.vtmp_dir(), 'name_${stamp}')` |\n| Cleanup | `defer { os.rm(path) or {} }` |\n| Only some files | `VTEST_ONLY='http' v test dir/` |\n| Only some functions | `VTEST_ONLY_FN='test_login' v test dir/` |\n| Hide passing tests | `v -silent test dir/` |\n| Timings | `v -stats test dir/` |\n| Line coverage | `v -no-skip-unused -coverage vcov/ test dir/` then `v cover vcov/` |\n| Untested functions | `v cover vcov/ --uncovered` |\n| Missing test bodies | `v cover vcov/ --write-tests --filter myapp/` |\n\n## Layout\n\n`foo.v` is tested by `foo_test.v` beside it. A test file holds only `test_`\nfunctions, imports, and helpers.\n\n```v ignore\n// foo_test.v\nimport foo\n\nfn test_it_adds() {\n\tassert add(2, 3) == 5\n}\n\nfn test_it_does_not_mutate_its_argument() {\n\tmut n := 3\n\tincrement(n)\n\tassert n == 3, 'increment changed its argument to ${n}'\n}\n```\n\n**A test function takes no parameters.** Not `*testing.T`, not a context. If you\nare porting a Go or Rust test, this is the first thing to change.\n\nA test file in a library directory does **not** declare `module main`. It imports\nwhat it needs, like any other file. Only a test file that is the `main` module of\nits project says `module main`.\n\n## Assertions say what they compared\n\nA bare `assert` failure prints `assertion failed` and a line number. That is not\nenough to act on.\n\n```v ignore\n// Weak: `assertion failed` and nothing else.\nassert lookup(users, id) == expected\n\n// Strong: the message is the difference.\ngot := lookup(users, id)\nassert got == expected, 'lookup(${id}) = ${got}, want ${expected}'\n```\n\nPut **got before want**. It reads in the order the mind works, and it makes a\nfailing suite scannable.\n\nAssert on values, never on message text, which is free to change:\n\n```v ignore\n// Brittle.\nassert read_config(x) == none\n\n// Correct: absence is the contract.\nif cfg := read_config(x) {\n    assert false, 'expected no config, got ${cfg}'\n}\n```\n\n## Running a subset\n\nThe full suite is slow. Filter it rather than paying for all of it:\n\n```bash\nVTEST_ONLY='http' v test dir/          # only files whose path contains http\nVTEST_ONLY_FN='test_login' v test dir/ # only functions matching\nv -silent test dir/                    # hide passing tests\nv -stats test dir/                     # add timings\n```\n\nBoth filters accept a comma-separated list. File filters use path fragments;\nfunction filters use glob patterns. If a filter matches nothing, the raw runner\nreports zero tests and exits successfully. The wrapper uses the normal reporter\nfor its child runs and preserves other `VFLAGS` options. It reports an empty\nselection as a failure, including when no filter is set:\n\n```bash\nv run scripts/run-tests.vsh dir/ --file http --fn 'test_login*'\n```\n\nSee `references/TROUBLESHOOTING.md` for other causes of tests not running.\n\n## Measuring coverage\n\n```bash\nv -no-skip-unused -coverage vcov/ test ./   # record what the run entered\nv cover vcov/                                # one percentage per file\n```\n\n`-no-skip-unused` is not optional. Without it a function nothing calls is\ndropped from the build, and the report cannot tell \"not tested\" from \"not\nbuilt\".\n\nLine coverage cannot name a function no test calls when that function's body\nholds no statement a line point would record. Two flags answer it:\n\n```bash\nv cover vcov/ --uncovered    # one `path:line function` per untested function\nv cover vcov/ --write-tests --filter myapp/   # the test bodies it is missing\n```\n\nThe generator writes a starting point, not a test — a call with zero values and\na `// TODO:` line, because it cannot know what the function should return. It\nappends to an existing `_test.v` and never touches a test function that is\nalready there. `--dry-run` shows the plan.\n\nSee [references/COVERAGE.md](references/COVERAGE.md) for the whole surface,\nincluding `--functions`, `--json` and what `--write-tests` refuses to do.\n\n## Fixtures\n\n\nThere is no test object to hang a fixture on, so a test writes to `os.vtmp_dir()`\nand registers the removal with `defer`. A unique name per test keeps parallel runs\nfrom colliding.\n\n```v ignore\nfn test_it_reads_a_config() {\n\tstamp := os.getpid()\n\tpath := os.join_path(os.vtmp_dir(), 'cfg_${stamp}.json')\n\tos.write_file(path, '{\"port\": 8080}') or {\n\t\tassert false, 'could not write ${path}: ${err.msg()}'\n\t}\n\tdefer {\n\t\tos.rm(path) or {}\n\t}\n\t...\n}\n```\n\nSee `references/PATTERNS.md` for table-driven tests, sub-suites and helpers.\n\n## Validation\n\nA test that has never failed is not known to test anything. Break it on purpose\nonce, watch it fail for the right reason, then put it back.\n\n```bash\nVTEST_ONLY_FN='test_the_new_thing' v test path/to/file_test.v\nv -silent test path/to/dir/\n```\n\nThen `v fmt -verify path/to/file_test.v`. See [v-workflow](../v-workflow/SKILL.md).\n\n## Related Skills\n\n- **The build loop**: see [v-workflow](../v-workflow/SKILL.md) for `-check`,\n  `fmt -verify`, `vet` and the flags that gate a change.\n- **Option and Result semantics**: see [v-lang](../v-lang/SKILL.md) for what to\n  assert when a function returns `?T` or `!T`.\n- **Through the MCP server**: see [v-mcp](../v-mcp/SKILL.md) when `v_test_run` can\n  report what passed and what the failures said, without parsing runner output.\n\n<!-- File: vlib/v/skills/v-tools/SKILL.md (Tokens: ~1791 | Category: anthropic-skill) -->\n---\nname: v-tools\ndescription: The parts of the V command surface that are easy to miss - inspecting code (`v ast`, `v where`, `v mod why`), shrinking a compiler error (`v reduce`), project hygiene (`v missdoc`, `v check-md`, `v bump`, `v git-fmt-hook`), embedding binaries (`v bin2v`), shell integration (`v complete`), timing and repeating commands (`v time`, `v repeat`, `v retry`), and compiler upkeep (`v up`, `v clean`, `v env`, `v tracev`). Use when a task is about the toolchain rather than the program, when reaching for `v` by habit and a better command exists, or when asked what V can do. Does not cover writing `.vsh` scripts (see v-scripts), the language rules (see v-lang), the build and test loop (see v-workflow), or reading a project through the MCP server (see v-mcp).\nlicense: MIT\n---\n\n# V tools\n\nMost of V's command surface is reached by habit. This is the part that is not:\ncommands that answer a question, shrink a failure, or keep a project honest.\n`v help topics` lists everything; the ones below are the ones worth knowing.\n\n## Look at code before changing it\n\n```sh\nv ast -p file.v         # print the AST as JSON; without -p it writes file.json\nv where fn NAME         # where a symbol is declared; also struct, method, enum, const\nv mod why MODULE        # which import chain pulls a module into the build\n```\n\n`v where` finds declarations only, and searches vlib as well as the project;\n`-mod main` keeps it to the project. Where a symbol is used is answered by the\n`v_references` tool of the MCP server.\n\n`v mod why` is the one that answers a question people otherwise guess at: nothing\nin `v.mod` records what a build actually reaches, so a dependency can outlive the\ncode that needed it.\n\nThrough the MCP server the same ground is covered with less shell work; see\nv-mcp.\n\n## Shrink a compiler error\n\n```sh\nv reduce -m 'text of the error' file.v   # the smallest source that still errors\n```\n\nWithout `-m`, `v reduce` only looks for a C compilation error, so a checker error\nsuch as `unknown function: nope` needs its text passed in. The reduction lands in\n`rpdc_<file>.v` (`rpdc_bad.v` for `bad.v`) in the current directory. Pair it with\n`v bug`, which opens a prefilled issue, and the report carries a minimal\nreproducer instead of the file you happened to be editing.\n\n## Measure test coverage\n\n```sh\nv -no-skip-unused -coverage vcov/ test ./   # record what the run entered\nv cover vcov/                               # one percentage per file\nv cover vcov/ -H                            # the hottest lines\nv cover vcov/ --lcov out.info               # the format CI tools read\nv cover vcov/ --uncovered                   # `path:line function` per untested function\nv cover vcov/ --functions                   # every function, covered or not\nv cover vcov/ --json report.json            # machine-readable, both levels\nv cover vcov/ --write-tests --filter dir/   # the test bodies it is missing\n```\n\nTwo steps: the run records, `v cover` reads. Delete the directory between runs —\nthe counters are appended, so a stale one reports coverage that no longer\nexists.\n\n`--write-tests` generates a `test_` function per untested function, beside its\nsource, as a starting point and not a test: a call with zero values and a\n`// TODO:` line. It appends to an existing `_test.v`, leaves a test function\nthat is already there alone, and refuses without `--filter`, because a coverage\ndirectory holds every file a run reached.\n\nSee [v-testing](../v-testing/SKILL.md) for the details.\n\n## Project hygiene\n\n```sh\nv missdoc src/          # public functions with no documentation comment\nv check-md README.md    # are the ```v blocks in this markdown correct?\nv bump --patch          # or --minor / --major; bumps the version in v.mod\nv git-fmt-hook install  # format on commit\n```\n\n`v check-md` is worth knowing about specifically: it compiles the V examples in a\nmarkdown file, which catches a snippet that stopped compiling long after anyone\nlooked at the prose. `v missdoc` takes `-p` to include private functions and `-t`\nto include function tags.\n\n## Embed binary files\n\n```sh\nv bin2v -m main -w assets.v assets/logo.png assets/font.ttf\n```\n\nWrites each file into `assets.v` as a byte-array constant (`logo_png`, with its\nlength in `logo_png_len`), so a program carries its images and fonts instead of\nreading them from disk at runtime. Without `-w` the source goes to stdout. The\nconstants are private, so generate them into the module that uses them. For a\nsingle file, `$embed_file('assets/logo.png')` does the same at compile time.\n\n## Shell integration\n\n```sh\nv complete setup bash   # print the completion setup script; bash, fish, zsh, powershell\n```\n\nLoad it from the shell's startup file; for bash, add\n`source /dev/stdin <<<\"$(v complete setup bash)\"` to `~/.bashrc`.\n\n## Time, repeat, retry\n\n```sh\nv time CMD              # how long CMD took, and what it exited with\nv repeat CMD...         # repeat commands and collect statistics\nv retry CMD             # rerun until it succeeds, for up to 10 retries or 10 minutes\n```\n\n`v retry` is the one to reach for when a flaky network call or a busy port is the\nthing in the way; `-r` and `-t` change its limits. `v time` is the one when the\nquestion is which of two approaches is actually cheaper.\n\n## Compiler upkeep\n\n```sh\nv up                    # update V itself\nv clean                 # remove what a default build leaves behind\nv env                   # the environment variables that steer the compiler\nv tracev                # a tracing build of the compiler\n```\n\n`v env` is the first thing to read when the compiler or one of its tools behaves\ndifferently on another machine, because most of that behaviour is an environment\nvariable. `v help build-c` documents the C-backend flags — `-cc`, `-glibc`,\n`-musl` — when the C toolchain itself is the thing in question.\n\n## Other commands worth knowing\n\n```sh\nv ls                    # install and run the V language server\nv repl                  # the V REPL\nv watch file.v          # rebuild when the sources it needs change\nv crun file.v           # build and run, keeping the executable\nv tool NAME             # run a tool module by name\nv share file.v          # send a .v file to the V Playground\nv download URL          # fetch a file over http or https\nv symlink               # put v on PATH\n```\n\n`v download -RD URL` also runs the downloaded file with `v run` and deletes it\nafterwards. That executes remote code, so read the file first.\n\nPackage management is `v search`, `v show`, `v list`, `v outdated`, `v update`,\n`v upgrade` and `v remove`, over the VPM registry.\n\n## The command that installs this skill\n\n```sh\nv skills list           # what is bundled, and what is installed\nv skills add v-tools    # install one\nv skills update         # refresh what a new V revision changed\n```\n\n## Related Skills\n\n- **Scripts**: see [v-scripts](../v-scripts/SKILL.md) for `.vsh` script mode and\n  its executable cache.\n- **The build and test loop**: see [v-workflow](../v-workflow/SKILL.md).\n- **The language rules**: see [v-lang](../v-lang/SKILL.md).\n- **Reading a project through the compiler**: see [v-mcp](../v-mcp/SKILL.md) for\n  declarations, references and diagnostics without shell commands.\n\n<!-- File: vlib/v/skills/v-veb/references/MIDDLEWARE.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `vlib/v/skills/v-veb/references/MIDDLEWARE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-veb/references/MIDDLEWARE.md)\n\n<!-- File: vlib/v/skills/v-veb/references/ROUTES.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `vlib/v/skills/v-veb/references/ROUTES.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/vlang/v/HEAD/vlib/v/skills/v-veb/references/ROUTES.md)\n\n<!-- ... +22 more modular skills available via /api/skills/vlang/v?path={path} -->\n</yakaai_skills>"}