From 8e9d324678edbbeb4bc88744761e15dc6b68d920 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Gro=C3=9F?= Date: Thu, 29 Oct 2026 19:57:30 +0100 Subject: [PATCH] docs: AGENTS.md keeps what an agent needs before it acts, and points to the rest AGENTS.md loads into every agent session. Five sections repeated what another file holds or detailed what an agent needs only at one moment: - Finding code: the graph's measured gaps move to design/code-graph.md (new, linked from design/plan.md); AGENTS.md keeps the rule and the gaps in one line. - Commands: the BuildVerdict, BashGuard and session-start paragraph shrinks to what the verdict prints and what the guard refuses. - Issues: the label legend, epics and PR checks are CONTRIBUTING's; what stays is what trips agents here. - Workflow step 7: the oracle-kit mechanics are test/oracles/README.md's. - Releasing: CONTRIBUTING's "Releasing" holds the steps; the seeds, the arm64 image and the never-delete-a-seed rule stay. "Rules that are easy to break" is unchanged, and so is every heading another document cites. 22,230 to 17,956 characters, about 1.1k tokens less per session start. Conventions tests pass (2,002) and docs-query check is clean. Refs #167 Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 147 +++++++++++++------------------------------ design/code-graph.md | 35 +++++++++++ design/plan.md | 1 + 3 files changed, 80 insertions(+), 103 deletions(-) create mode 100644 design/code-graph.md diff --git a/AGENTS.md b/AGENTS.md index 4ce25db..ae2b6c1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -66,33 +66,23 @@ dotnet tools/PrHygiene/bin/Release/net10.0/pr-hygiene.dll event.json # a PR's dotnet tools/CiLanes/bin/Release/net10.0/ci-lanes.dll classify ... # the CI lanes a changed path selects (Linux, Windows, the pack; macOS only on the release's runs), as CI's changes job decides them ``` -The oracle kit's local tool, oracles (`tools/Oracles`, started as its built dll like docs-query), which needs no -model and never runs in CI: `test/CodoMetis.TypeKit.Oracles.Tests` holds the catalogue there without building -anything else ([decision 112](design/adr/0112-the-prober-gives-way-to-valueranges-oracle-kit.md), -[test/oracles/README.md](test/oracles/README.md)): +The oracle kit's local tool, which needs no model and never runs in CI +([test/oracles/README.md](test/oracles/README.md)): ```bash dotnet tools/Oracles/bin/Release/net10.0/oracles.dll status # the worklist: unheld claims, unread waivers, unseeded oracles, candidates dotnet tools/Oracles/bin/Release/net10.0/oracles.dll seeds [filter] # each seed: a copy of the tree, the patch, one project built, one class run ``` -An agent runs builds and tests through `tools/BuildVerdict`, run as its built dll (`build -[]` or `test`). It prints the exit code named, each diagnostic code once with its remedy -from `tools/BuildVerdict/build-verdict-remedies.tsv`, and the failing tests with their assertion; the -full log is `$GIT_DIR/build-verdict-last.log`. A code it prints as UNMAPPED gets a row once its fix is -known. In an agent's Bash tool, `tools/BashGuard` (registered in `.claude/settings.json`) refuses -`dotnet build` of anything but a tool, `dotnet test` and `dotnet msbuild` (a `-getProperty`/`-getItem` query -without a target excepted), and the verdict piped into -`head` or `tail`, and prints the verdict's form of the command. The raw commands above are for a -terminal. - -Both are tool projects under `tools/` (design/adr/0092): BCL only, in the solution, each with its -tests (`tools/BuildVerdict.Tests`, `tools/BashGuard.Tests`), and run from `bin/Release`, which -`.claude/settings.json` builds at session start: the guard in the main checkout, every other tool -(`tools/AgentTools.slnf`) in the session's own, a worktree included, with the SDK installer's `dotnet` -when the one on PATH lacks the pinned SDK; a session start that builds nothing says so in your context -(design/adr/0105). Each records the sources it was built from and refuses to run from others, naming -the command that rebuilds it: `dotnet build tools/ -c Release`, which the guard lets through. +An agent builds and tests through `tools/BuildVerdict` (`build []` or `test`). It prints a +verdict: the exit code named, each diagnostic once with its remedy from +`tools/BuildVerdict/build-verdict-remedies.tsv` (give an UNMAPPED code a row once its fix is known), and the +failing tests; the full log is `$GIT_DIR/build-verdict-last.log`. In an agent's Bash tool, `tools/BashGuard` +refuses the raw `dotnet build`, `dotnet test` and `dotnet msbuild` and prints the verdict's form of the command; +the raw commands above are for a terminal. The tools run from `bin/Release`, built at session start in the +session's own checkout ([decision 105](design/adr/0105-every-tool-is-built-in-the-session-s-checkout.md)). +A tool built from other sources refuses to run and names the command that rebuilds it, +`dotnet build tools/ -c Release`. The SDK is pinned in `global.json` (10.0.4xx band, `latestPatch`). Tests run on Microsoft Testing Platform (xunit.v3 4.x), so `dotnet test` takes `--solution`/`--project`, and TRX output is @@ -115,33 +105,16 @@ here, so it leads and the source decides. before researching what ASP.NET, System.Text.Json, EF Core, the runtime or the platform do, and a new `design/knowledge/` entry when research settles one that something here turned on ([decision 93](design/adr/0093-verified-facts-are-pinned-dated-and-executable-where-they-can-be.md)). -- **An empty result proves nothing.** Fall back to text search when the graph is unavailable, stale - or silent, and never conclude "unused", "no other caller" or "safe to change" from it alone. - Measured on 2026-10-13, the graph: - - finds types, interfaces, enums and ordinary methods, a type's implementers through generic - interfaces (84 of the Samples' 88 value objects), and the callers of an ordinary method across - files; - - has no node for a member of a C# 14 `extension(...)` block, which holds most of the `Option` and - `Result` extensions (`Choose`, `FirstOrNone`, `Transpose`, `Sequence`, the continuations of a - `Task>`) and `AsText`, and puts the calls inside one on the file; - - has nothing Metalama generates: none of the tests' 222 `.From(` calls has an edge, nor has a call - to `TryFrom`, `FromKnownGood`, `Parse` or a converter, and a template an aspect introduces by - `nameof` looks unreferenced. Read woven code as the Generators README's "Reading the generated - code" says; - - misses a reference that is not a call: a DI registration, a type argument, `typeof`, an attribute; - - resolves a call on a generic instance poorly (`Option.Match` has one resolved caller, where - the repository writes `.Match(` about 200 times across its types) and guesses wrong callers: a low-confidence `suffix_match` edge, or a same-named method of another API - (Metalama's `Diagnostics.Report` listed as calling `GeneratedCode.Report`). Overloads share one - node. -- **Refresh it**, since it does not follow the working tree: `index_repository` with the repository - root as `repo_path` and `name: "CodoMetis.TypeKit"`, at the start of a session and after a branch - switch, a rebase, a merge or a pull (about 6 s). Without `name` it starts a second project under a - derived name. `index_status` reports the checkout's HEAD, not the commit the graph was built from, - so refresh rather than check. -- It indexes `spikes/` and leaves out `.claude/`, `bin` and `obj`, so an agent in a worktree under - `.claude/worktrees/` reads the main checkout's graph, not its own: index the worktree under a name - of its own and delete that project when done, or search the source. `.codebase-memory/`, the - tool's local artifact, is ignored by git. +- **An empty result proves nothing.** The graph has no node for a member of a C# 14 `extension(...)` + block (most of the `Option` and `Result` extensions), nothing Metalama generates (no `.From(` or + `TryFrom` call has an edge), and no reference that is not a call; it resolves calls on a generic + instance poorly and guesses wrong callers ([design/code-graph.md](design/code-graph.md) has the + measurements). Fall back to text search, and never conclude "unused", "no other caller" or "safe to + change" from it alone. +- **Refresh it**, since it does not follow the working tree: `index_repository` with the repository root + as `repo_path` and `name: "CodoMetis.TypeKit"`, at the start of a session and after a branch switch, a + rebase, a merge or a pull. A worktree under `.claude/worktrees/` isn't indexed: index it under a name of + its own and delete that project when done, or search the source. The way into the docs is docs-query (`dotnet tools/DocsQuery/bin/Release/net10.0/docs-query.dll`, [its README](tools/DocsQuery/README.md)): `entry`, `cites`, `cited-by`, `list`, `claims` and `check` @@ -207,24 +180,16 @@ the whole page. A ranked miss proves nothing. ## Issues Work is tracked as GitHub issues, not as lists in docs or memory -([decision 109](design/adr/0109-work-is-tracked-as-labelled-issues-and-ci-checks-each-pull-request.md)). -Open the issue before starting the work: a bug, an enhancement, a follow-up, a finding from a review or a -probe. -- **Labels, on issues and PRs alike** (ValueRanges' scheme; the legend is CONTRIBUTING's "Issues and - labels", and `gh label list` the source): exactly one type (`bug`, `enhancement`, `documentation`, - `tests`, `chore`), exactly one `severity/*` on a bug, the `package/*` and `area/*` it touches, and - `silent` (a bug that does less than it appears to), `breaking`, `found-by/prober` or `stacked` where they - apply. Never invent a label; if none fits, say so. -- **Epics:** a release is an `epic` issue with a milestone of its version (`2.3.0`), its work attached as - native sub-issues with the same milestone (`gh issue create --parent N --milestone M`). A theme that spans - releases is an `epic` without a milestone; a finding belongs to the release it ships in. The plan stays in - `design/roadmap.md`; there is no roadmap issue. -- **PRs** carry their issue's labels and milestone, open with `Closes #n`, and follow - `.github/pull_request_template.md` (Problem, Change, Tests). `pr-hygiene.yml` runs `tools/PrHygiene` on - every PR into `main` or a release branch and fails a missing type, severity or closing reference, a branch - whose `/` disagrees with its type label, a PR into `release/` without that milestone, and - `breaking` into anything but a major. A topic merges into its release branch, where `Closes` closes - nothing: close its issue by hand on merge, with a comment linking the PR. +([decision 109](design/adr/0109-work-is-tracked-as-labelled-issues-and-ci-checks-each-pull-request.md)): +open the issue before starting the work. The labels, the epics and what a PR carries are CONTRIBUTING's +"Issues and labels" and "Pull requests"; `gh label list` is the source, and a label is never invented. +What trips agents here: +- A release's work is a native sub-issue of its `epic`, with the version's milestone: + `gh issue create --parent N --milestone M`. A finding belongs to the release it ships in. +- Name the branch from the type label before the first push (`feat/`, `fix/`, `docs/`, `test/`, `build/`, + never `chore/`): PR hygiene fails a `/` that disagrees, and renaming a pushed head closes its PR. +- A topic merges into its release branch, where `Closes` closes nothing: close its issue by hand on merge, + with a comment linking the PR. - When the work changes what an issue says (its scope, its state, a measured fact), edit the issue. ## Workflow @@ -248,43 +213,19 @@ probe. tables of [decision 94](design/adr/0094-topic-branches-into-a-release-branch-and-fix-trailers.md) (`CommitTrailerTests`). Work happens on a `/` branch from `release/`, reaches it by a PR, and every merge is a merge commit: never squash or rebase a pushed branch. -7. **Hold what the documents promise.** `test/oracles/` is the catalogue - ([decision 112](design/adr/0112-the-prober-gives-way-to-valueranges-oracle-kit.md)): each sentence a consumer - document promises is a claim in `claims.json`, quoted verbatim and naming the test classes that hold it, and - every public member of the run-time libraries is claimed or waived in `surface-waivers.json`. - - A new public member, or a new promise in `docs/` or a README, gets its claim, with the test that holds it, - or a waiver that says why it needs none. `OracleSurfaceCoverageTests` fails on a member neither names, and - `OracleCatalogueTests` on a quote its document no longer holds: reword the claim with the document. - - An oracle is an ordinary test class that checks the library against an independent answer over many cases: - a differential, a sweep over a variant catalogue, a completeness test by reflection. Register it in - `oracles.json` with a seed, a defect in `src/` kept as `seeds/.patch` (`git diff` of the edit, then - undo it by the inverse edit), and show it caught with `oracles seeds `. - - A disagreement an oracle finds and nobody fixes yet goes to `candidates.json` with its issue, never into an - assertion that hides it. - - `axes.md` lists what a consumer varies and the pairs no test varies yet; a combination a finding names - joins it. +7. **Hold what the documents promise** ([decision 112](design/adr/0112-the-prober-gives-way-to-valueranges-oracle-kit.md), + [test/oracles/README.md](test/oracles/README.md) for the files and how to add an oracle). A new public member, + or a new promise in `docs/` or a README, gets a claim in `test/oracles/claims.json`, quoted verbatim and naming + the test that holds it, or a waiver in `surface-waivers.json` that says why it needs none; a claim is reworded + with its document. A new oracle is registered in `oracles.json` with a seed, shown caught by + `oracles seeds `. A disagreement nobody fixes yet goes to `candidates.json` with its issue, never into + an assertion that hides it, and a combination a finding names joins `axes.md`. ## Releasing -Before `git tag`, show every seed caught, locally: a baseline with no seed, then a build of one project per seed. -A seed counts only where its oracle passes without it and fails with it in a test of its own; a fixture that never -started, a crashed host or a failing baseline is `ERROR`, exit 4 (decision 117). On an arm64 Mac set -`TYPEKIT_MSSQL_IMAGE=mcr.microsoft.com/azure-sql-edge:latest`, or the SQL Server oracles are `ERROR`: - -```bash -dotnet tools/Oracles/bin/Release/net10.0/oracles.dll seeds -``` - -Then re-read the competitor facts that are due, and correct `docs/comparison.md` with them (CONTRIBUTING, -"Releasing", step 1): - -```bash -dotnet tools/DocsQuery/bin/Release/net10.0/docs-query.dll due --nuget # external facts over six months old, or behind NuGet -``` - -It reads api.nuget.org, needs no model, and is never a CI check: time passing fails no build. - -The release workflow runs the suite, which holds the catalogue, before it packs; there is no gate beside it -(decision 112 withdrew decision 97's). A seed that is not caught is answered by a stronger oracle or a better -seed, never by deleting the seed, and an `ERROR` by fixing the environment or the baseline, never by reading it as -caught. +Follow CONTRIBUTING's "Releasing". Its step 1 runs locally, before `git tag`, and is the one easy to skip: +`oracles seeds` shows every seed caught, and `docs-query due --nuget` lists the competitor facts to re-read. On an +arm64 Mac set `TYPEKIT_MSSQL_IMAGE=mcr.microsoft.com/azure-sql-edge:latest`, or the SQL Server oracles are `ERROR` +([decision 117](design/adr/0117-a-seed-is-caught-against-a-passing-baseline.md)). A seed that is not caught is +answered by a stronger oracle or a better seed, never by deleting it, and an `ERROR` by fixing the environment or +the baseline, never by reading it as caught. diff --git a/design/code-graph.md b/design/code-graph.md new file mode 100644 index 0000000..0928df8 --- /dev/null +++ b/design/code-graph.md @@ -0,0 +1,35 @@ +# The code graph + +The codebase-memory MCP server keeps a graph of this repository, the project `CodoMetis.TypeKit`. +[AGENTS.md](../AGENTS.md), "Finding code", says how to use it: the graph leads and the source decides. +This note holds what that rests on: what the graph was measured to find and miss. + +## What it finds and misses + +Measured on 2026-10-13, the graph: + +- finds types, interfaces, enums and ordinary methods, a type's implementers through generic interfaces + (84 of the Samples' 88 value objects), and the callers of an ordinary method across files; +- has no node for a member of a C# 14 `extension(...)` block, which holds most of the `Option` and `Result` + extensions (`Choose`, `FirstOrNone`, `Transpose`, `Sequence`, the continuations of a `Task>`) + and `AsText`, and puts the calls inside one on the file; +- has nothing Metalama generates: none of the tests' 222 `.From(` calls has an edge, nor has a call to + `TryFrom`, `FromKnownGood`, `Parse` or a converter, and a template an aspect introduces by `nameof` looks + unreferenced. Read woven code as the Generators README's "Reading the generated code" says; +- misses a reference that is not a call: a DI registration, a type argument, `typeof`, an attribute; +- resolves a call on a generic instance poorly (`Option.Match` has one resolved caller, where the + repository writes `.Match(` about 200 times across its types) and guesses wrong callers: a low-confidence + `suffix_match` edge, or a same-named method of another API (Metalama's `Diagnostics.Report` listed as + calling `GeneratedCode.Report`). Overloads share one node. + +## Keeping it current + +The graph does not follow the working tree. `index_repository` with the repository root as `repo_path` and +`name: "CodoMetis.TypeKit"` refreshes it in about 6 s; without `name` it starts a second project under a +derived name. `index_status` reports the checkout's HEAD, not the commit the graph was built from, so +refresh rather than check. + +It indexes `spikes/` and leaves out `.claude/`, `bin` and `obj`. So an agent in a worktree under +`.claude/worktrees/` reads the main checkout's graph, not its own: it indexes the worktree under a name of +its own and deletes that project when done, or searches the source. `.codebase-memory/`, the tool's local +artifact, is ignored by git. diff --git a/design/plan.md b/design/plan.md index 82b29f2..9a76a93 100644 --- a/design/plan.md +++ b/design/plan.md @@ -24,6 +24,7 @@ you touch; the rest is reference. | [Mutation survivors, 2026-10-16](mutation-2026-10-16.md) | The first Stryker.NET run, on the Serilog satellite: counts per package, each survivor read as a missing assertion or an equivalent mutant, and the valid mutants the compile lost | one dated page per run | | [Finder benchmark, 2026-10-16](finder-benchmark-2026-10-16.md) | The 24 release escapes put back as seeds: which test, probe row or nothing detects each, totals per finder as applied, reached and detected, and the accounting every probe round and analyst run reports | one dated page per run | | [Oracles](../test/oracles/README.md) | What the documents promise as claims held by named tests, every public member claimed or waived, the oracle classes and their seeds, the candidates and the axes a consumer varies ([decision 112](adr/0112-the-prober-gives-way-to-valueranges-oracle-kit.md)) | with each feature and each promise a document gains | +| [The code graph](code-graph.md) | What the codebase-memory graph was measured to find and miss, and how to keep it current; AGENTS.md's "Finding code" rests on it | when the graph is measured again | | [Probing without the tooling](probing-without-tooling.md) | How BLite applied the probing concept by hand, with no prober, covering array or ledger: what each tool function became, the process as it ran, the issues and PRs it produced, and an adoption guide for trying the same here without the tooling | copied 2026-10-18; as the method is tried here | The spikes that decided something are in [spikes/](../spikes/), each with a README stating the