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