ADR 0065 — Agent-facing reference: the library API in llms-full.txt, ceesafe_api, and "did you mean"
- Status: Accepted by the owner, 2026-11-04 ("Go ahead implement 1-4"), after the review of the agent-facing documentation that followed the ClayWind port. Implemented on the working tree; not released.
- Scope: documentation (
spec/api/std.md, new;guide/builtins.md, new;guide/for-agents.md;templates/AGENTS.md; the site'sllms.txtandllms-full.txt), the MCP server (ceesafe_api), and diagnostics (N0100, N0200, N0230, N0234 messages). No language change.
1. Context
An agent porting ClayWind (~25k lines) to CeeSafe missed names that existed, and guessed names from other languages that did not:
- it looked for
TextWriter.unsigned, butTextWriter.decimal(u64)existed (spec/api/core.md); - it wrote
Environment.args()forProcess.argument(i), andTimer.now().since(…)forTimer.now(), au64; - it learned the async socket API from another program's source.
The causes were in what an agent reads:
llms-full.txt, the one fileAGENTS.mdtells an agent to load, had the guide and the specification but not the library reference;- lib/std had no API page at all;
- the compiler's own types (
Console,File,Process,Vector,StringView, the integer methods) are described only in specification prose; - the MCP server had no lookup by name (
ceesafe_symbolis a hover at a position); - "has no method named 'unsigned'" named nothing that does exist.
2. Decision
- The reference, complete and in one file.
spec/api/std.mdisceesafec doc lib/std --title "lib/std", andtools/spec_check.pychecks it as it checkscore.md.guide/builtins.mdtabulates the built-in types and functions. Each section ends with a run example, and a final type-checked block calls every row with the type the tables give, soguide_checkfails when a table drifts from the compiler. Writing that block found two wrong rows:checkedTruncateandsaturatingTruncatetake only floats.llms-full.txtnow holds the guide (with the new page), the specification, and both API pages.llms.txtlinks lib/std, and tells agents to look a name up before concluding it is missing.
ceesafe_api, the MCP server's tenth tool (src/tooling/api_lookup.cee, tooling.md §6.3).- Input: a type (
TextWriter), a member (TextWriter.decimal) or a bare member name (decimal). - Results: the matching entries of the two API pages and the built-in types page: a type's section, a member's entry, or the table rows that name it.
- When nothing matches, up to eight suggestions:
- the closest names by edit distance;
- for
Type.member, the type's real members; - the entries whose documentation mentions the member's name (
unsignedfindsTextWriter.decimal).
tools/mcp_api_check.py, a new contract, holds 15 lookups against names the documents state.
- Input: a type (
- "Did you mean" (
src/check/suggest.cee, diagnostics.md).- Unknown member (N0200, N0230): a struct's close members, then those whose doc comment mentions the name, at most three; a small type with no close member lists all its members.
- Built-in types: ranked by the longest shared start, then edits, from the same lookup as
ceesafe_api, so the error and the tool agree. - Numbers: an unknown method on a number is "has no method 'm'", with its close methods. Before, it was "has no methods", which became false with ADR 0063.
- Reserved words: N0100 says when the token is a reserved word.
- The agent guide and template.
- New sections in
guide/for-agents.md: looking names up, reserved words that read like names, optionals against results,ConsoleagainstAsyncConsole, andmutat a call againstborrow mutelsewhere. Each section has a checked example with the compiler's exact output. templates/AGENTS.md: four new lines on the same.
- New sections in
3. Evidence (macOS arm64)
- Checks:
guide_checkpasses 117 examples,spec_checkreports 0 problems (includingstd.md), andmcp_api_checkreports 0 problems over 15 lookups. The corpus passes 596 tests with 0 failures; no expectation needed a change. - Messages the port actually hit, before and after:
w.unsigned(42)gives "; did you mean 'signed', 'decimal' or 'hex'?";Process.args(1)gives "'argument', 'argumentCount' or 'run'";Console.writeLngives "'writeLine', 'write' or 'writeBytes'";Environment.args()gives "it has 'variable'";n.mx(1)gives "'max'".
4. Consequences
- The built-in types page is written by hand, but it can't drift silently: its final block is checked against the compiler.
- Suggestions only append to existing messages. The codes are unchanged, and tools that match on codes are unaffected.
ceesafe_apireads the documents embedded inceesafec, so it answers for the compiler that serves it, asceesafe_spec_searchdoes.