Skip to content

fix(buyer): PresenceUnverified named an operator action that does not exist (#290) - #294

Merged
orveth merged 1 commit into
devfrom
fix/290-presence-unverified-wording
Jul 30, 2026
Merged

fix(buyer): PresenceUnverified named an operator action that does not exist (#290)#294
orveth merged 1 commit into
devfrom
fix/290-presence-unverified-wording

Conversation

@orveth

@orveth orveth commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Closes #290.

PresenceUnverified told 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 only release() 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.rs knows what a deadline is. The predicate is (buyer/mod.rs:1094-1116):

payable = claim_live || has_bind
Dead    = no payment progress AND NOT payable   ->  released

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 PublishedButUnrecorded needs 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:

    assert!(message.contains("collect") && message.contains("release"),);

    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 phrase release the reservation is absent.

Verification

Red-proved rather than assumed — reintroducing the original wording verbatim makes the test fail, and restoring it makes it pass:

with the #290 wording restored:  test result: FAILED. 0 passed; 1 failed; 647 filtered out
with the fix:                    test result: ok.     1 passed; 0 failed; 647 filtered out

Wording-only; no behaviour change, no config change, nothing on the money path moves. Suite green.

Not addressed here

… 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>
@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
mobee Ready Ready Preview Jul 30, 2026 3:46am

Request Review

@orveth
orveth merged commit 0ac8b2e into dev Jul 30, 2026
5 of 6 checks passed
@orveth orveth added this to the v1 milestone Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant