Tools Return Data, Agents Make Decisions
I watched an agent run openpkg diff old.json new.json. It got back a complete, correct wall of changes and started counting.
It wanted to know one thing, "did anything break", and I'd handed it a dataset. The verbs were right after the December cleanup. The outputs were answering a question nobody asked.
Three questions
A diff gets consulted for three different reasons, and each consumer already speaks a shape:
| Who asks | The question | The shape they want |
|---|---|---|
| CI | did anything break | an exit code |
| a release script | what version is this | a string, major / minor / patch |
| a changelog | what changed, for humans | markdown |
Same computation, three answers. This week's commit split the surface and kept one engine underneath.
breaking
did anything break?
exit code 1, the gate itself
semver
what bump does this need?
always exit 0, a recommendation
changelog
what do I tell humans?
markdown, release-note material
breaking doesn't print a report and wish you luck; the exit code is the answer, so a pipeline consumes it with zero parsing. semver always exits 0, and the source has a comment insisting on it ("this is a recommendation only"), because recommending is data and enforcing would be a decision.
filter
The command that fell out of the refactor unprompted: take a spec and criteria, get a smaller spec back.
openpkg filter spec.json --kind function --search fetch{ "matched": 1, "total": 5, "exports": ["fetchUsers"] }
The flags read like questions an agent asks: --deprecated, --missing-description, --tag, --search-members, --summary for counts only. It never changes the spec, and since every other command takes a spec as input, filter composes in front of all of them.
Who decides
If the tool decides, it's a feature, and features don't compose. If the tool returns data and the caller decides, it's a primitive.
| Command | Decides | Returns |
|---|---|---|
breaking | nothing; the categorizer in the spec package defines "breaking", in versioned code | pass / fail |
semver | nothing; "recommendation only" | a bump |
diagnostics | nothing; missing descriptions, deprecations without reasons, undocumented params | facts, no verdict |
Your CI config gets to have the opinion. Rule I keep: a tool's output should still be useful to a program I haven't written yet.