agentleFS
MicrosoftKnown publishermicrosoft/verona/AGENTS.md

verona

AGENTS.md3.7k starsChanged 3 months agoMITView on GitHub

Read ~/.config/agents/AGENTS.md for user preferences, workflow rules, and irreversible-action guardrails (git commit/push/PR require explicit permission).

Trending

Quiet this year.about the whole repository

Dec 2025: 0 mentionsJan 2026: 0 mentionsFeb 2026: 0 mentionsMar 2026: 0 mentionsApr 2026: 0 mentionsMay 2026: 0 mentionsJun 2026: 0 mentionsJul 2026: 0 mentionsAug 2026: 0 mentionsSep 2026: 0 mentionsOct 2026: 0 mentionsNov 2026: 0 mentions
Dec 2025Jun 2026Nov 2026
Mentions a month of the microsoft/verona repository that our weekly crawl found. There is no adoption data for this file yet.

Who's talking about microsoft/verona

The biggest 5 of 18 mentions linking to the repository that our crawl found. Each line is their own headline.

See all 18

Talked about on GitHub and Hacker News (18 times).

On GitHub · 12

7 more on GitHub

On Hacker News · 6

1 more on Hacker News

The file

Show its rules and text

We split this AGENTS.md into the rules it gives an agent

13 rules. About 0 are common, 1 is distinctive, and 12 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 12

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Read ~/.config/agents/AGENTS.md for user preferences, workflow rules, and irreversible-action guardrails (git commit/push/PR require …

    line 3
  2. Distinctive:

    Before speculative interpreter/runtime changes, explain the evidence and proposed fix and get feedback.

    line 11
  3. Very specific:

    Baseline before starting work: Before making any changes, run ninja install && ctest -j$(nproc) to verify the full test suite passes.

    line 16
  4. Very specific:

    Debugging: Use gdb for debugging.

    line 18
  5. Very specific:

    Self-referential includes: When resolving a TypeName inside a Use node, lookup() can return the Use node itself via the include mechanism.

    line 25
  6. Very specific:

    Return values: when a label's terminator is Return, refines the returned local against the function's declared return type.

    line 37
  7. Very specific:

    Verona syntax details: No semicolons after closing braces of control flow (for, if, while).

    line 57
  8. Very specific:

    Don't use explicit type annotations on literals when inference can determine the type from context.

    line 87
  9. Very specific:

    Use (obj.field)(args) to call apply on a field access result: (self.data)(i) indexes into a field that is an array.

    line 88
  10. Very specific:

    Use Type(args) constructor sugar: string(result) instead of string::create(result).

    line 89
  11. Very specific:

    Use ! prefix for boolean negation: !(self == other).

    line 93
  12. Very specific:

    Ownership clearing: Only clear parent/cown ownership when root == region->get_entry_point().

    line 101
  13. Very specific:

    Use-after-move in write barrier: When apply_in needs the header for set_parent, save it BEFORE the register is cleared (move semantics).

    line 104

≈ “Common” is an estimate: at least 1 in 5 of the 40,246 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
AGENTS.md

STOP — Read Global AGENTS.md First

Read ~/.config/agents/AGENTS.md for user preferences, workflow rules, and irreversible-action guardrails (git commit/push/PR require explicit permission).

Workflow & Knowledge Storage

  • Durable Verona guidance belongs in this AGENTS.md or .agents/skills/, not in agent-specific memory interfaces.
  • Cross-project workflow rules belong in ~/.config/agents/AGENTS.md.
  • For tricky compiler/runtime/FFI bugs, do principled analysis first: reproduce, trace, inspect dumps, and localize the fault before changing semantics.
  • Before speculative interpreter/runtime changes, explain the evidence and proposed fix and get feedback.

Verona Compiler (vc) Specifics

  • Build / test workflow: Always build in the build directory. Always run ninja install to build; use the installed binaries under build/dist/ (e.g., dist/vc/vc, dist/vbci/vbci). The build binaries under build/vc/vc do NOT have _builtin next to them. ctest runs the full test suite. Use ninja update-dump to regenerate golden test files.
  • Baseline before starting work: Before making any changes, run ninja install && ctest -j$(nproc) to verify the full test suite passes. Do NOT proceed if tests fail — fix the environment first. This prevents committing broken golden files (e.g., segfaults from misconfigured builds).
  • Running vc: Always run vc from the build directory, passing the path to the source directory on the command line (e.g., dist/vc/vc build ../testsuite/v/hello). Do NOT cd into the source directory — the .vbc output name is derived from the directory name, so building with . produces a hidden ..vbc file.
  • Debugging: Use gdb for debugging.
  • Pass-limited testing: -p <passname> stops after a specific pass (e.g., -p ident). --dump_passes=<dir> dumps intermediate ASTs.
  • _builtin resolution: The parser's postparse hook looks for _builtin at executable.parent_path() / "_builtin". Only the installed binary has it.
  • use semantics: use X imports X's contents for unqualified lookup within the current scope, but NOT for qualified lookdown from outside. A Use node with [Include] creates an include entry in the parent's symtab, which is followed by lookup() but not lookdown().
  • Function call resolution: Free function calls must be either qualified (e.g., MyModule::func(args)) or reached via use (intentional shadowing). Unqualified function names are NOT resolved by resolve_first() walking up scopes — only classes, type aliases, and type params are. An unqualified name that doesn't resolve becomes a MethodName → CallDyn (dynamic dispatch on the receiver type). This prevents functions on enclosing scopes from shadowing methods of the same name on other types (e.g., string::!= would have been shadowed by a != function in an enclosing scope).
  • Fully qualified names: After the ident pass, all TypeName/FuncName nodes contain only NameElement children — no relative references. Every name is fully qualified from Top, using scope names with empty TypeArgs for intermediate scopes (even if they have TypeParams). This means names can be resolved without knowing the enclosing scope.
  • Ident pass architecture: Uses NodeWorker<Resolver> with a Processor class. Key methods: resolve_first() (walks up scopes via lookup, builds FQ prefix via build_fq_prefix()), resolve_down() (looks into ClassDef via lookdown), resolve_last() (final type checks), resolve_alias() (follows TypeAlias chains), substitute() (substitutes TypeParam references using a scope-to-prefix-elem map built from the prefix path), find_def() (navigates a fully qualified path from Top).
  • Self-referential includes: When resolving a TypeName inside a Use node, lookup() can return the Use node itself via the include mechanism. The ident pass skips these with n->parent() == def.
  • Shared FQ helpers: scope_path(node) walks to Top and returns a top-down vector of scope nodes. fq_typeparam(path, tp) builds a fully qualified TypeName for a TypeParam. fq_typeargs(path, tps) builds TypeArgs with FQ TypeParam references. make_selftype(node, fq) builds a self-type for a class. All in lang.h/lang.cc, used by ident.cc, sugar.cc, and lang.cc.
  • Reify pass architecture: Monomorphization pass. Starts from main, reifies the call, then iteratively processes a worklist of classes/functions/type aliases. Output WF is wfIR (defined in include/vbcc.h, not vc/lang.h). Uses std::deque<Reification> for pointer stability (worklist holds pointers into deque elements).
    • get_reification(): Navigates FQ names from top, builds a substitution map from TypeArgs, deduplicates against existing reifications, and schedules new ones. The caller's substitution context is kept separate from the navigation-accumulated substitution — only the latter is stored in the Reification for deduplication comparison.
    • make_id(): Builds identifiers — ClassId ^ "scope::name::index", FunctionId ^ "scope::name.arity[.ref]::index".
    • Key ANF→IR transformations: Var → extracted to Vars as LocalId, removed from Body. Lookup 6-child → 3-child (dst, src, MethodId). New (dst, Type, NewArgs) → (dst, ClassId, Args). Call (dst, Lhs, FuncName, Args) → (dst, FunctionId, Args).
    • Shapes: shape definitions (ClassDef with Shape child) are treated as Dyn during reification. get_reification() returns Dyn early, before creating a ClassId, so no class definition is emitted.
    • Primitive types: Map of string→token in reify.cc. reify_primitive() ensures each is reified exactly once. Singleton token nodes must be clone()d before AST insertion.
  • Infer pass architecture: Between ANF and reify. dir::once with post() hook. Per-function: builds a TypeEnv (Location → LocalTypeInfo) from Params, Const, Convert, Copy/Move, Var annotations, New, binops, comparisons, unops. Refines default-typed Const literals (U64/F64) to domain-compatible expected types in these contexts:
    • Call arguments: navigates FuncName to find Function def (filter by arity + handedness), infers empty TypeArgs from actual arg types via direct TypeParam matching, then refines args.
    • Var assignments: refines Consts assigned to explicitly-typed Vars via Copy/Move.
    • New arguments: looks up the ClassDef via find_def(), matches NewArg idents to FieldDef idents, refines based on field types.
    • Return values: when a label's terminator is Return, refines the returned local against the function's declared return type.
    • FFI return types: Navigate up via node->parent(ClassDef), scan ClassBody for Lib nodes, match SymbolId in Symbols, and use the Symbol / Type child as the return type. Walk up through nested ClassDefs if the first doesn't contain the lib.
    • Read semantics: Read gives a read-only reference to a cown. For type inference, the result type is the same as the argument type (same as arithmetic unops).
    • Statement coverage: Tracks types for Const, ConstStr, Convert, Copy, Move, RegisterRef, FieldRef, ArrayRef, ArrayRefConst, New, NewArray, NewArrayConst, Load, Store, Lookup, Call, CallDyn, TypeCond, Var, binops, unops (including Read), nulops, and FFI. When is intentionally untracked (deferred until cowns are working).
    • Uses const_node pointer propagation through Copy/Move for alias tracking; refine_const() clears all aliases after refinement to prevent conflicts.
  • Running compiled programs: dist/vbci/vbci foo.vbc executes a compiled .vbc file (output of vc build). Debug the interpreter with lldb-20 -- dist/vbci/vbci foo.vbc.
  • Verona source syntax notes: Class definitions use bare names (no class keyword): myclass[T] { ... }. Fields need semicolons: val: T;. new uses new { field = val } (no class name). No let/var keyword on class fields. use for imports.
  • Node construction pitfall: auto tn = TypeName copies the TokenDef (deleted copy constructor). Use Node tn = TypeName to create a new AST node.
  • Reify dedup strategy: find_or_push() uses a hybrid approach. Primitives/wrappers use id->equals() with structural IDs (no index embedded). Everything else uses subst_equal() because make_id() embeds r_vec.size() into the ClassId string — calling it before the dedup loop gives each entry a unique ID and dedup silently fails.
  • Runtime "bad type" errors: These usually trace back to unrefined default literal types (U64/F64) in the compiler output that don't match the expected type at the use site. Check the infer pass output (--dump_passes) to see if Const nodes have the expected types.
  • .vbc output naming: vc build <dir> produces <project_name>.vbc in the current directory. If the project name is empty or ".", the output file is .vbc (hidden file).
  • Infer pass cascade re-propagation: The return-type refinement cascade uses a cascade_changed set to track affected locations and prevent oscillation. Without this, a variable assigned from multiple sources (e.g., var x = i32 0; (x, _) = tuple) causes the loop to flip between types infinitely. The set is seeded with locations matching the refined type, then grown monotonically as propagation flows through Copy/Move/Lookup/CallDyn.
  • Infer pass backward refinement: Phase 4 of infer_call backward-refines prior calls whose TypeArgs were inferred entirely from default-typed literals (U64/F64). When a later call reveals the expected type, backward_refine_call() re-extracts constraints, updates TypeArgs, and re-refines Const literals. Includes reverse shape matching (class return type vs expected shape type). Phase 4 must NOT be gated on !subst.empty() — non-generic functions with concrete param types need it too.
  • apply_subst with empty subst: Returns clone(type_node) unchanged — safe to call unconditionally.
  • navigate_call() helper: Shared FuncName navigation for backward_refine_call and infer_call. Filters Function overloads by hand + arity. The arity check in the overload loop makes a post-loop params->size() != args->size() check redundant.
  • Store value refinement: The infer pass refines default-typed Const literals stored through typed refs. When a Store writes through ref[T], try_refine() is called on the stored value's location to refine u64/f64 to T. This makes a.f = 3 work when a.f is i32.
  • CallDyn backward propagation: Phase 4 of infer_call backward-refines prior CallDyn results (not just Call results). When a static Call expects type T for an arg from a CallDyn (e.g., u64::+(1, 2)), backward_refine_calldyn() refines the CallDyn's Const args, re-resolves the Lookup (e.g., u64::+ → i32::+), and updates the env. The CallDyn handler sets call_node = stmt on default-typed results (when receiver is default) to enable this. Default-ness propagates from the receiver — if the receiver is concrete, Phase 1 of CallDyn handles arg refinement via method param types.
  • Test conventions: Compiler tests in testsuite/v/ must be self-contained — no external dependencies (no use "https://..." imports). Use only _builtin types. Tests should not call FFI functions (use :::name(...) syntax for FFI, not regular function calls). Do NOT write use "_builtin" — _builtin is always implicitly available.
  • use for packages: use "url" imports a package from a git repo URL, making its contents available for unqualified lookup. use x = "url" creates a type alias x for the package's top-level scope. The use X form (without quotes) imports a local class/module's contents. _builtin is always implicitly imported — never write use "_builtin".
  • Test golden file format: exit_code.txt has no trailing newline (use printf '0', not echo). stdout.txt and stderr.txt are empty files for silent tests. Golden directory structure: testsuite/v/<name>/<name>/compile/ and testsuite/v/<name>/<name>/run/.
  • Verona syntax details: No semicolons after closing braces of control flow (for, if, while). Semicolons are only needed after statements and field definitions.
  • Array literal syntax: ::(expr, ...) creates an array literal. Parsed in the structure pass as ArrayLit, flattened from Tuple/ExprSeq children, lowered in ANF like tuples but with l_arraylit = Location("array") prefix for tracking. The infer pass does sibling refinement (finds dominant non-default type, refines defaults to match). The reify pass wraps in Array << inner and calls ensure_array_reified to trigger _builtin::array[T] class reification.
  • ensure_array_reified pattern: When a wrapper type (array, ref, cown) is used without an explicit Call that would navigate its ClassDef, reification must be triggered manually. ensure_array_reified looks up the _builtin::array ClassDef, builds a subst mapping T to the element type, and calls find_or_push. Modeled on ensure_ref_reified.
  • Expression binding precedence: Juxtaposition (application) rules live in the dot pass, not the application pass. This makes a(index) (→ a.apply(index)) bind tighter than infix operators, so sum + a(index) parses as sum + (a.apply(index)). The dot pass handles: dot access, TripleColon builtins/FFI, and juxtaposition. The application pass handles: Ref, Hash, infix/prefix function calls, and infix/prefix method calls.
  • Dot consumes arguments: x.f(y) calls method f with arg y (dot rule matches first). To call apply on the result of a field access, use (x.f)(y) (parens force evaluation order) or x.f.apply(y) (explicit method call). This is a deliberate design choice — parsing is type-free and unambiguous.
  • Match expression syntax: (match expr { (pattern) -> body; ... }) else (default). Patterns can be type tests (x: T) or value tests (expr). Value tests use TryCallDyn to call == — if the method doesn't exist or args don't type-check, the match arm simply fails (nomatch) rather than crashing. Multiple arms are chained as else-if with nomatch subtraction at each join point.
  • TryCallDyn: A variant of CallDyn for fallible dynamic dispatch. At runtime, returns an empty Value if the method doesn't exist (func == nullptr) or if argument types don't match (try_check_args fails). Used by value match desugaring in sugar.cc. Defined in: vc/lang.h (WF), vc/passes/sugar.cc (desugaring), vbcc/passes/typecheck.cc (type checking), vbci/thread.cc (runtime), vbcc/bytecode.cc (encoding).
  • Case value vs parameter ambiguity: In lambda params, bare identifiers always parse as ParamDef (with TypeVar type), never as case value Expr nodes. To use a variable as a case value, it must be wrapped in an expression that doesn't match ParamPat (e.g., a function call, typed literal). This is a deliberate parsing choice — type-free and unambiguous.
  • Infer pass return type inference: Post-convergence in process_function(), the return type inference must prefer per-label exit_envs (which have typetest narrowing from Phase B) over the global env. The global env has un-narrowed types (e.g., x: any from $arg: any), while exit_envs have narrowed types (e.g., x: i32 after typetest). Getting this wrong produces imprecise return types like Union(nomatch, any) → reified as dyn → hides all downstream type errors. This matches how Const finalization already prioritizes per-label envs.
  • Compile-error test conventions: Tests expecting compile errors belong under testsuite/v/compile_only/; they have exit_code.txt: 1 in the compile/ golden directory and no run/ directory. The golden files are auto-generated by ninja update-dump.
  • New op/builtin checklist: Adding a new bytecode op requires updates in ~15 places. Use this checklist:
    1. Token definition: include/vbcc.h (token def), include/vbci.h (Op enum)
    2. Frontend WFs in vc/lang.h: wfExprDot (or equivalent early WF), wfPassDot (with <<= Args), wfBodyANF, wfPassANF (with <<= wfDst * wfSrc)
    3. Frontend passes: dot.cc (builtin registration), anf.cc (lowering pattern), infer.cc (type tracking), reify.cc (IR transformation)
    4. Backend WFs in include/vbcc.h: wfStatement, wfIR
    5. Backend Def pattern in vbcc/lang.h: manually maintained list of all statement types that define a register — MUST include the new op if it has a dst LocalId
    6. Backend vbcc/passes/liveness.cc: manually categorized ops for use/def analysis — add to correct category (e.g., use(Rhs) + def(LocalId))
    7. Backend vbcc/bytecode.cc: encoding handler
    8. Backend vbcc/passes/typecheck.cc: type checking if needed
    9. Interpreter vbci/thread.cc: op handler + op name in name array Missing any of items 5 or 6 causes "undefined register" errors in later passes, not at the registration site.
  • New _builtin files affect ALL golden files: Adding a new .v file under vc/_builtin/ changes the compilation output for every test (because _builtin is always parsed). This means ALL golden files need regeneration with ninja update-dump.
  • Init function reification: Init functions (declared with inline bodies in use blocks) are only reified when their lib's FFI symbols are called from reachable code. If no FFI call reaches the lib, the init function won't be reified and won't run. Tests exercising init behavior must include at least one reachable FFI call from the same lib. There is no fini keyword — init returns any, and if the return value is callable (a lambda), the runtime uses it as the finalizer.
  • Startup ordering for once vs FFI init: Program::run() must initialize the scheduler, then fill memo_slots from MemoInit, and only then run FFI init callbacks. Use-block init bodies may call once stubs, which execute MemoLoad and require populated memo slots.
  • FFI fini ordering: Program::run() executes FFI fini callbacks only after sched.run() has returned. Fini callbacks cannot depend on newly queued when behaviors making progress; teardown there must be synchronous, or the relevant libuv handle must already be uv_unrefd so loop exit does not depend on scheduler work.
  • callback(f) constructor sugar: callback(f) is shorthand for callback::create(f) — juxtaposition on a type name calls its create method. This works for any class with a create function.
  • FFI wrapper patterns in _builtin: The _builtin/ffi/ directory contains Verona wrappers for FFI builtins. Pattern: define a class with use block containing FFI symbols, then export wrapper functions. Functions in _builtin/ffi/ are called as ffi::func_name(args) from user code.
  • FFI data passing: When passing a Verona value to an FFI function, the runtime passes a pointer to the data, not to the Verona wrapper type. An array[u8] is passed as uint8_t* (pointer to the array elements). An object is passed as a pointer to its fields, which looks like a C struct (e.g., a class with a: i32; b: i32; looks like struct { int32_t a; int32_t b; }). This means no special builtins are needed to get raw pointers for FFI — just pass the Verona value directly.
  • Package development: use "url" "tag" clones a git repo into _vdeps/<hash>/. For local dev, use "~/dev/pkg" "main" works (local git repo). Changes must be committed to the local repo before the consuming project can see them (the dependency system uses git fetch/checkout).
  • Idiomatic Verona style:
    • Don't use explicit type annotations on literals when inference can determine the type from context. Write 0 not usize 0, var result = 0; not var result = i32 0;.
    • Use (obj.field)(args) to call apply on a field access result: (self.data)(i) indexes into a field that is an array.
    • Use Type(args) constructor sugar: string(result) instead of string::create(result). Juxtaposition on a type name calls its create method.
    • Chain method calls with dot syntax: self.size.min(other.size).
    • For zero-extra-arg methods (only self), call without parens: s.bool, s.size.
    • Delegate comparison operators: implement < and derive <= as !(other < self), > as other < self, >= as !(self < other), != as !(self == other).
    • Use ! prefix for boolean negation: !(self == other).
    • Bitmask test pattern: var result = 0; then if cond { result = result + N } with powers of 2, return result. Exit code 0 means all passed.
  • Freeze architecture: freeze() in vbci/freeze.cc freezes a reachable subgraph within a region, making objects immutable with union-find ARC tracking. Key design points:
    • RC-transfer in SCC union: During SCC detection, scc_union(child, rep) transfers child.rc - 1 to rep (the -1 subtracts the tree edge). Back edges decrement rep.rc before the union loop. At SCC completion, set_arc(rep->get_rc()) gives the exact external ref count.
    • Union-by-address: Uses uintptr_t of the header address as rank (no ties, no storage needed). With path compression, gives O(α(n)) amortized.
    • Pending loop correctness: The back-edge union loop MUST use pending.back() != rep (pointer comparison), NOT Header::find(pending.back()) != rep. Union can change the representative mid-loop, invalidating find-based comparison.
    • frozen_set pattern: A std::unordered_set<Header*> tracks objects frozen by THIS call (inserted at PENDING time). Used to distinguish newly-frozen cross edges from pre-existing immutables in the DFS, and to identify unfrozen→frozen refs during the post-DFS scan.
    • stack_rc adjustment: stack_adjustment = arc_sum - frozen_cross - unfrozen_to_frozen. The unfrozen scan counts field refs from surviving region objects to newly-frozen objects.
    • Ownership clearing: Only clear parent/cown ownership when root == region->get_entry_point(). Sub-regions discovered during DFS are guaranteed to be entered through their entry point (single-entry-point invariant).
    • Entry points: Region tracks the entry point header. Set in set_parent(Region*, Header*) and set_cown_owner(Header*). The entry point is available at all callsites: write barrier has in->get_header(), drag has next_h, cown has content.get_header().
    • freeze_local: Freezes frame-local objects in place, then delegates heap sub-regions to freeze() in phase 2. freeze() delegates frame-local roots to freeze_local().
    • Use-after-move in write barrier: When apply_in needs the header for set_parent, save it BEFORE the register is cleared (move semantics). Use content.get_header() not next->get_header() after extract.
    • clear_cown_owner auto-free: clear_cown_owner() now auto-frees like clear_parent() (calls free_region() if stack_rc == 0). Callers that need to prevent premature free must stack_inc() first (cown destructor and exchange already do this).
  • Type system: Dyn is a top type (anything <: Dyn). The bottom type is an empty Union (no members). In IRSubtype, the axiom table is keyed by the right operand's type (r->type() lookup in reduce()).

Skills

Task-specific knowledge and procedures are stored in .agents/skills/. Each skill is a directory with a SKILL.md file containing instructions. Available skills: add-test, debug, docs-update, review, testsuite, vbci-memory, verona-conventions.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.