From f0cbb88efbc42d30a2319e4647ded0700ac7a5d2 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sat, 7 Nov 2026 13:06:46 +0100 Subject: [PATCH] Consolidate the AGENTS.md rules into a rulebook The root, test-suite, and macro-core guides now contain only rules, and the two macro-core files merge into crates/macros/AGENTS.md. The development commands move to the README, and the macro review procedure moves to the knowledge base's implementation document. The root states the universal rules and the cgp-specific code, test, review, and sibling rules. Co-Authored-By: deepseek-v4.1-flash-1017 --- AGENTS.md | 409 ++++--------------- README.md | 16 + crates/macros/AGENTS.md | 27 ++ crates/macros/cgp-macro-core/AGENTS.md | 96 ----- crates/macros/cgp-macro-extra-core/AGENTS.md | 51 --- crates/tests/AGENTS.md | 227 +--------- 6 files changed, 144 insertions(+), 682 deletions(-) create mode 100644 crates/macros/AGENTS.md delete mode 100644 crates/macros/cgp-macro-core/AGENTS.md delete mode 100644 crates/macros/cgp-macro-extra-core/AGENTS.md diff --git a/AGENTS.md b/AGENTS.md index b8658a5de..50392c0bd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,342 +1,91 @@ # AGENTS.md -This file provides guidance to LLM agents when working with code in this repository. - -## Project status - -This working tree **is** the upcoming **v0.8.0** release. Its crates may carry a pre-release version -such as `0.8.0-alpha` in their `Cargo.toml`, published to crates.io as a test run of the release -process; that suffix is not a version anyone documents or tracks. **Every document, comment, and -README describes this tree as v0.8.0**, as though it had already been published, per the knowledge -base's [rule on versions](https://github.com/contextgeneric/cgp-knowledge-base/blob/main/AGENTS.md#document-official-versions-never-pre-releases): -never name a pre-release, describe how one behaves, or compare one with the source, unless the user -explicitly asks. The same holds for every sibling crate, `cargo-cgp` v0.1.0 included. - -The previous official release, **v0.7.0**, is not compatible with this tree. v0.7.0 carries legacy -features and syntax that v0.8.0 has removed or changed, so code or documentation written against -v0.7.0 will not compile here. Treat v0.7.0 as a separate, older line and do not reconcile this tree's -syntax against it. - -Only the library crates are published. The five crates under [crates/tests/](crates/tests) are -marked `publish = false`, so `cargo publish --workspace` skips them and publishes just the real CGP -crates. - -## The sibling projects - -`cgp` is one repository in a family that is developed together, so a task here routinely reads — and -often updates — another one. [sibling-projects.md](sibling-projects.md) is the authoritative list: -where each project lives, which revision of it to read, and the rules for finding a sibling locally -versus linking to it. Three of them matter constantly. - -**This repository's documentation lives in [`cgp-knowledge-base`](https://github.com/contextgeneric/cgp-knowledge-base).** -There is no `docs/` directory here. The construct reference, the concepts, the guides, the error -catalog, and the macro-implementation notes are all in that repository's -[`cgp/`](https://github.com/contextgeneric/cgp-knowledge-base/tree/main/cgp) directory, and the worked -examples sit at its top level. It is a *consolidated* base, with `cargo-cgp`'s documentation as the -neighbouring section, so an error class here and the tool's handling of it are one directory apart. Treat the knowledge base as part of this repository's surface: **a change to a -construct's behavior carries its documentation change**, and when the checkout is missing, say plainly -what needs updating there. - -**[`cargo-cgp`](https://github.com/contextgeneric/cargo-cgp) is CGP's first-class toolchain**: a cargo -subcommand that rewrites the compiler's diagnostics into readable, root-cause-first CGP errors. It is -the recommended way to check CGP code and to read a CGP compile error, so recommend it -wherever CGP errors are discussed, and prefer it over raw `cargo check` when diagnosing a wiring -failure here. When a construct change alters a diagnostic it has a UI fixture for, update the fixture -in the same change, following that project's own conventions. - -**[`cgp-skills`](https://github.com/contextgeneric/cgp-skills) holds the `/cgp` skill**, the distilled -view of the knowledge base that every agent loads first. It is the easiest view to leave stale, so a -change to a construct's syntax, expansion, defaults, or recommended form propagates out to the matching -sub-skill in the same change. - -**Finding a sibling.** Look first in the parent directory, at `../cargo-cgp`, -`../cgp-knowledge-base`, `../cgp-skills`. When the checkout is present, use it — it is the fastest -reference and reflects any uncommitted work. When it is absent, fetch the file you need from the -project's repository at the revision [sibling-projects.md](sibling-projects.md) records. - -**Writing cross-project links.** A *link* from a committed file here to a document in another -project — a Markdown link or a URL — is **always written as a GitHub URL** on `main` -(`https://github.com/contextgeneric//blob/main/`), never as a relative -`../cargo-cgp/...` link, so it resolves for a reader who has only this repository checked out. When -*reading* such a link yourself, prefer the local checkout. A bare mention of a checkout's location, -like the path `../cargo-cgp`, is a filesystem reference rather than a link and stays relative — as is -a doc pointer in a source comment, which names its knowledge-base path -(`cgp-knowledge-base/cgp/implementation/entrypoints/cgp_impl.md`). - -**The code dependency stays one-way.** No `cgp` *crate* may depend on a `cargo-cgp` crate: -`cargo-cgp` reads `cgp`, never the reverse. Documentation is the deliberate exception, since the knowledge -base and the skill both recommend `cargo-cgp`, so the *code* graph stays acyclic and the documentation -need not. Post-codegen compile-fail cases are `cargo-cgp` UI fixtures, and the -[error catalog](https://github.com/contextgeneric/cgp-knowledge-base/blob/main/cgp/errors/README.md) -links each error class to the fixture that backs it. - -## Orient before any task - -This repository **is** the implementation of Context-Generic Programming (CGP), and its behavior is -recorded as much in the [knowledge base](https://github.com/contextgeneric/cgp-knowledge-base/tree/main/cgp) -as in the code. Before starting any task here — reading, writing, reviewing, debugging, or answering a -question — load the CGP mental model and the documentation that covers what you are about to touch. -The following steps are standing requirements: they apply to every task regardless of how small it -looks, not just to the macro review workflow below. Prefer the local `../cgp-knowledge-base` checkout -when reading them, and note that its [summary.md](https://github.com/contextgeneric/cgp-knowledge-base/blob/main/summary.md) -lists every document in one page, which is the fastest way to find the few a task needs. - -- **Always invoke the `/cgp` skill** to load the fundamentals (consumer vs. provider traits, - `#[cgp_component]`/`#[cgp_impl]`/`#[cgp_fn]`, `delegate_components!`, `HasField`, `UseDelegate`, - check traits, and so on). Re-invoke it whenever you move into an unfamiliar construct — the macros - and core traits here are the ground truth the skill describes, so read the two together. -- **Always read the knowledge base's [`cgp` section README](https://github.com/contextgeneric/cgp-knowledge-base/blob/main/cgp/README.md)** to orient, - then follow it into the README of whichever part covers your task. -- **Read the [construct reference](https://github.com/contextgeneric/cgp-knowledge-base/blob/main/cgp/reference/README.md) and the relevant reference - documents whenever the task requires understanding a CGP construct** — what it means, what syntax - it accepts, and what code it expands to. -- **Read the [implementation reference](https://github.com/contextgeneric/cgp-knowledge-base/blob/main/cgp/implementation/README.md) and the relevant - implementation documents whenever the task involves reading or modifying the CGP source code** — - they map each macro to its `cgp-macro-core`/`cgp-macro-lib` internals, corner cases, and tests. -- **Load the `/point-first-writing` skill whenever the task involves editing markdown documentation or - inline code comments**, and follow its writing convention for any prose you add. - -The canonical export surface for users is `cgp::prelude` — see -[crates/main/cgp/src/prelude.rs](crates/main/cgp/src/prelude.rs), which re-exports -`cgp_core::prelude` + `cgp_extra::prelude`. When unsure what a name resolves to, start from the -prelude re-exports in [crates/main/cgp-core/src/prelude.rs](crates/main/cgp-core/src/prelude.rs). - -## Commands - -This is a Cargo workspace (edition 2024, resolver 3). Toolchain is pinned to **1.98.1** via -[rust-toolchain.toml](rust-toolchain.toml). Nearly every crate is `#![no_std]` — keep new code -`no_std`-compatible (use `core`/`alloc`, gate `std`/`alloc` usage behind features as existing crates -do). - -- **Format** (requires nightly — `.rustfmt.toml` uses unstable `group_imports`/`imports_granularity`): - `cargo +nightly fmt --all` (check: `cargo +nightly fmt --all -- --check`) -- **Lint:** `cargo clippy --all-features --all-targets -- -D warnings` - and `cargo clippy --no-default-features --all-targets -- -D warnings` -- **Test** (uses `cargo-nextest`): `cargo nextest run --all-features --no-fail-fast --workspace`. - This runs the whole suite: `cgp-tests` (behavior + expansion snapshots) and `cgp-macro-tests` - (macro internals, rejection cases, invalid-expansion snapshots). -- **Single test crate / test:** `cargo nextest run -p cgp-tests` or target one file with the - standard test harness, e.g. `cargo test -p cgp-tests --test component` -- **Post-codegen compile failures** (input a macro accepts but whose expansion fails to compile) are - tested elsewhere: they are UI fixtures in [`cargo-cgp`](https://github.com/contextgeneric/cargo-cgp), - the first-class error toolchain, and cataloged in the [error catalog](https://github.com/contextgeneric/cgp-knowledge-base/blob/main/cgp/errors/README.md). See - [crates/tests/AGENTS.md](crates/tests/AGENTS.md) for the workflow. -- Many "tests" are **compile-time wiring checks** (`check_components!` / - `delegate_and_check_components!`) and **macro-expansion snapshots** — for these, a successful - `cargo build`/`cargo test` compilation *is* the passing test. A wiring mistake surfaces as a - compile error, not a runtime failure. - -## Architecture: layered micro-crates - -Crates are organized so that low-level primitives have no knowledge of the high-level facade. Work -inward (core/macros) when changing fundamentals, outward (main) only to adjust the public surface. - -- **`crates/macros/`** — the proc-macro pipeline. `cgp-macro` is a thin `#[proc_macro]` entrypoint - that forwards to `cgp-macro-lib` (one module per macro), which in turn builds on - **`cgp-macro-core`** — this is where the real parsing, AST types, and codegen live (see - `cgp-macro-core/src/{types,functions,visitors,macros}/`). When a macro misbehaves, the logic to - fix is almost always in `cgp-macro-core`, not the entrypoint crate. `cgp-async-macro` provides - `#[async_trait]`. The extra-feature macros (`#[cgp_computer]`, `#[cgp_producer]`, - `#[cgp_auto_dispatch]`) mirror the same split: `cgp-macro-extra` forwards to - `cgp-macro-extra-lib`, which builds on **`cgp-macro-extra-core`**, which in turn builds on - `cgp-macro-core`, lowering through its AST types. - -- **`crates/core/`** — the foundational runtime traits the macros expand into: - - `cgp-component` — the wiring machinery: `DelegateComponent`, `IsProviderFor`, - `CanUseComponent`, `UseContext`, `UseDelegate`, `UseField`, `WithProvider`, etc. - - `cgp-type` — abstract types: `HasType`, `TypeProvider`, `UseType`. - - `cgp-field` — `HasField` and extensible data: `Cons`/`Nil`, `Symbol`, `Index`, `Field`, - builders/extractors for records and variants. - - `cgp-error` — `HasErrorType`, `CanRaiseError`, `CanWrapError`. - - `cgp-base-types` — the lowest-level type-level primitives (`Symbol`/`Chars`/`Cons`/`Nil`/path). - -- **`crates/extra/`** — higher-level building blocks layered on core: `cgp-handler`, - `cgp-dispatch`, `cgp-monad`, `cgp-run`, `cgp-runtime`, `cgp-field-extra`, `cgp-error-extra`. - -- **`crates/main/`** — facade crates that only re-export. `cgp` is the crate users depend on - (`cgp = core + extra`, exposing `cgp::prelude`). `cgp-core`/`cgp-extra`/`cgp-base`/ - `cgp-base-extra` are intermediate bundles. Changes here are almost always just re-export plumbing. - -- **`crates/standalone/error/`** — pluggable error backends implementing the `cgp-error` traits: - `cgp-error-anyhow`, `cgp-error-eyre`, `cgp-error-std`. These are opt-in and not part of the - default `cgp` facade. The knowledge base documents them as a project of their own, under - [`projects/error/`](https://github.com/contextgeneric/cgp-knowledge-base/tree/main/projects/error), - and their tests are the `error_backends` target of `cgp-tests`. - -- **`crates/tests/`** — `cgp-tests` exercises real wiring and the user-facing macros end-to-end; - `cgp-macro-tests` covers parser corner cases plus **expansion snapshots** via the `snapshot_*` - proc macros in `cgp-macro-test-util{,-lib}` (which pretty-print generated code with - `prettyplease`). When you change macro codegen, expect snapshot output to change — update and - review the expanded code, since it is the contract users see. - -## Conventions specific to this repo - -- All versions are kept in lockstep at the workspace level (the release in preparation, **0.8.0**, - whatever pre-release suffix the manifests carry while it is tested); inter-crate - dependencies are declared once in the root [Cargo.toml](Cargo.toml) `[workspace.dependencies]` - and referenced with `{ workspace = true }`. Add new crates to the `members` list and the - workspace dependency table together. A crate that exists only for testing also gets - `publish = false` so `cargo publish --workspace` excludes it from the published release. -- The crate split is deliberate (coherence-friendly micro-crates). When adding functionality, - place it in the lowest layer that makes sense and re-export upward through the facade crates, - rather than adding cross-layer dependencies that skip the hierarchy. -- See [CHANGELOG.md](CHANGELOG.md) for the evolution of macro syntax — it is the most reliable - record of which macro forms are current vs. removed (e.g. `#[cgp_context]` was removed, - `ProvideType` → `TypeProvider`). +Read this file before working in this repository. The +[knowledge base's AGENTS.md](https://github.com/contextgeneric/cgp-knowledge-base/blob/main/AGENTS.md) +carries the canonical wording of the universal rules. Change a universal rule there first, then +update this block to match. + +## Universal rules + +- Prefer the local sibling checkout when reading another project. +- Write a cross-project link as a GitHub URL on `main`. +- Name official release versions only, never a pre-release. +- Commit only when the user explicitly asks, on the current branch. +- Verify a claim against the source before writing it. +- Keep every view of the same truth in sync in the same change. +- Ask the user when a decision is ambiguous. +- Apply point-first writing to internal prose. + +## Versions + +- Document this tree as v0.8.0. +- Treat v0.7.0 as a separate, older line; do not reconcile this tree's syntax against it. + +## Before any task + +- Invoke the `/cgp` skill before reading, writing, reviewing, or debugging CGP code. +- Re-invoke it when you move into an unfamiliar construct. +- Read the knowledge base's [cgp README](https://github.com/contextgeneric/cgp-knowledge-base/blob/main/cgp/README.md), + the [construct reference](https://github.com/contextgeneric/cgp-knowledge-base/blob/main/cgp/reference/README.md), + and the [implementation reference](https://github.com/contextgeneric/cgp-knowledge-base/blob/main/cgp/implementation/README.md) + when the task needs them. +- Load the `/point-first-writing` skill before editing Markdown or inline comments. +- Start from `cgp::prelude` and the prelude re-exports when a name is unclear. + +## Code rules + +- Keep every crate `no_std`-compatible unless it already requires `std`; gate `std` and `alloc` behind + features. +- Place functionality in the lowest layer that makes sense and re-export it upward. +- Do not add a cross-layer dependency that skips the hierarchy. +- Keep all crate versions in lockstep at the workspace level. +- Declare inter-crate dependencies once in `[workspace.dependencies]` and reference them with + `{ workspace = true }`. +- Mark a test-only crate `publish = false`. +- Treat [CHANGELOG.md](CHANGELOG.md) as the record of which macro forms are current and which are + removed. +- Add a `///` to every public item that lacks one, and keep inline docs brief. +- Run the repository's checks before committing; the commands are in [README.md](README.md). +- Put a post-codegen compile failure in a `cargo-cgp` UI fixture, not in this repository, and update the + matching fixture when a construct change alters a diagnostic. + +## Tests + +- A compile-time wiring test passes when the crate compiles; still run the suite for the runtime tests. +- Update the expansion snapshots when macro codegen changes, and review the expanded code. ## Macro review workflow -This section defines the standing process for reviewing one CGP macro implementation at a time, -hardening it until no further issue is found. The goal of an iteration is a macro whose -implementation, tests, and documentation are correct, complete, mutually consistent, and as simple -as the behavior allows. - -### Orient before touching anything - -Perform the standing steps in [Orient before any task](#orient-before-any-task) first, every -iteration. Then read the knowledge base's documentation for the macro under review: its -[reference document](https://github.com/contextgeneric/cgp-knowledge-base/tree/main/cgp/reference), its -[implementation documents](https://github.com/contextgeneric/cgp-knowledge-base/tree/main/cgp/implementation) — the `entrypoints/` document, the -`asts/` stack it drives, and any `functions/` helpers it relies on — and the `AGENTS.md` files that -define how those documents stay in sync with the code -([the `cgp` section's](https://github.com/contextgeneric/cgp-knowledge-base/blob/main/cgp/AGENTS.md), -[the implementation tree's](https://github.com/contextgeneric/cgp-knowledge-base/blob/main/cgp/implementation/AGENTS.md), and -[cgp-macro-core's](crates/macros/cgp-macro-core/AGENTS.md)). These establish -that the source is the single source of truth and that reference, implementation, snapshot, and -skill are four views of it that must never drift. - -Next, study the implementation itself in [crates/macros/](crates/macros). Start from the -`cgp-macro-lib` entry function, follow it into the `cgp-macro-core` `types//` AST stack -and the `functions/` helpers it calls, and read closely enough to reason about corner cases, not -just the happy path. Finally, study the tests in [crates/tests/](crates/tests) — the behavioral -tests in `cgp-tests` and the failure cases and expansion snapshots in `cgp-macro-tests` — and read -[crates/tests/AGENTS.md](crates/tests/AGENTS.md) to learn how the suite is organized and how to run -and update it. - -### Harden the implementation and its tests - -With the macro understood, work through the review in these areas. Each is a distinct concern; -treat correctness as non-negotiable and simplification as a judgment call, and never let a -readability edit introduce a behavioral change. - -- **Fix bugs and corner cases.** Identify potential bugs and unhandled corner cases in the - implementation and fix them. When a corner case cannot be fixed in this iteration, capture it as a - failure case in `cgp-macro-tests` and record it under the construct's Known issues, per - [crates/tests/AGENTS.md](crates/tests/AGENTS.md). -- **Close test gaps.** Add tests for corner cases that are not yet covered, placing each in the - concept target that owns the behavior and snapshotting only in the macro's owning target. -- **Verify existing tests.** Confirm each existing test really exercises the behavior it claims to, - that the corner case it checks makes sense, and that it makes appropriate assertions wherever an - assertion is possible rather than relying on compilation alone. -- **Deduplicate and simplify tests.** Merge or remove tests that check the same or overlapping - behavior, and factor common boilerplate into shared test helpers. -- **Improve the documentation and inline docs.** Update the reference document, the implementation - documents, and any README when they are inconsistent with the code or when something is worth - explaining or clarifying; add a brief `///` to any public struct, trait, or function that lacks - one; and simplify existing inline docs, removing facts that are obvious from reading the code. - -### Scrutinize the macro codegen - -A CGP macro is only as correct as the code it emits, so review it against the full space of inputs -it can parse and outputs it can expand, not the happy path its tests exercise. A gap in any area -below is a latent miscompilation — or a broken editor experience — waiting for the right input. -**Apply every area to every macro you review**, not only the ones that already have a fixture or a -worked example for it; these concerns recur across the whole suite, and a macro that has never been -audited for one of them (spans especially) most likely has the bug. The areas below are the *what to -check*; the [cross-cutting implementation notes](https://github.com/contextgeneric/cgp-knowledge-base/blob/main/cgp/implementation/README.md#cross-cutting-implementation-notes) -explain *why the code behaves this way*, and most areas link out to the note or process doc that -carries their full detail. - -#### Attributes - -Enumerate the attributes the macro accepts and confirm each is parsed, validated, mutually -constrained, and rejected when unknown: a bad attribute should fail with a spanned error, a mutually -exclusive pair should error when both appear, and a duplicate should not be silently accepted (nor -accepted for one attribute while rejected for another). - -#### Parsing the input - -Build every `syn` node from quasi-quoted tokens with -[`parse_internal!`](https://github.com/contextgeneric/cgp-knowledge-base/blob/main/cgp/implementation/macros/parse_internal.md) rather than `parse2`/`parse_quote!`, -and thread `syn::Result` so a malformed fragment propagates a named error instead of panicking; -reserve the panicking `parse_quote!` for tokens trivially guaranteed to parse and `parse2` for -re-parsing tokens already known valid. Reject malformed or unsupported input early, at the macro's -own parse stage with a spanned message, rather than letting it surface deep inside fragment parsing. -Walk the full input space — path-qualified types, generics, lifetimes, arrays, tuples, turbofish, -associated-type bindings — and confirm none reaches a parser that fails obscurely; since `syn` parses -far more leniently than Rust accepts, validate against CGP's restricted argument types. See -[Parsing](https://github.com/contextgeneric/cgp-knowledge-base/blob/main/cgp/implementation/README.md#parsing-build-with-parse_internal-and-distrust-syns-leniency). - -#### Expanding the output - -Walk the shapes the expansion can take across the whole input space and confirm none produces invalid -Rust — no conflicting `impl` blocks from a cartesian expansion, no unbound or doubly-declared generic, -no empty expansion that checks nothing, no clash on a generated identifier. When an expansion fails to -compile, capture it as a [`cargo-cgp` UI fixture](https://github.com/contextgeneric/cargo-cgp/blob/main/tests/README.md) -(post-codegen compile-fail cases live there, filed by their error class) -and catalog the class in the [error catalog](https://github.com/contextgeneric/cgp-knowledge-base/blob/main/cgp/errors/README.md); when the failure is a CGP *defect* -the macro should have rejected, also record it under the macro's `## Known issues`, per -[crates/tests/AGENTS.md](crates/tests/AGENTS.md) and [cgp-knowledge-base/cgp/implementation/AGENTS.md](https://github.com/contextgeneric/cgp-knowledge-base/blob/main/cgp/implementation/AGENTS.md). - -#### Generics - -Keep the *kinds* (lifetime, type, const) and the *roles* (impl generics `impl` versus type -generics, the `` in `Foo`) distinct, render each in the right position, merge parameters from -different sources without colliding, and bind every parameter that appears in a generated header so -nothing is left free. See -[Generic-parameter insertion](https://github.com/contextgeneric/cgp-knowledge-base/blob/main/cgp/implementation/README.md#generic-parameter-insertion-and-lifetime-ordering) -and [keeping the kinds distinct](https://github.com/contextgeneric/cgp-knowledge-base/blob/main/cgp/implementation/README.md#generics-keep-the-kinds-and-roles-distinct). - -#### Qualified paths and hygiene - -Emit every CGP item through a `crate::exports` marker so it resolves as `::cgp::macro_prelude::`, -never as a bare or hand-written path — grep the codegen for any CGP name not interpolated from an -`exports` marker. Give the reserved identifiers the expansion introduces the double-underscore form -(`__Context__`, `__Provider__`, `__Component__`, …) so they cannot clash with a user's names, and keep -the expansion idempotent when the same entry is listed more than once. See -[Hygiene](https://github.com/contextgeneric/cgp-knowledge-base/blob/main/cgp/implementation/README.md#hygiene-exports-markers-and-reserved-identifiers). - -#### Spans: for the compiler *and* the IDE +Review one macro at a time when the user asks for a macro review, and continue until no further issue +is found. Follow the detailed procedure in +[cgp-knowledge-base/cgp/implementation/review.md](https://github.com/contextgeneric/cgp-knowledge-base/blob/main/cgp/implementation/review.md). -Span placement is a correctness concern for two tools at once, and it applies to **every macro that -emits items** — `#[cgp_impl]`, `#[cgp_provider]`, `#[cgp_new_provider]`, `#[cgp_type]`, -`#[cgp_getter]`, and the derives — not just `delegate_components!`/`check_components!`. By default -`parse_internal!`/`quote!` stamp generated tokens with the macro's `call_site` span (the whole -invocation), which misleads both tools, so confirm both for each macro: +- Orient first: read the macro's reference and implementation documents, its source, and its tests. +- Fix bugs and corner cases; close test gaps; verify each test exercises what it claims; deduplicate + the tests; improve the documentation and inline docs. +- Scrutinize the codegen across attributes, parsing, expansion, generics, hygiene, and spans. +- Capture an accepted input whose expansion fails to compile as a `cargo-cgp` UI fixture, and catalog + the class. +- Keep the reference, implementation documents, snapshots, and skill in sync in the same change. +- Run the format, clippy, and test checks for the affected crates before finishing. +- Ask when a behavior, corner case, or design choice is ambiguous. -- **Compiler carets.** An error on a generated item's header — a coherence conflict (`E0119`), an - unsatisfied bound, a name-resolution failure — must point at the entry the user wrote, not the whole - macro block. Re-span each generated item onto its originating token: with - [`override_item_span`](crates/macros/cgp-macro-core/src/functions/override_span.rs) for a generated - impl, a carried span field where the origin token is synthesized (`EvaluatedCheckEntry.span`, - `EvaluatedDelegateEntry.span`), or reuse of the user's own `for` token. -- **IDE go-to-definition.** rust-analyzer maps a source token to its expansion by source range alone, - ignoring hygiene, so never drag a resolvable reference (`IsProviderFor`, `DelegateComponent`) onto a - user token's range, and span a *derived name* (a component marker) on the user identifier it derives - from, not `Span::call_site()`. A leaked derived name makes go-to-definition on a reference offer the - defining macro alongside the item. +## Sibling projects -Only the caret half is pinned by a test (a `trybuild` `.stderr` fixture records each caret's exact -position, so a regression changes the snapshot). The IDE half has no fixture and must be checked by -hand in an editor — and cross-crate, since the derived-name leak is invisible from inside the defining -crate. See [Spans](https://github.com/contextgeneric/cgp-knowledge-base/blob/main/cgp/implementation/README.md#spans-aim-generated-items-at-the-token-the-user-wrote) -for the full mechanism and the `delegate_components!`/`#[cgp_impl]` worked examples. +- Read [sibling-projects.md](sibling-projects.md) to find a sibling and the revision to read. +- Use the local checkout first; fetch the file from the repository when it is absent. +- Write a link to another project as a GitHub URL on `main`; keep a bare checkout path relative. +- Keep the code dependency one-way: no `cgp` crate may depend on a `cargo-cgp` crate. +- Prefer `cargo-cgp` over raw `cargo check` when diagnosing a wiring failure. +- Propagate a construct change to the matching skill sub-skill and to `cargo-cgp` when it changes a + diagnostic. -### Keep every view in sync and verify +## AGENTS.md files -Every change propagates to all four views in the same change, per the synchronization rule. When -you alter the macro's behavior, syntax, expansion, or defaults, update the reference document's -Expansion, the implementation document's Pipeline and Generated items, the affected snapshots, and -the `/cgp` skill. When you move or rename a test, update the implementation document's Tests or -Snapshots section. Then verify the work: run `cargo +nightly fmt --all`, the clippy invocations, and -`cargo nextest run` for the affected crates (and `cargo insta` to review any snapshot diffs) as -described in the Commands section above, confirming a green suite before considering the iteration -done. +- Add a nested `AGENTS.md` only when its directory has significant rules of its own; otherwise extend + this file. -### Ask when in doubt +## Committing -During the review, ask the user for clarification whenever something should be settled before the -next step is taken — an ambiguous intended behavior, a corner case whose correct outcome is unclear, -or a design choice with more than one defensible answer. Surface the question rather than guessing, -since a wrong assumption baked into the source, tests, and four documentation views is expensive to -unwind. +- Never commit as a side effect, suggest a commit unprompted, or treat a request as standing + authorization. diff --git a/README.md b/README.md index 1e5efed99..cf4d6d2bc 100644 --- a/README.md +++ b/README.md @@ -152,6 +152,22 @@ CGP is a collection of small, layered crates, arranged so that low-level primiti Contributions, questions, and bug reports are welcome. Open an [issue](https://github.com/contextgeneric/cgp/issues) or a pull request, and see the [website](https://contextgeneric.dev/) to get oriented first. Unless stated otherwise, contributions are made under the terms of the Apache-2.0 license below. +### Development + +Run the checks from the repository root. The toolchain is pinned in `rust-toolchain.toml`, and most +crates are `#![no_std]`, so keep new code `no_std`-compatible. + +```sh +cargo +nightly fmt --all +cargo clippy --all-features --all-targets -- -D warnings +cargo clippy --no-default-features --all-targets -- -D warnings +cargo nextest run --all-features --no-fail-fast --workspace +``` + +The suite covers `cgp-tests` for behavior and expansion snapshots and `cgp-macro-tests` for the macro +internals. Post-codegen compile failures are UI fixtures in +[`cargo-cgp`](https://github.com/contextgeneric/cargo-cgp). + ## License Licensed under the [Apache License, Version 2.0](LICENSE). diff --git a/crates/macros/AGENTS.md b/crates/macros/AGENTS.md new file mode 100644 index 000000000..1df3518f6 --- /dev/null +++ b/crates/macros/AGENTS.md @@ -0,0 +1,27 @@ +# AGENTS.md + +The root [cgp/AGENTS.md](../../AGENTS.md) rules apply. Invoke the `/cgp` skill before working here. + +## Shared rules + +- Parse input by implementing `syn::parse::Parse`; emit code by implementing `quote::ToTokens`. +- Build AST nodes with `parse_internal!`, inside a `syn::Result`-returning function. +- Emit every CGP item through an `exports` marker; add a new item to `export_constructs!`. +- Keep the staged `preprocess` → `eval` → `to_items` pipeline. +- Define a custom keyword with `define_keyword!` and `IsKeyword`. +- Rewrite ASTs with `VisitMut` visitors, never with string manipulation. +- Add a one-line `///` to every public struct, trait, and function that lacks one. +- Put parser corner cases and expansion snapshots in `cgp-macro-tests`. + +## Core rules + +- `cgp-macro-core` has no `#[proc_macro]` entrypoints and does not depend on `proc-macro`. +- The crate's module map and pipeline are documented in the knowledge base's implementation + documents. + +## Extra-core rules + +- Lower through `cgp-macro-core`'s intermediate representation, not by emitting a nested macro call. +- Reuse `cgp-macro-core`'s `exports` markers rather than declaring a duplicate. +- Build each intermediate node with `parse_internal!`. +- The crate's module map is documented in the knowledge base's implementation documents. diff --git a/crates/macros/cgp-macro-core/AGENTS.md b/crates/macros/cgp-macro-core/AGENTS.md deleted file mode 100644 index c00530e7d..000000000 --- a/crates/macros/cgp-macro-core/AGENTS.md +++ /dev/null @@ -1,96 +0,0 @@ -# AGENTS.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -`cgp-macro-core` is the pure-logic core of the CGP proc-macro suite. **Always invoke the `/cgp` -skill before working here** — every type in this crate exists to generate one of the CGP constructs -the skill describes (`#[cgp_component]`, `#[cgp_impl]`, `#[cgp_fn]`, `delegate_components!`, -`check_components!`, `#[derive(HasField)]`, …). You cannot correctly change the *output* of a macro -without knowing the *expansion* it is supposed to produce, and that expansion is documented by -`/cgp`. The workspace-level [../../../AGENTS.md](../../../AGENTS.md) covers build/test/lint commands -and the crate hierarchy. - -## What this crate is (and isn't) - -- It contains **no `#[proc_macro]` entrypoints** and does **not depend on `proc-macro`** — only - `syn` (with `full`, `extra-traits`, `visit`, `visit-mut`), `quote`, `proc-macro2`, `itertools`. - Everything operates on `proc_macro2::TokenStream` and `syn` AST. -- The proc-macro pipeline is **`cgp-macro` (entrypoints) → `cgp-macro-lib` (per-macro glue) → - `cgp-macro-core` (this crate, all the logic)**. A `cgp-macro-lib` function is thin: `syn::parse2` - the attr/item, build a `cgp_macro_core::types::::Item*`, run its transform pipeline, and - wrap the result in `quote!`. See `cgp-macro-lib/src/cgp_component.rs` for the canonical shape. - The extra-feature macros follow the same split one level up, in `cgp-macro-extra-core`, which - depends on this crate and lowers through its AST types (see that crate's `AGENTS.md`), so a - change to a type it reuses, such as `ItemCgpProvider` or `DelegateTable`, reaches them too. -- Because it is `proc-macro`-free, its parsers and codegen are **unit-testable as plain functions**. - Tests live in the sibling `crates/tests/cgp-macro-tests` crate (parser corner cases like - `IdentWithTypeArgs`, plus expansion **snapshots** via the `snapshot_*` macros). Prefer adding - coverage there over ad-hoc verification. - -## Module map - -- **[src/types/](src/types/)** — the bulk of the crate. One submodule per user-facing macro - (`cgp_component`, `cgp_impl`, `cgp_provider`, `cgp_fn`, `cgp_getter`, `cgp_auto_getter`, - `cgp_type`, `cgp_data`, `delegate_component`, `check_components`, - `delegate_and_check_components`, `namespace`, `product`, `sum`, and `shape` for `Struct!`/`Enum!`), - plus shared building-block types: - `attributes/` (parsing of `#[uses]`, `#[use_type]`, `#[use_provider]`, `#[derive_delegate]`, - `#[default_impl]`), `generics/`, `field/`, `getter/`, `implicits/`, `ident/`, `path/`, `keyword`. -- **[src/functions/](src/functions/)** — free helper functions: identifier case conversion - (`camel_case`/`snake_case`), `parse_internal`, generics merging, delegated-impl synthesis, - field/getter/implicit-argument parsing, `strip`, the `Struct!`/`Enum!` body parsing and - validation in `shape/`, and `reject_non_empty_attributes`, shared by the macros that accept no - attributes. -- **[src/visitors/](src/visitors/)** — `syn` `VisitMut` passes that rewrite ASTs. `replace_self` - (receiver/type/value) is the heart of `#[cgp_impl]`: it rewrites `self`/`Self` into the explicit - `context`/`Context`. Also `replace_provider`, `remove_self_path`, `self_assoc_type`, and - `substitute_abstract_type` (the `#[use_type]` machinery). **Do AST rewriting with these visitors, - never with string manipulation.** -- **[src/exports.rs](src/exports.rs)** — see "hygiene" below. -- **[src/traits/](src/traits/)** — small internal traits (`ToType`, `IsKeyword`, bound helpers). -- **[src/macros/](src/macros/)** — internal `macro_rules!`: `parse_internal!`, `export_construct(s)!`, - `define_keyword!`. -- **[src/vendor.rs](src/vendor.rs)** — re-exports `quote::quote` so exported `macro_rules!` can use - it from downstream crates. - -## Conventions you must follow - -**1. Construct = a type implementing `Parse` and/or `ToTokens`.** Input is parsed by implementing -`syn::parse::Parse`; code is emitted by implementing `quote::ToTokens`. Add new syntax by writing a -type with these impls rather than hand-rolling token munging. - -**2. Build AST nodes with `parse_internal!`, not by hand.** `parse_internal!(#some_tokens ...)` -(in [src/functions/parse_internal.rs](src/functions/parse_internal.rs)) quasi-quotes tokens and -parses them into the target `syn` type, attaching a descriptive error (including the offending -tokens with the `::cgp::macro_prelude::` prefix stripped) on failure. It expands to a `?` -expression, so call it inside a `syn::Result`-returning function. - -**3. Reference CGP items through `exports.rs`, never by hardcoded path.** Each name in -[src/exports.rs](src/exports.rs) (`IsProviderFor`, `DelegateComponent`, `UseField`, `HasField`, …) -is a zero-sized marker struct whose `ToTokens` emits the fully-qualified path -`::cgp::macro_prelude::`. Generated code interpolates these markers (e.g. -`use crate::exports::IsProviderFor;` then `#is_provider_for`) so the expansion is hygienic and the -user only needs `cgp` in scope. **To emit a new CGP item, add it to `export_constructs!` in -`exports.rs` and interpolate the marker** — don't write `::cgp::...` path literals inline. - -**4. Codegen is a staged transform pipeline.** Each macro type moves through explicit stages rather -than emitting in one pass. The reference shape is `cgp_component`: -`ItemCgpComponent { args, item_trait }` → `.preprocess()` → `PreprocessedCgpComponent` → -`.eval()` → `Evaluated…` → `.to_items()` → `Vec` (driven by -`cgp-macro-lib/src/cgp_component.rs` as `item.preprocess()?.eval()?.to_items()?`). Other macros use -their own stage names — look for `item.rs`, `preprocessed.rs`/`lowered.rs`, `evaluated/`, and -`to_*` methods within each `types//` directory. When extending a macro, slot your change -into the existing stage rather than collapsing the pipeline. - -**5. Custom keywords go through `define_keyword!` + `IsKeyword`** (see -[src/macros/keyword.rs](src/macros/keyword.rs) and `types/keyword*.rs`). - -**6. Keep inline docs brief and current as you go.** When you review a file — whether to change it -or to write its [implementation document](https://github.com/contextgeneric/cgp-knowledge-base/blob/main/cgp/implementation/README.md) — improve the -inline docs in the same pass. Add a one-line `///` to any public struct, trait, or function that -lacks one, saying what it is or does (for a pipeline stage, its role in the sequence); prefer naming -the *why* or a corner case over restating the signature. Fix a doc that no longer matches the code, -and clarify genuinely confusing code (for example, why `generic_params_to_path` keeps only type -parameters) with a short comment. Keep them terse: delete a comment that only restates obvious code, -and do not narrate line by line. Deep mechanics belong in the implementation document, not in a wall -of inline prose — link to it rather than duplicating it. diff --git a/crates/macros/cgp-macro-extra-core/AGENTS.md b/crates/macros/cgp-macro-extra-core/AGENTS.md deleted file mode 100644 index 9258a2eb0..000000000 --- a/crates/macros/cgp-macro-extra-core/AGENTS.md +++ /dev/null @@ -1,51 +0,0 @@ -# AGENTS.md - -`cgp-macro-extra-core` is the pure-logic core of CGP's extra-feature macros: -`#[cgp_computer]`, `#[cgp_producer]`, and `#[cgp_auto_dispatch]`. It is to -`cgp-macro-extra-lib` what [`cgp-macro-core`](../cgp-macro-core) is to -`cgp-macro-lib`, and it depends on `cgp-macro-core` so it can reuse that crate's -AST types and helpers rather than duplicating them. **Read -[cgp-macro-core/AGENTS.md](../cgp-macro-core/AGENTS.md) first**: its conventions -(`Parse`/`ToTokens` construct types, `parse_internal!`, `exports` markers, staged -pipelines, `define_keyword!`, `VisitMut` visitors, and brief inline docs) all -apply here unchanged. This file adds only what is specific to this crate. - -## The pipeline is `crate → cgp-macro-core` - -The proc-macro pipeline is **`cgp-macro-extra` (entrypoints) → -`cgp-macro-extra-lib` (per-macro glue) → `cgp-macro-extra-core` (this crate) → -`cgp-macro-core`**. Like `cgp-macro-core`, this crate has no `#[proc_macro]` -entrypoints and does not depend on `proc-macro`, so its stages are unit-testable -as plain functions. - -## Lower through another macro's IR - -These macros build on CGP's core macros: a computer is a `#[cgp_new_provider]` -impl plus a `delegate_components!` table, and a dispatch trait adds a -`#[cgp_computer]` per method. Rather than emitting those invocations for the -compiler to expand again, each macro's `eval()` stage returns the **AST nodes -those macros parse into**, its intermediate representation (IR): -`cgp_macro_core::types::cgp_provider::ItemCgpProvider`, -`cgp_macro_core::types::delegate_component::DelegateTable`, or this crate's own -`ItemCgpComputer`. The entrypoint then lowers that IR with the owning type's own -method (`ItemCgpProvider::lower`, `DelegateTable::eval`), so the expansion is the -final code in one step and every CGP name in it is qualified through an `exports` -marker. `#[cgp_impl]` lowers through `ItemCgpProvider` the same way. Build each IR -node with `parse_internal!` from quoted tokens, as any other AST node. - -## Module map - -- **[src/types/](src/types/)**: one submodule per macro, each a staged pipeline - of `Item…` (raw input) → `.preprocess()` → `Preprocessed…` → `.eval()` → the - IR, plus `handler_fn`, the `EvaluatedHandlerFn` IR the computer and producer - macros share. -- **[src/exports.rs](src/exports.rs)**: the `exports` markers for the handler and - dispatch items the expansions name, declared with `cgp-macro-core`'s - `export_constructs!`. Reuse `cgp_macro_core::exports` for any item already - declared there, such as `HasExtractor`. -- **[src/functions/](src/functions/)**: free helpers the stages share, such as - the default provider and computer names (`derive_provider_ident`, - `derive_computer_ident`, which unraw a raw identifier) and `return_type`. -- **[src/visitors/](src/visitors/)**: the `syn` passes over types: - `ElaborateElidedLifetimes` (a `VisitMut` naming a dispatch method's elided - lifetimes by the elision rules), `collect_lifetimes`, and `find_impl_trait`. diff --git a/crates/tests/AGENTS.md b/crates/tests/AGENTS.md index e78e1a3eb..368024e5b 100644 --- a/crates/tests/AGENTS.md +++ b/crates/tests/AGENTS.md @@ -1,207 +1,24 @@ # AGENTS.md — maintaining the CGP test suite -This file governs the test crates under `crates/tests`. Read it before adding, -moving, or refactoring any test here. Invoke the `/cgp` skill first — every test -in this tree is CGP code, and the skill is the authoritative source for CGP -semantics and vocabulary. - -This repository's documentation lives in the sibling -[`cgp-knowledge-base`](https://github.com/contextgeneric/cgp-knowledge-base) repository, under its -`cgp/` directory, so a doc pointer below names a path there (`cgp-knowledge-base/cgp/…`) rather than a -local one. See [../../sibling-projects.md](../../sibling-projects.md) for finding that checkout. - -The test suite has two jobs, split across crates: - -- **`cgp-tests`** is the main suite: realistic example code that must **compile and - run**. A passing test is often just successful compilation, because much of CGP - is compile-time wiring. This is where behavior is verified and where the - user-facing macros are exercised end-to-end. -- **`cgp-macro-tests`** tests the **internals** of the CGP macros by calling the - functions in `cgp-macro-core` directly (parsers, AST types), and is the home for - **inputs a macro rejects** (via `assert_macro_rejects`) and for **pinning the exact - invalid tokens** a macro emits (`invalid_expansion` string snapshots). - -**Post-codegen compile failures are tested in `cargo-cgp`, not here.** The cases where a -macro *accepts* input but its *expansion* then fails to compile, and the cross-crate -coherence and orphan-rule fixtures, are in `cargo-cgp`'s UI test suite, which pins the -readable errors the tool renders for each class. `cargo-cgp` is CGP's first-class error -toolchain, so those diagnostics belong where the tool that improves them lives; see -"Adding a failure case" for the workflow. - -## Organize by concept, not by construct - -Group tests by the **CGP concept or feature** under test, never by the macro that -happens to appear. A single construct such as `delegate_components!` serves many -concepts — basic delegation, `open` dispatch, namespace headers, `UseDelegate` -tables — so a bucket named after the construct mixes unrelated concerns and hides -what is actually being verified. Name each group for the concept: `basic_delegation`, -`abstract_types`, `implicit_arguments`, `namespaces`, `higher_order_providers`, and -so on. - -The right granularity is driven by the feature, its implementation complexity, and -how many cases are needed to cover it exhaustively — **not** by mirroring the -concept documents under `cgp-knowledge-base/cgp/concepts/`. The names may coincide, but the split is -chosen for coverage. **When a category accumulates too many test cases to stay -coherent, split it into finer categories** rather than letting it sprawl; prefer -splitting early. - -## A test target is a "sub-crate" - -Each concept is one **integration test target**, which Cargo compiles as its own -crate — so each concept has its own coherence scope, exactly like a separate crate. -A target is two things: - -- an **entrypoint file** `tests/_tests.rs` — the `_tests` suffix marks it - as the target root; it carries a module doc comment, `#![allow(dead_code)]` when - the target is mostly compile-time wiring, and a single `pub mod ;`; -- a **module directory** `tests//` — the clean concept name — whose - `mod.rs` lists the unit-test modules, one `pub mod` per file. - -`basic_delegation` is the reference implementation of this layout — copy its shape -when adding a concept. - -## One unit test per file - -Put each unit test in its own `.rs` file under the concept directory, and make the -file **self-contained**: define its own components, providers, and context types at -module scope. Do **not** separate unrelated units with `#[test]` functions or nested -`mod`s inside one file. CGP tests are dominated by type-level constructs and -compile-time wiring that live at module scope and cannot be isolated by a function -boundary; separate files are the only reliable isolation within a target. A file may -still contain a `#[test]` fn for its runtime assertions, plus the module-scope items -that test exercises. - -## Explain what each test covers - -Open every test file with a brief comment stating **what behavior it exercises**, -and annotate individual tricky cases inline. Link to the owning **implementation -document** — the one under `cgp-knowledge-base/cgp/implementation/` whose Tests and Snapshots -sections index this test (for example `// see cgp-knowledge-base/cgp/implementation/entrypoints/cgp_impl.md`); -that document is where test pointers live, since a reference document never links -to a test (per the knowledge base's `cgp/AGENTS.md`). You may additionally link to a reference -document when a reader needs the user-facing semantics. Tests link **to** the -documentation; the reference documents never link back to a test. - -## Use macro snapshots sparingly - -`cgp-macro-test-util` provides `snapshot_*!` macros (`snapshot_cgp_component!`, -`snapshot_cgp_impl!`, `snapshot_delegate_components!`, …). Each **emits the real -generated code** into the module *and* generates a `#[test]` that asserts a -pretty-printed inline `insta` snapshot of it — so adding or removing a snapshot -never changes the compile/runtime coverage, only the golden assertion. Always keep -the snapshot string **inline** in the file (`@"…"`). - -The rule for when to snapshot: **snapshot a macro only in the concept target that -owns that macro's feature; everywhere else invoke the macro plainly.** Concretely, -each macro has one canonical full-expansion snapshot (plus snapshots for its -genuinely distinct variants) in its owning target, and nowhere else: - -| Macro | Owning target(s) | -| --- | --- | -| `#[cgp_component]` | `basic_delegation` (+ generic variant in `generic_components`) | -| `#[cgp_impl]` | `basic_delegation` (+ `higher_order_providers`, `implicit_arguments` variants) | -| `#[cgp_type]` | `abstract_types` | -| `#[cgp_getter]` / `#[cgp_auto_getter]` | `getters` | -| `#[cgp_fn]` | `implicit_arguments`, `impl_side_dependencies` | -| `delegate_components!` | `basic_delegation` (basic), `namespaces` (open/namespace), `dispatching` (`UseDelegate`) | -| `check_components!` / `delegate_and_check_components!` | `checking` | -| `cgp_namespace!` | `namespaces` | -| `#[blanket_trait]` | `blanket_traits` | -| `#[derive(HasField)]` / `HasFields` / `CgpData` | `field_access` / `extensible_records` / `extensible_variants` | -| `Struct!` / `Enum!` | `shape_macros` (type-equality tests only: each emits a single type, so neither has a snapshot) | -| `#[cgp_computer]` / `#[cgp_producer]` | `handlers` | -| `#[cgp_auto_dispatch]` | `auto_dispatch` | - -When a file uses one of these macros as **incidental scaffolding** — a -`#[cgp_component]` needed to set up a `delegate_components!` test, say — write the -plain macro, not the snapshot form. The expansion is already pinned in the owning -target, and a redundant snapshot only adds golden output that breaks on unrelated -macro changes. - -## Adding a failure case - -CGP will have corner cases it does not yet handle. Do **not** try to fix them inline -while refactoring; capture them as failing-behavior tests instead. The mechanism -depends on *where* the failure lands — whether the macro refuses the input, or -accepts it and emits Rust that then fails to compile. - -**Input a macro rejects — test the entrypoint in `cgp-macro-tests`.** When a macro -itself refuses the input by returning `Err` during expansion, assert it with the -`assert_macro_rejects` helper in `cgp-macro-tests` (see `parser_rejections`). This -drives the `cgp-macro-lib` entrypoint directly and checks the internal `Result`, -which is enough to pin a rejection and gives a precise check of the macro's own -diagnostic. This is the right tool for a structural error the macro is expected to -catch, and such a case does **not** also need a compile-fail test. - -**Input a macro accepts whose expansion fails to compile: a UI fixture in -`cargo-cgp`.** Reserve these for input a CGP macro *accepts* but whose *expansion* then -fails to type- or borrow-check, so the failure lands on the emitted Rust, not inside the -macro. These **live in another repository**: they are UI fixtures in `cargo-cgp`, -the tool that rewrites such errors into readable form, so the pinned snapshot is what a -user actually sees rather than the raw cascade. Add the fixture there, a standalone -`.rs` program with `fn main`, plus a `//@aux-build: ` directive when the case is -cross-crate, following -[cargo-cgp's UI-test guide](https://github.com/contextgeneric/cargo-cgp/blob/main/tests/README.md); -its harness records both `cargo-cgp`'s output (`.cgp.stderr`) and the raw compiler -baseline (`.rust.stderr`). cargo-cgp files a fixture under its **error class**, -mirroring the knowledge base's error catalog, and records the quality of its output in a -`//@quality:` header (`acceptable` when the tool leads with the root cause, `usability` -when the cause is present but buried, `hidden` when it is absent). Then catalog the class and link the -fixture (below). - -**Pinning the exact invalid output** is a separate, rarer need that *does* stay here: -only when you must *inspect* the wrong tokens a macro emits (not merely assert they fail -to compile), capture the expanded code as an `insta` inline string snapshot in the -`invalid_expansion` target of `cgp-macro-tests` (the snapshot is a *string*, so it -compiles even though the code would not), with a comment explaining **why** the output -is wrong and **what the correct output should be**. - -**Where a post-codegen class is documented.** Such a class is cataloged in the -[error catalog](https://github.com/contextgeneric/cgp-knowledge-base/blob/main/cgp/errors/README.md) under `cgp-knowledge-base/cgp/errors/`, the canonical -reader-facing documentation for these errors (see -[cgp-knowledge-base/cgp/errors/AGENTS.md](https://github.com/contextgeneric/cgp-knowledge-base/blob/main/cgp/errors/AGENTS.md)): the class doc describes the raw -diagnostic *and* how cargo-cgp presents it, and links the backing `cargo-cgp` UI fixture -as a GitHub URL. When a construct change alters such a diagnostic, the cross-project -[sync rule](../../AGENTS.md) applies: update the `cargo-cgp` fixture and the class doc -here together when both repos are checked out. A macro's **implementation document** -still records the *rejection* cases it catches and its behavioral tests (its -`## Known issues` and `## Tests` sections); the accept-then-fail classes are documented in the -error catalog, and a macro's `## Failure modes` section links out to the catalog class -and its `cargo-cgp` fixture rather than to a local fixture. - -## Keep the docs in sync - -This suite is one of the views of CGP's truth, alongside the macro implementation -in `cgp-macro-core`, the knowledge base's `cgp/reference` and `cgp/implementation` -documents, and the `/cgp` skill (see the knowledge base's `cgp/AGENTS.md`). The -implementation documents are the ones tightly coupled to this suite: each macro's implementation document has a `## Tests` section linking -every behavioral test and failure case that exercises it, and every entrypoint -document a `## Snapshots` section indexing the expansion snapshots and calling out -which variants are still missing. When a test reveals or pins a behavior worth -documenting, update the implementation document to explain that behavior directly — -and the reference document when the behavior is user-facing — without referring to -the test. When you add, move, or rename a test, update the implementation -document's Tests or Snapshots section in the same change. - -## Running the suite - -``` -cargo nextest run -p cgp-tests # the main suite -cargo nextest run -p cgp-macro-tests # macro internals + rejection/invalid-expansion cases -cargo nextest run --workspace # everything - -cargo insta test -p cgp-tests --review # review snapshot diffs -cargo insta test -p cgp-tests --accept # accept intended snapshot changes -``` - -A snapshot test that fails prints a diff of the generated code; accept it with -`cargo insta` only after confirming the change is intended. When a change touches a -macro's accepted input or its *expansion*, also check the post-codegen behavior in -`cargo-cgp`'s UI suite (its fixtures compile CGP code through the tool), per the -cross-project [sync rule](../../AGENTS.md). - -## Growing the suite - -As categories grow, keep splitting them per the rule above, and keep expanding rejection coverage in -`cgp-macro-tests`. Post-codegen compile-fail coverage and cross-crate coverage grow in `cargo-cgp`'s UI -suite rather than here. +The root [cgp/AGENTS.md](../../AGENTS.md) rules apply. Invoke the `/cgp` skill before adding, moving, +or refactoring a test. Read [README.md](README.md) for the target layout and the commands. + +## Rules + +- Group tests by the CGP concept under test, never by the macro that happens to appear. +- Split a category into finer ones when it grows too large to stay coherent. +- Put each unit test in its own file under the concept directory, self-contained at module scope. +- Open every test file with a brief comment on the behavior it exercises, and link the owning + implementation document. +- Snapshot a macro only in the concept target that owns its feature; keep the snapshot inline. +- Invoke a macro plainly when it is incidental scaffolding; the owning target already pins its + expansion. +- Put an input the macro rejects in `cgp-macro-tests` with `assert_macro_rejects`. +- Put an accepted input whose expansion fails to compile in a `cargo-cgp` UI fixture, filed under its + error class, and catalog the class in the knowledge base's error catalog. +- Put a pin on the exact invalid tokens in the `invalid_expansion` target of `cgp-macro-tests`. +- Update the implementation document's Tests or Snapshots section in the same change as a test move. +- Run the suite with the commands in [README.md](README.md), and review every `insta` diff before + accepting it. +- Check the post-codegen behavior in `cargo-cgp`'s UI suite when a construct change alters an accepted + input or an expansion.