feat(sell): loud resume failures, clock-skew preflight, honest status staleness#705
Open
OisinKyne wants to merge 1 commit into
Open
feat(sell): loud resume failures, clock-skew preflight, honest status staleness#705OisinKyne wants to merge 1 commit into
OisinKyne wants to merge 1 commit into
Conversation
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.
Summary
Three fixes sharing one theme: can a seller trust this stack with real revenue? Each closes a way the stack could look healthy while money silently stopped flowing.
1. Partial resumes are now loud
Previously
resumeSellOfferswarned per-offer and returned nil — after a reboot, a seller reading "stack up: OK" could have offers that never came back and no signal beyond a scrolled-past warning. Now:obol sell resumeexits non-zero on any failed offer — so the systemd boot unit reports failure instead of green.obol stack upkeeps warn-and-continue (one broken offer must not block cluster startup) but now surfaces the same named-offer summary.The test that pinned the old silent behavior (
TestResumeSellOffers_HTTPOnlyStore) was deliberately flipped: it now asserts the aggregate error is returned and names the failed offer.2. Clock-skew preflight
EIP-3009 authorizations anchor
validAfter/validBeforeto wall clocks. A drifted seller host makes the facilitator reject every buyer payment withauthorization is not yet validwhilesell statuslooks perfectly healthy — the classic silent-revenue-stop (see CLAUDE.md pitfall 18 for the anvil-fork variant of the same failure).New
x402.MeasureClockSkewprobes the facilitator's HTTPDateheader (3s timeout, request-midpoint latency correction).warnOnClockSkewfires above ±30s — comfortably over Date-header resolution + network noise, well under the drift that breaks payments:Called at the four moments a seller acts:
sell inference(honoring its--facilitatorflag),sell http,sell agent,sell resume. Probe failures are silent by design — the check may never block or delay selling. Deliberately not wired intosell status(runs inside release-smoke loops; an external 3s probe per call would slow them) orstack updirectly (reached via resume).3. Honest status staleness
sell status <name>conditions are the controller's reconcile snapshot, not live counters — a known trap (CLAUDE.md "PurchaseRequest status caveat"). Conditions now show their age and the output says what it is:Labeled "changed" (not "updated") because
lastTransitionTimeonly moves on state flips — calling it "last updated" would misrepresent a healthy long-running offer. Zero timestamps render no age. The closing line echoesdocs/observability.md's chain-is-canonical doctrine.Testing
httptestservers.TestResumeSellOffers_HTTPOnlyStorepins the loud contract;TestResumeSellHTTPOffers_EmptyStoreNoOpextended for the new two-value return.TestFormatConditionLine_ShowsAge(incl. zero-time guard),TestServiceOfferStatusLines_SnapshotCaveat(caveat only when conditions exist),TestHumanAgetable test.go build ./... && go test ./...Review focus
cmd/obol/sell.goresumeSellOffers: error semantics —sell resumemust fail on partial resume,stack upmust not; per-offer loop must still continue past failures.internal/x402/clockskew.go: read-only GET, bounded by a 3s context, no interaction with verification/settlement code paths.warnOnClockSkewcall sites: preflight only — nothing downstream may depend on its outcome.🤖 Generated with Claude Code