prod-code 0.3.52: read-only tools that say so, and failures that name the way on
More tools declare in the MCP catalog that they never write, shell and assembly files get outlines without a language server, and silent failures now explain themselves. Built and signed by a hosted workflow using Mitts; generated Rust commands still run cargo.
On this page · 8 sections
Most of prod-code v0.3.52
(commit 91ec5def, published 2026-11-05) fixes cases where a call failed without giving a
reason, or reported success when it had not done the work. This post follows the v0.3.52
section of the
changelog at the tag; each
item below carries its issue number. The Zenodo citation for prod-code is still the
v0.3.49 record. There is no archived v0.3.52 version
yet.
Read-only tools say so
An MCP host whose approval policy is never refuses any tool it cannot tell is read-only
(MCP tool call requires approval, but approval policy is never), and the agent falls back to
the command line. Until this release only the navigation tools carried readOnlyHint. Eleven
more now carry it: code_impact, code_diagnostics, code_validate_edit,
code_validate_edits, code_assists, code_dead_code, code_dependencies,
code_find_duplicates, code_structural_search, code_propose_expression and code_status.
Tools that write, run a command, or are read-only only for some arguments stay unmarked. A test
checks the marked list against the server’s own definition of a read-only tool (#1887, #1890,
#1900, #1905).
Files without a language server
No language server answers a shell symbol. code_definition NAME now finds a function defined
in a .sh, .bash or .zsh script, with its body. If two scripts define the same name, both
are listed as candidates instead of one being guessed (#1940). A shell script’s outline is now
read from the file itself. It covers all three function forms, and it skips comments and
here-document bodies. .s, .S and .asm files are outlined the same way, from .globl
symbols, labels and .macro definitions (#1851, #1935). An outline that is read from the file
no longer asks the cluster for a node that serves its language. A directory outline that mixes
languages outlines what it can reach, and counts the files of an engine that did not start as
skipped (#1898).
Failures that explain themselves
- A silent timeout. A command runs on the node with standard input closed and no terminal. One that waits for a login, a browser or a prompt therefore waits until its deadline and prints nothing. When a command times out with no output, the report now says why and suggests a non-interactive option or another place to authenticate (#1892).
- Dropped standard input.
prod-code exec -- python3 - <<'PY' … PYused to run an empty program on the node and report success, because the command line does not carry standard input to the node. When input is waiting,execnow stops before anything runs. It suggests putting the program in a file in the checkout or passing it as an argument. Standard input is still not forwarded (#1944). - An unreachable node. Connecting to a node that does not answer used to take three
operating-system connect timeouts in a row and end in a bare “timed out”. Each attempt now
waits at most 8 seconds. The error names the node and points to
prod-code status,--remoteor--pin-node(#1938). - A Swift rename that missed files. sourcekit-lsp renames only what its index knows, and a
call in another file is not indexed until the target is built.
code_renameused to report success anyway. It now lists the Swift files that still contain the old name, with the advice to build and rename again (#1953). This reports the gap; it does not close it.
Answers about the right checkout
code_impact now accepts the same workspace selector as code_exec and the read tools. From
a PR worktree, the report covers that worktree’s diff. Before, it described the configured
checkout: 290 changed files for a two-file change (#1939). It also stops naming
swift test for Swift tests in an Xcode project without a Package.swift, and reports that
group as unsupported (#1951). code_source now reads a dependency’s source when workspace is
set; paths with .. are still refused (#1955).
Smaller changes
- Moving a private Rust item into a sibling module now makes it
pub(crate)when another module still names it, and the move report says so (#1933). - Files that an
execcreates on the node’s copy, such as a scratch script or a generated framework, are no longer deleted by the next connection’s sync (#1895, #1888). - A tracked symlink that the node will not create is now a warning. It used to be an error that stopped every command in the workspace (#1889).
Nothing was removed. The release makes two behaviours stricter or quieter on purpose: exec
refuses input it would drop, and the symlink error is now a warning.
How the release itself was built
This is the first prod-code release whose binaries were built, signed and published by a GitHub
Actions workflow on standard hosted runners (#1852). The workflow builds with
Mitts, the author’s Rust build tool, and CI runs formatting, Clippy and the
tests through it. The release run for the tag took about 30 minutes. The Linux ARM64 build took
13 minutes, Linux x86-64 16 and macOS ARM64 28, then signing and collection ran. The release
page lists six binaries, the client and the server for Linux x86-64, Linux ARM64 and macOS ARM64,
plus SHA256SUMS. The macOS binaries are Developer ID signed and Apple notarized. I checked each
line of SHA256SUMS against the digest GitHub records for that asset, and all six match.
CI previously ran each shard’s test targets one after another without a concurrency flag. Now
each shard passes --concurrency (#1930). The last full-workspace run before the tag is recorded
in PR #1956, after which only the version bump
followed. On one 32-core Linux x86-64 node, mitts test --workspace --no-fail-fast -c ran 5569
tests in 663 seconds. All 5569 passed and 23 were skipped.1 Two tests that read the machine’s Cargo crate cache were rewritten so that they pass
with an empty CARGO_HOME (#1930). A stack-depth failure in the Go signature tests and a race
in the hot-reload drain were also fixed (#1941, #1814).
What did not change
Mitts builds and tests prod-code itself. The commands prod-code generates for a user’s Rust
project are different: code_test, code_check and code_lint still run cargo. In
verify/plan/basic.rs at the tag, a Rust test is still cargo test --workspace. Switching
them was tried and reverted before the release. As
PR #1952 records, Mitts rejects --quiet and
spells check differently, and 147 tests failed. A setting to choose another runner per project
is not part of this release.
Footnotes
-
The changelog at the tag gives 5553 tests in 660 seconds for #1930. That entry was written before #1954 and #1956 added tests. The tag cannot change, so its changelog keeps the older figure; the entry on prod-code’s main branch is being corrected to match PR #1956. ↩
Cite this article
Alexander Panasenko (2026-11-06). prod-code 0.3.52: read-only tools that say so, and failures that name the way on. https://prod.codes/blog/prod-code-0-3-52-failures-that-name-the-way-on/