Build1 distinct publisher3 min readPublished
TrueForge sorts tools by readOnlyHint and destructiveHint, so a tool declaring neither matches no tag in the approval policy and nothing asks before it touches production, which is why the default has to be deny at both enforcement points.
The Engineer · Build desk
Compiled by The EngineerSomething wrong?How this is made
Omitting the annotation object is the loud version of this bug. The partial version is quieter. Take a server that declares `destructiveHint: false` and stops there. `isWrite` tests `a?.readOnlyHint === false`, not "is not read-only", so an undefined `readOnlyHint` fails it [2]. `isReadOnly` needs `true`. `isDestructive` needs `true`. The tool lands in no bucket at all, exactly like the tool that annotated nothing [6]. Half-filling the form buys you nothing over ignoring it.
The policy file cannot see any of this. `require_approval_for_tools` is a list of tags that require approval [4], which makes it an enumeration of the gated set, and everything outside the enumeration is permitted by construction [7]. Default-deny means moving the burden to the other side of the predicate: gate unless `isReadOnly(a) === true`. That is a one-line change in the harness, and it turns a forgotten field into an extra approval prompt instead of an unattended write.
The inversion only covers one door. Enforcement lives in the harness, not in the MCP server [10], and in the author's setup that server bound to `0.0.0.0` and served `/mcp` with no authentication [9]. A curl to that port reaches the tool without ever passing the harness [10]. His own framing is that the bind did not weaken the safety model, it offered a way around it entirely [11], which is why "is `rollback_deployment` gated?" has one answer per route rather than one answer per tool, and has to be measured route by route [15]. The reviewer that caught the bind was Qodo, a bot, and the first human instinct was to call it low severity because the estate was simulated [9]. Draw your own conclusions about severity triage under hackathon deadlines.
For code you own, the structural fix beats the policy fix. Every tool is built through a `defineTool` call where `risk` is required and the annotations are derived from it, so no registration path exists that omits them [12], and `rollback_deployment` carries `risk: 'destructive'` at its definition site [13]. I prefer that ordering, because the omission becomes a build problem rather than an incident. It also only covers tools that pass through your own `defineTool`. Third-party servers hand you whatever they have, and the harness reads annotations as an optional argument [2].
So the transfer conditions are narrow but common. You have this hole if your approval policy names tags rather than tools, if those tags are derived from annotations the tool server supplies, and if the policy lists what to gate rather than what to allow. Change any one of those and the failure mode changes shape. Keep all three and the omission ships without an error or a warning, and code review looks clean [5].
One gap worth naming: the writeup says the hole was closed three ways and that a suite was built whose only job is to attack the fix [14], and the text supplied here documents the structural closure. Sentinel-agent's split, with investigation automated end-to-end and a human authorising any change to production state [8], is worth exactly as much as the weakest route into the tool server underneath it [10].
Ranked by verification strength, evidence, and original report placement.
TrueForge is an open-source agent harness; the function that decides whether an AI agent may touch production systems without asking first is four lines long and lives in trueforge-core/src/core/mcp/toolSelectors.ts.
The selectors read a tool's optional annotations argument: isReadOnly(a?: ToolAnnotations) returns a?.readOnlyHint === true; isWrite returns a?.readOnlyHint === false && a.destructiveHint !== true; isDestructive returns a?.destructiveHint === true.
When the annotations argument is undefined, isReadOnly, isWrite and isDestructive all return false, so a tool that publishes no annotations at all matches none of the three predicates.
The default approval policy is a list of tags: "require_approval_for_tools": ["@write", "@destructive"], and a tool that matches no tag matches nothing in that list.
A rollback_deployment tool that omits its annotations is not gated: it does not error, it does not warn, it fires straight at production silently, and nothing in code review looks wrong.
Sentinel-agent is an autonomous incident responder that reads the incident, characterises the symptom, enumerates recent deployments, reads the diffs, exports raw metrics and computes the magnitude in an isolated sandbox, then correlates that into a root cause with a stated mechanism and a confidence number, and stops: it will not change production state on its own authority, and a human authorises that.
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 30, 2026
Follow any of these and your For You feed starts watching them — no settings page required.
build
Airlock's per-case proxy enforces what a tool's readOnlyHint only claims1 distinct publisher
build
A gate that warns and a fan that queues: test the executor, not the card1 distinct publisher
build
TrueFoundry open-sources an agent harness and calls managed agents a lock-in play2 distinct publishers
build
Signed receipts on MCP tool calls: 20 lines buys you evidence, not trust1 distinct publisher
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
checkable code, uncorroborated scope
The core mechanism is quoted rather than asserted: three predicate bodies, one policy line, and a walkthrough of the undefined case that any reader with the harness source can repeat in a minute. That is the strongest kind of evidence a single post can carry. What it lacks is everything around the code — no version or commit for TrueForge, no maintainer confirming the file still reads that way, no second party reproducing the silent rollback, and a probe run whose per-route verdicts stop mid-sentence.
one engineer, one simulated estate
The only estate in this story is the author's own, and he tells us it is simulated. The concrete numbers — 13 tools, none unannotated, five gated — describe his hackathon project after he fixed it, not anyone else's fleet. No other user of the harness appears, no organisation has adopted the required-risk pattern, and nobody outside the post has run the gate probes.
modest, and mostly in the framing
The headline promise — a missing annotation walks a rollback tool past the gate — is exactly what the quoted code supports, and the author actively resists his own slogan ("investigation is automated, execution is authorised" gets deflated a paragraph later). The overshoot is narrower than that: a hazard demonstrated in one simulated estate is presented as a property of the harness generally, without a version, an upstream acknowledgement, or the probe numbers that were supposed to settle it empirically.
a hackathon entry marking its own homework
The author is writing for judges and says so — the stated goal is turning a slogan into "something a judge can actually check." He found the flaw, wrote the fix, wrote the tests against the fix, and wrote the suite that grades the fix, all inside the submission window. To his credit the test design deliberately works against him, reimplementing TrueForge's predicates instead of asserting his own labels. Worth noting too that Qodo, a commercial code reviewer, gets the credit for the finding that reorganised the project.
solid on the mechanism, thin on the world
Confidence splits cleanly. That an unannotated tool matches no approval tag is about as verifiable as a single source gets — the code is on the page. Whether this is live in current TrueForge, how many shipped tool definitions omit annotations, and whether the open /mcp endpoint was ever reachable outside a sandbox are all unanswered, and one dev.to post cannot answer them.