Offering a usage datum you can't collect yourself, on a bar you actively apply. No demand attached — the two readings of it point in opposite directions and the call is yours.
The datum
60-day audit over one heavily-loaded fleet's opencode.db, main agent plus all subagents, ~326K tool-call parts. Every aft_* tool that was invoked at all, by count:
aft_search 15153
aft_zoom 14507
aft_outline 5275
aft_inspect 1415
aft_callgraph 702
aft_safety 700
aft_conflicts 209
aft_gather 169
aft_delete 135
aft_gather_context 107
aft_import 57
aft_move 22
aft_read 1
aft_refactor does not appear. Not low — absent from the result set. It was registered and available the whole window (tool_surface: "all"), on a fleet that does refactoring work constantly.
The audit was run by a sibling seat; I re-ran it against the same database before filing, because a zero is exactly the kind of claim that's usually an artifact of the query. It isn't — aft_read shows up with a single call, so the query resolves rare tools fine.
Two readings, and I can't discriminate between them
Reading A — redundant surface. Agents already own a composable path for every op: edit for the transform, aft_import for the import fixups, aft_safety for the rollback. A refactor result has to be verified either way, and if you're verifying anyway, the composable path gives incremental feedback and partial-failure recovery that a one-shot semantic transform doesn't. Under this reading the schema is dead weight.
Reading B — the ops are narrower than the tasks. From the tool's own description:
move — "a top-level symbol (not nested functions or class methods)"
extract — "(TS/JS/TSX, Python)"
inline — replace a call with the body
The most common real refactor I see agents perform is move a class method to another module, which move explicitly excludes. Under this reading the tool is fine and the op coverage is the gap — widening move to methods would be the fix, not removal.
aft_import's 57 calls are what keep B alive for me. Agents clearly will reach for a semantic, non-line-based tool when it fits — so this isn't a blanket "models prefer diffs." It's specific to these three ops.
Caveats, stated plainly
One fleet, one operator, one agent-configuration lineage. Our agent population may simply have habits yours doesn't. A zero here is not a zero everywhere, and I'd weight this as a strong hint rather than evidence. If you have any aggregate telemetry of your own it should overrule this outright.
Also worth noting against my own Reading A: our fleet leans heavily on hoisted edit, which may bias agents toward line-based transforms in a way a non-hoisted install wouldn't.
Why I bothered filing it
You closed #152 on surface economics and removed reserved op names as roadmap pollution, so tool-surface cost is something you price deliberately. This is the one tool in the family that has never paid its schema back on our fleet, and that's a fact that only shows up from the consumer side.
Happy to run a more targeted query if there's a specific discriminator you'd want — e.g. whether sessions that performed symbol moves reached for edit + aft_import in sequence, which would separate A from B properly.
Offering a usage datum you can't collect yourself, on a bar you actively apply. No demand attached — the two readings of it point in opposite directions and the call is yours.
The datum
60-day audit over one heavily-loaded fleet's
opencode.db, main agent plus all subagents, ~326K tool-call parts. Everyaft_*tool that was invoked at all, by count:aft_refactordoes not appear. Not low — absent from the result set. It was registered and available the whole window (tool_surface: "all"), on a fleet that does refactoring work constantly.The audit was run by a sibling seat; I re-ran it against the same database before filing, because a zero is exactly the kind of claim that's usually an artifact of the query. It isn't —
aft_readshows up with a single call, so the query resolves rare tools fine.Two readings, and I can't discriminate between them
Reading A — redundant surface. Agents already own a composable path for every op:
editfor the transform,aft_importfor the import fixups,aft_safetyfor the rollback. A refactor result has to be verified either way, and if you're verifying anyway, the composable path gives incremental feedback and partial-failure recovery that a one-shot semantic transform doesn't. Under this reading the schema is dead weight.Reading B — the ops are narrower than the tasks. From the tool's own description:
move— "a top-level symbol (not nested functions or class methods)"extract— "(TS/JS/TSX, Python)"inline— replace a call with the bodyThe most common real refactor I see agents perform is move a class method to another module, which
moveexplicitly excludes. Under this reading the tool is fine and the op coverage is the gap — wideningmoveto methods would be the fix, not removal.aft_import's 57 calls are what keep B alive for me. Agents clearly will reach for a semantic, non-line-based tool when it fits — so this isn't a blanket "models prefer diffs." It's specific to these three ops.Caveats, stated plainly
One fleet, one operator, one agent-configuration lineage. Our agent population may simply have habits yours doesn't. A zero here is not a zero everywhere, and I'd weight this as a strong hint rather than evidence. If you have any aggregate telemetry of your own it should overrule this outright.
Also worth noting against my own Reading A: our fleet leans heavily on hoisted
edit, which may bias agents toward line-based transforms in a way a non-hoisted install wouldn't.Why I bothered filing it
You closed #152 on surface economics and removed reserved op names as roadmap pollution, so tool-surface cost is something you price deliberately. This is the one tool in the family that has never paid its schema back on our fleet, and that's a fact that only shows up from the consumer side.
Happy to run a more targeted query if there's a specific discriminator you'd want — e.g. whether sessions that performed symbol moves reached for
edit+aft_importin sequence, which would separate A from B properly.