fix(buyer): PresenceUnverified named an operator action that does not exist (#290) - #294
Merged
Merged
Conversation
… exist (#290) The refusal told the operator to "release the reservation to re-arm the award". No such command exists in the CLI or the MCP surface; the only release() caller is the internal publish-failure path. Recovery rides the reconcile pass. State the mechanism correctly: reconcile releases on (claim_live || has_bind) both false, NOT on a deadline -- nothing in reservations.rs knows what a deadline is. The offer deadline bounds it only indirectly, via the claim expiring, so the message says "once the claim is no longer live on the relay". Also correct the doc comment above both variants: it claimed both refusals name an operator action. Only PublishedButUnrecorded needs a human, because nothing else will write the missing awards row. Strengthen the test. It asserted message.contains("release"), which a message reading "reconcile RELEASES this reservation" still satisfies -- the substring survives a change that inverts the meaning, so it would have passed on exactly the wording it was written to reject. It now asserts the property: the one reachable command is named, recovery is stated as automatic, and the phrase "release the reservation" is absent. Red-proved: reintroducing the original wording verbatim fails the test (0 passed; 1 failed), restoring it passes (1 passed). Suite 645/0/3, unchanged from #289's baseline -- assertions added, not tests. Wording only. No behaviour change, no config change, nothing on the money path. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #290.
PresenceUnverifiedtold the operator to "release the reservation to re-arm the award." There is no such command — not in the CLI (buyer= serve/status,wallet …), not in the MCP surface (post_job/get_job/collect/award/stub_pay). The onlyrelease()caller in the codebase is the internal publish-failure path. Recovery actually rides the reconcile pass.Naming an unreachable action is worse than naming none: it sends the reader hunting for a command that was never there, on a refusal that is otherwise correct and money-safe.
The mechanism, stated correctly
This is the part worth reviewing, because the obvious reword is also wrong. I asserted during #289's review that reconcile "auto-releases a Dead reservation past deadline" — it does not, and nothing in
reservations.rsknows what a deadline is. The predicate is (buyer/mod.rs:1094-1116):The offer deadline bounds it only indirectly, by the claim ceasing to be live. The load-bearing form is anything that keeps a claim alive keeps the budget reserved — so the new wording says "once the claim is no longer live on the relay" rather than inventing a timer.
Changes
The refusal now states that recovery is automatic and names the one reachable command (
collect <job_id>) as an optional way to settle sooner, rather than as the remedy.The doc comment above both variants was also wrong — it claimed "both refusals name the operator action." Only
PublishedButUnrecordedneeds a human (nothing else will write the missing row). Rewritten to say what each message is for and why they differ.Test strengthened, and this is the substantive bit. The existing assertion was:
A message reading "reconcile releases this reservation" still satisfies
contains("release")— the substring survives a change that inverts the meaning, so the test would have passed on exactly the wording it was written to reject. It now asserts on the property instead: the reachable command is named, recovery is stated as automatic, and the phraserelease the reservationis absent.Verification
Red-proved rather than assumed — reintroducing the original wording verbatim makes the test fail, and restoring it makes it pass:
Wording-only; no behaviour change, no config change, nothing on the money path moves. Suite green.
Not addressed here
Ok(empty)-on-timeout conflation at three call sites offetch_job_view_async, one of which is the reconcile liveness read this message now points at. That is the contract bug; this is the string.awardsrow.