Skip to content

id(currency): fold_authorisation duplicate highest version folds by input order — no deterministic tie-break #4069

Closed

Description

@dezren39

Summary

pkgs/id/src/currency.rs fold_authorisation (landed today via PR #3888, closing #3800) selects the live version with max_by_key(|(_, body)| body.version) (currency.rs:313-320). When two distinct signed bodies from the expected issuer carry the same highest version (a forked/duplicated issuer chain, or a re-issued v2 with a different rate), Iterator::max_by_key returns the last maximal element in slice order — so the folded Live authorisation depends on the order the caller happens to present the objects, not on the objects alone. The module header promises "State is a fold over the objects alone" (currency.rs:20-23), and the revoke side of the same fold was made explicitly deterministic ("ties break to the smallest revoked_version_from, deterministically", currency.rs:296-300) — the version selection has no such tie-break.

Evidence (verified this run, laptop probe)

Probe: ~/git/fleet/probe-2026-11-01-laneB-currency/ on the laptop — verbatim validate + version-selection core of fold_authorisation, against currency.rs at main c3fda1c4 (sha256 8f68982526219dff920586495a076d2c1a33f87fe3b6716a74e561ea821b9493, reference copy in the probe dir).

Two issuer-signed v2 bodies for authorisation 7, rate_num 8000 vs 9000:

order [8000,9000] -> rate_num 9000
order [9000,8000] -> rate_num 8000
test duplicate_version_fold_depends_on_input_order ... ok

Same object set, two presentation orders, two different live rates.

Why it matters

The fold is the library source of truth every later phase consumes: Phase 2 (#3801) folds "highest version" at submission time and Phase 3 (#3802) binds quotes to it. Two nodes (or one node after a re-sync that changes object order) can fold the same signed object set to different live rates and both be "correct" per this code — a consensus-style divergence in a money-adjacent type, created by the issuer merely signing two bodies at one version (which the chain permits on forked chains; nothing in validate or the fold rejects duplicate versions).

Acceptance criteria

  • Duplicate highest versions fold deterministically regardless of input order: either a canonical tie-break (e.g. by body hash / canonical bytes) documented in the fold's doc comment, or duplicate versions at the maximum are rejected / fold to no live authorisation with the rule stated.
  • Regression test: the same duplicate-version object set in both orders folds to the identical state.
  • Existing currency tests (golden rates, hijack, revoke tie-break) still pass.
  • The chosen rule is recorded on id: design: RateAuthorisation as a signed ID-graph object — schema, signing, revocation #2290 (parent design) if it changes the design record's fold semantics.

Distinct from: #3801 (gateway live-state checks — it consumes this fold), #3802 (quote binding), #4060/#4063 (app_manifest validation, different module).

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    priority/normalOrdinary priority (the default)
    queue/queuedHas a queued task in the Supabase agent queue
    source/creatorFiled by the new-issue-creator job
    task/triageQueue packet kind: verify, reproduce, and route this item
    app/idPackage/app: id (pkgs/ or apps/ in this repo)
    effort/sSmall: expected under 2 hours of work
    on Nov 1, 2026
  2. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

  3. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 2, 2026
  4. 28 remaining items

  5. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

    • Agent: fleet monitor
    • Session / ID: repo-fleet-monitor run 2026-11-02 16:12 CST; queue task 6306
    • Found via: monitor reconciliation
    • Task: Claim/label divergence auto-repair (Duty 1 addendum): queue task 6306 is queued with no live lease after attempt(s) reached its cap (checkpoint stage: handed_off), but this issue still carried claimed/assigned labels. The claim evaporated — the task is queued and unclaimed. Labels reset to the truth: queue/queued + status/needs-triage. Open PR: PR fix(id): deterministic tie-break for duplicate highest currency versions (#4069) #4547 (verify and shepherd it on resume — do not re-implement).
    • Expectation: operator repair — task 6306 is dead-in-queue (attempts at max); raise its cap or enqueue a fresh packet so a lane can resume from checkpoint stage handed_off.
  6. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    agent-note:v1
    event: picked-up
    task: 6306 (repo-health-code-pilot, repo-worker-code; lane ec118cad-0fd3-4652-91c4-27432f2f2efd)
    state: claimed (resume from awaiting_ci)
    verified: Fresh GitHub state 2026-11-03T08:05Z: #4069 OPEN; bound PR #4547 OPEN on branch queue/6306-4069-currency-tiebreak at aee7648 (matches checkpoint); checks basic guard SUCCESS + CodeRabbit SUCCESS. Resuming: verify worktree/tests, then merge per standing criteria.
    next: Targeted currency tests in task worktree, re-check mergeability, merge + complete.

  7. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 3, 2026
  8. added a commit that references this issue on Nov 3, 2026
  9. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    agent-note:v1
    event: done
    task: 6306 (repo-health-code-pilot, repo-worker-code; lane ec118cad-0fd3-4652-91c4-27432f2f2efd)
    state: complete fixed_merged
    result: PR #4547 merged as 5994eaf on origin/main; issue auto-closed. fold_authorisation now breaks equal-version ties by lexicographically greatest canonical body bytes (documented); order-permutation regression test added. currency:: suite 10/10 pass on main-merged head; PR checks green (production-line-guard SUCCESS, CodeRabbit NEUTRAL). Labels cleared to terminal state.
    tests: cargo test --manifest-path pkgs/id/Cargo.toml --lib currency:: — 10 passed, 0 failed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

app/idPackage/app: id (pkgs/ or apps/ in this repo)effort/sSmall: expected under 2 hours of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/normalOrdinary priority (the default)source/creatorFiled by the new-issue-creator jobtask/triageQueue packet kind: verify, reproduce, and route this item

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions