Skip to content

chore: close out the MCP control-surface milestone (docs reconciliation + hygiene)#228

Merged
bastien-gallay merged 5 commits into
mainfrom
chore/wrap-mcp-milestone
Jul 25, 2026
Merged

chore: close out the MCP control-surface milestone (docs reconciliation + hygiene)#228
bastien-gallay merged 5 commits into
mainfrom
chore/wrap-mcp-milestone

Conversation

@bastien-gallay

Copy link
Copy Markdown
Collaborator

What

Closing pass over the MCP control-surface milestone (#194, #195, #216 — six of
#90's eight slices now shipped). No behaviour changes to the app; this is the
paperwork the three feature PRs could not do individually, because the claims
they falsified only became false once all three had landed.

The one that matters

run_in_session's tool description told an agent to read a command's result
"with snapshot's scoped terminal text" — polling, which races the very
transition the caller is watching for. That was accurate when orchestration
shipped and wait_for_status did not exist. It has been wrong since #195, and
it is the text an LLM reads at tool-selection time, so it was actively
steering agents into the pattern the wait rung was built to replace. It now
routes to wait_for_statusread_terminal and says plainly not to poll.

Superseded claims, reconciled

Found by grepping for the claim rather than by re-reading the files already
open — these were all in files none of the three feature PRs touched:

Where Said Actually
README.md orchestration is "a planned follow-up" three PRs merged
handler.rs read the result via snapshot see above
bridge.rs, orchestrate.rs "waiting is a later rung" that rung landed
AGENTS.md mcp is an adapter "yet to land (Unsure)"; crates/app is "a tracing + single-instance stub" both long false
ARCHITECTURE.md §5 "nothing in the v1 path imports it" false for the control surface

The ARCHITECTURE claim is retracted in place (struck through, marked
superseded, pointed at what replaced it) rather than edited into correctness —
a reader arriving still holding the old belief needs to recognise it.

It also earns a disambiguation, because the name collision is real and will
otherwise be re-litigated: §15's deferred mcp crate is F-mcp-ide-bridge
termherd as a client of Claude's IDE bridge — and it genuinely is unbuilt.
The shipped surface runs the other way round (termherd is the server) and lives
in app rather than in a crate of its own, because it is a bridge into the
shell, not a port core calls out through.

Entry point

AGENTS.md gains a Driving termherd over MCP section stating what is
settled, what is still open (#215, #196 — both ticketed), and the contradiction
that is not one. README.md documents both surfaces — the automatic live
bridge and the manual stdio config server — and the act → wait → observe loop
they exist to serve.

One lesson promoted

AGENTS.md quality bar: an invariant expressed twice will drift — extract
the predicate.
Its instance is the bug found in review on #227: two call
sites decided "has this settled?" with hand-written conditions, one counting a
session exit and the other not, so a wait placed after a crash parked until the
caller's timeout. Recorded with the part that makes it a trap rather than an
oversight — the doc-comment stating the correct rule sat directly above the
code that broke it.

Nothing else generalised past its own feature.

Two dirt sources tied off

  • .impeccable/ — the skill drops a cache beside every source directory it
    reads; four had accumulated under crates/ and none were ignored.
  • markdownlint was linting .personal/ and .local/ — gitignored notes CI
    never sees. It reported 4 errors on 37 local files where CI lints 24 and sees
    none: errors nobody could act on, which teaches you to skim the summary line.
    Local and CI now lint the same set.

Config

.wrap.md records the locations, gates and conventions this pass had to infer,
plus two things learned the hard way today: worktree removal must check the PR
state rather than git log main..branch (PRs are squash-merged, so a merged
branch's commits never appear on main), and .impeccable caches get ignored
rather than deleted because they regenerate.

Verification

fmt · clippy -D warnings · test --workspace (13 suites) · markdownlint
(25 files) · check-deps · check-arch all green. No .rs logic changed —
the only code edits are doc-comments and one tool description string.

…g replaced

The tool description is what an LLM reads when it chooses a tool, and this one
told it to read a command's result "with `snapshot`'s scoped terminal text" —
polling, which races the very transition the caller is watching for. That was
accurate when orchestration shipped and `wait_for_status` did not exist; it has
been wrong since. It now routes callers to `wait_for_status` then
`read_terminal`, and says plainly not to poll.

Two doc-comments still called the wait "a later rung" for the same reason.
… ARCHITECTURE

Six slices of #90 landed, and three documents still described the surface as
unbuilt. README called workspace orchestration "a planned follow-up"; AGENTS
listed `mcp` among the adapters yet to land (and still called `crates/app` a
tracing stub); ARCHITECTURE said nothing in the v1 path imports it.

README now documents both surfaces and the act -> wait -> observe loop they
exist to serve, and AGENTS gains an entry point naming what is settled, what is
open, and where each piece lives.

The ARCHITECTURE claim is retracted in place rather than edited into
correctness: a reader arriving with the old belief needs to recognise it. It
also earns a disambiguation, because the collision is real — §15's deferred
`mcp` crate is `F-mcp-ide-bridge`, termherd as a *client* of Claude's IDE
bridge, which is genuinely unbuilt. The shipped surface runs the other way
round and lives in `app`, since it is a bridge into the shell rather than a
port `core` calls out through.
…redicate

The one lesson from the MCP milestone that generalises past its own feature.
The wait rung stated 'has this settled?' at two call sites with hand-written
conditions; one counted a session exit as settling, the other only compared
against the requested statuses. A wait placed after a crash therefore parked
until the caller's timeout — on a session core had already marked terminal.

Recorded with its instance rather than as an abstraction, and with the part
that makes it a trap rather than an oversight: the doc-comment stating the
correct rule sat directly above the code that broke it. A comment is not
enforcement.
Two recurring dirt sources, tied off at the root rather than cleaned again
next time.

The impeccable skill drops a hook.cache.json beside every source directory it
reads; four had accumulated under crates/ and none were ignored, so each one
surfaced as untracked noise in git status.

markdownlint was linting .personal/ and .local/, which are gitignored working
notes CI never sees. It reported four errors on a fresh clone's 24 files
becoming 37 locally — errors nobody could act on, which teaches you to skim
past the summary line. Local and CI now lint the same 24 files.
The pass ran unconfigured and re-derived the canonical locations, the gate
list and the commit conventions from the tree. Written down so the next one
does not, and so a moved location is corrected in one place.

Two entries encode things this session learned the hard way: worktree removal
must check the PR state rather than `git log main..branch` (PRs are
squash-merged, so a merged branch's commits never appear on main), and
.impeccable caches are ignored rather than deleted because they regenerate.
@bastien-gallay
bastien-gallay merged commit 5c3ab3a into main Jul 25, 2026
17 checks passed
@bastien-gallay
bastien-gallay deleted the chore/wrap-mcp-milestone branch July 25, 2026 20:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant