v0.0.277
v0.0.277 (2026-08-26)
MCP-inside-rlm + learnt slim is now on the full harness, and the licensed
one-read-surface / turn pulse / Smolify opt-in that was already on
release/v0.0.277 is in the cut. graff still writes a code REPL
(ADR 0022); this release is what that loop does with MCP, with X, and
with a licensed codedb-pro seat.
The short version: fat MCP results slim themselves, grok-4.6 can search X
without a scrape, small turns do not pay for rlm, and a licensed
codedb-pro install is one read surface and one edit surface — no default
16-call cap, no Smolify unless you ask.
MCP-inside-rlm and learnt slim
Loaded MCP names are rlm host functions (src/rlm_mcp.zig). After
load_tool_schemas unfolds a tool, a script may call that name;
unloaded names are refused, and deferral can only subtract.
GRAFF_RLM_MCP=0 restores the structured-only gap.
After each MCP result, graff infers a value-free return shape (keys +
broad types, never values), persists .graff/mcp-shapes.json, and splices
it onto the next load_tool_schemas result. Never the always-on
prefix (ADR 0011).
The learnt part is applied in Zig, not as another prompt hint:
- Fat JSON arrays auto-slim after
remember(): identity keys on
issue-like rows; comment arrays fold to{n, latest_author}. - Two stored shapes add a one-line
# muscle:playbook on the load
result. No newGRAFF_knob. each/len/projectexist so a script can print a slim summary.
They are not advertised as the recipe — aneach()hint is a footgun
on grok-4.6 (it inventsfor/len/importor dumps the fat bind).
A later run in the same repo is L: shapes already on disk, muscle on
the load result. N is the same slim on a cold cache. Both dominate
the pre-slim H/I/J front. This is not Blacksmith's V8 sandbox and not an
rlm-only catalog — those already failed (ADR 0023 / 0024).
See ADR 0029.
Performance
All MCP numbers are live grok-4.6 SuperGrok OAuth, one rep, ReleaseSafe,
--no-lean (graff-dev-nolean). SuperGrok is flat-rate ($0.0000 both
ways). Token/call cuts are the spend win on a metered XAI_API_KEY.
Rerun: python3 scripts/eval-mcp-shapes.py / eval-mcp-pareto.py.
Learnt slim vs pre-slim (Linear fixture, no each() hint)
| wall | in | calls | |
|---|---|---|---|
| L warm + slim | 14.8s | 31k | 5 |
| N cold + slim | 20.8s | 30k | 5 |
| H (pre-slim, same harness) | 28.0s | 112k | 7 |
| I (pre-slim, warm shapes unused) | 31.1s | 107k | 7 |
L is N after muscle memory has been written. Both dominate H/I/J (J was
47s / 84k / 9). Variant P (reduce recipe + slim, 48s / 66k / 9) is
dominated by L/N. Lean default -p (variant R, 16.3s / 14k / 4) is a
different catalog and is not on this front — the MCP bench stays
--no-lean.
What did not ship as the default path
The first A/B (2026-08-25) showed MCP-inside-rlm + shape cache can cut
tokens when the model stays in each() (D vs --old: 167k → 107k in).
A same-day rerun did not hold (D-r2 159k / 61s), and an explicit each()
hint plus "never print fat arrays" blew out to 220s / 462k / 29 calls.
No-hint structured MCP (H/I) was the stable cheap path until slim; slim
then made L/N the front. Do not copy the each() recipe onto the prefix.
Default -p connects MCP
Lean used to skip MCP connect and hide load_tool_schemas even
when a workspace .mcp.json was present. That forced --no-lean for
every MCP one-shot (ADR 0024 leftover).
Default -p still implies lean + yolo, but it now connects a
workspace .mcp.json and folds (names + one-liners, full schemas a
load away). Empty -p (no deferred MCP) still hides the meta tool.
Consent is unchanged. --no-lean is the eager-schema opt-out, not the
MCP on-switch.
Licensed codedb-pro: one read surface, one edit surface
When codedb-pro is licensed, the catalog is one read/search surface and
one edit surface (#626,
#627):
- Native
read_fileand legacycodedbare hidden. codedb-pro
read/faster_search/meta_searchstay. - Companion write tools (
mcp__codedbpro__/mcp__muonry__
edit/patch/create/replace/byte_delta) are omitted — they
bypass/rewind. The exec gate still refuses them if they leak through. - Native
edit_file/write_filestay.subagentstays. webfetch is
not hidden here. bashwhose first token is a search (grep/rg/find/cat/head/
tail/sed/awk/…) is refused; use the licensed search tools.- Workers inherit the same catalog. A subagent gets codedb-pro reads,
not nativeread_file, and never the companion writes. Hidingsubagent
from the REPL was the other half of #627 and is not done.
Without a license the native tools stay. The hide is catalog + exec, not
a prompt essay.
Long turns pulse; no 16-call cap
A local trajectory corpus (129 real turns) had p50 = 5 model calls. A
default cap of 16 would have paused 174 successful turns. So the inner
loop is unlimited (max_turn_model_calls = 0).
What you get instead (#624,
#625, ADR 0020 chrome):
- From the second model call of a turn:
· turn still going · model call 3 · 11 tools - On transport flakes:
· retry 3/6 · HungRequest(alsoUnknownHostName) - JSON mode and subagent turns stay quiet (chrome is not output).
GRAFF_MAX_TURN_MODEL_CALLS=16 is the opt-in cap, with a denominator on
the pulse (model call 3/16). It is not the default.
Optional MCP stays off until asked
Smolify, DeepWiki, and Mobbin were the high-fail unauthenticated servers
in the same corpus. They stay out of the default catalog
(#628):
| want | set |
|---|---|
| Smolify | GRAFF_SMOLIFY=1 (or GRAFF_SMOLIFY_ACCESS=public / full) |
| never Smolify | GRAFF_NO_SMOLIFY=1 |
| DeepWiki | GRAFF_DEEPWIKI=1 |
| Mobbin | GRAFF_MOBBIN=1 |
| either/both | GRAFF_MCP_OPTIONAL=deepwiki,mobbin |
A workspace .mcp.json you actually configured still connects (folded on
-p). The skip is the bundled unauthenticated extras.
Hosted x_search (xAI Responses)
grok-4.6 already runs {"type":"x_search"} server-side. This cut splices
that hosted tool onto xAI Responses tools turns
(ADR 0031).
It is not a catalog function, not on the prefix, and not dispatched by
graff — x_search_call / custom_tool_call names x_search,
x_keyword_search, x_semantic_search, x_user_search, x_thread_fetch
are already done. Tools-off turns stay tools-off. Chat completions
(GRAFF_XAI_WIRE=chat) and Codex never get it.
GRAFF_XAI_X_SEARCH=0 (or off/false/no) opts out.
Same SuperGrok prompt (“what are people saying about xAI on X?”):
scrape (GRAFF_XAI_X_SEARCH=0) |
hosted x_search |
|
|---|---|---|
| wall | 101s | 37s |
| calls | 7 | 1 |
| input tokens | 64k | 28k |
Citation annotations ride the message; v1 does not pretty-print them.
Revisit the splice if the extra hosted object shows up as cache noise on
a coding eval.
Late + context showcase
Small turns hide rlm (and sPTC). L/N never call rlm; advertising the
spec on every turn still costs prefix bytes and invites the each()
dialect. Zhang / Li / Khattab argue RLM's job is length generalization,
not replacing structured tools on short in-distribution turns.
Showcase (markLoaded, schema on the tools tail, never the head) when
(ADR 0030):
--rlm/GRAFF_RLM=1— immediate;- a structured batch of ≥4 native host tools (
read_file/codedb/
bash/webfetch) — the scatter-gather that sPTC overlaps; effectiveContextTokens() ≥ 50%ofprovider.compactAt()— the existing
overflow meter, not a second counter;- explicit
load_tool_schemas tools=["rlm"].
MCP-only fan-out and first slim do not showcase. /new / /clear
hide a session-discovered showcase; --rlm sticks. --old resets
discovery. Auto-load on a confident rlm call still works (gateExec).
GRAFF_RLM_CONTEXT overrides the size gate: unset = 50% of compactAt;
0/off disables; 1–100 or 50% is a percent; 32k / 32768 is
an absolute floor (Zhang's RL curriculum, opt-in — not the default).
The hide is the token win on short work. Showcase itself costs ~200
input tokens (schema on the tail) until the model actually scripts.
Flags
| knob | default | what it does |
|---|---|---|
--no-lean |
off on -p |
eager MCP schemas; not the MCP on-switch |
--rlm / GRAFF_RLM=1 |
late showcase | force rlm on now |
--old / --no-rlm |
off | structured-only catalog |
GRAFF_RLM_MCP=0 |
on | MCP names are not rlm host functions |
GRAFF_RLM_CONTEXT |
50% of compactAt | size gate for late showcase |
GRAFF_XAI_X_SEARCH=0 |
on (xAI Responses) | no hosted x_search splice |
GRAFF_MAX_TURN_MODEL_CALLS |
0 (unlimited) |
inner-loop pause cap |
GRAFF_SMOLIFY / _ACCESS |
off | connect Smolify |
GRAFF_NO_SMOLIFY |
— | never connect Smolify |
GRAFF_DEEPWIKI / GRAFF_MOBBIN |
off | bundled extras |
GRAFF_MCP_OPTIONAL |
empty | comma list of those extras |
Last CLI flag still wins over env for --old / --rlm (ADR 0022).
What we did not take
- No V8 Code Mode / IPython / QuickJS.
rlmis already the analog
(ADR 0023). MCP-inside-rlm is host functions + shapes, not a sandbox. - No rlm-only catalog. File/shell tools stay; hiding them tanks pass
rate (ADR 0024). - No
each()recipe on the prefix. It is a grok-4.6 footgun. - No default 16-call turn cap. Pulse instead (#624).
- No hiding
subagent. Workers keep codedb-pro reads (#627). - No Smolify / DeepWiki / Mobbin in the default catalog (#628).
- No
x_searchcatalog tool. Hosted splice only (ADR 0031). - No showcasing
rlmon MCP fan-out or first slim (ADR 0030). - Experiment worktree pool / default fan-out is #629 and is not in this cut.
Also in this cut
- ADR 0029 MCP-inside-rlm + return shapes + learnt slim.
- ADR 0030 hide
rlmon small turns; showcase later. - ADR 0031 xAI Responses hosts
x_search. - #624 – #628 out-of-the-box surface (pulse, retry chrome, licensed reads, subagent catalog, optional MCP).
- Test ratchet: unit suite 1677 (floor 1664).
Downloads
All six CLI archives, the curl installer, and checksums:
| File | Platform |
|---|---|
| graff-aarch64-linux.tar.gz | Linux arm64 |
| graff-aarch64-macos.tar.gz | macOS Apple Silicon — Developer ID signed, Apple notarized |
| graff-aarch64-windows.tar.gz | Windows arm64 |
| graff-x86_64-linux.tar.gz | Linux x86_64 |
| graff-x86_64-macos.tar.gz | macOS Intel — Developer ID signed, Apple notarized |
| graff-x86_64-windows.tar.gz | Windows x86_64 |
| install.sh | curl installer |
| SHA256SUMS | checksums for the six archives |
curl -fsSL https://github.com/justrach/codegraff/releases/download/v0.0.277/install.sh | bashBoth macOS binaries are Developer ID Application signed (hardened runtime,
timestamped) and Apple notarized (notary-local, Accepted). Stapling is
skipped on the bare CLI (stapler error 73); Gatekeeper checks the ticket
online at first launch. GitHub also attaches the usual source zip/tarball
for the tag.