Skip to content

id(currency_poc): conversion preflight never checks same-account legs — user==LP, fee==user, LP-dest==user-dest pass prepare, fail in submit, and strand a Submitted intent #4124

Closed

Description

@dezren39

Problem

ConversionExecutor::preflight (pkgs/id/src/currency_poc/executor.rs:138-194) never checks that the accounts taking part in a conversion are distinct where a leg requires two different accounts. Every leg is a transfer, and the ledger rejects any transfer whose debit and credit accounts are the same (TransferError::AccountsMustBeDifferent, ledger.rs:172-174), but preflight only checks existence and ledger for each role (executor.rs:149-168) and the two balance checks. So a request with

  • user_source == lp_source (T1 debits and credits the same account), or
  • fee_account == user_source (the fee leg debits and credits the same account), or
  • lp_dest == user_dest (T3 debits and credits the same account)

passes preflight, prepare() persists the write-ahead intent, and submit() fails mid-submission — leaving the record in state Submitted, unresolved forever: the recovery scanner can only resubmit the same doomed legs (ResubmissionRejected) or abandon a conversion that should never have been recorded. This breaks preflight's own contract (executor.rs:132-136): "Failing here means no intent is written and nothing is submitted." These configurations are statically knowable before any write.

Evidence (verified this run, laptop probe, main caacf3a)

Probe crate with verbatim copies of the currency_poc modules (md5 executor.rs d12b3b971441cb271cec2abc9ada0798, matching main caacf3a), funded-ledger fixture and request copied from tests.rs:

  • lp_source = user_source: prepare() returns Ok(Recorded) and the intent is persisted; submit() errors AccountsMustBeDifferent; the stored record stays Submitted (unresolved).
  • fee_account = Some(user_source): same outcome — prepare() Ok, submit() errors, record stuck Submitted.
  • Control: the unmodified request execute()s to Posted — the fixture is sane.

Why it matters

The write-ahead intent is the crash-recovery contract of the PoC (#2289): anything in Recorded/Submitted is owed a reconciliation. Letting a deterministically-invalid conversion into that store pollutes it with records no recovery policy can ever post, and in a production-shaped successor the same gap means a misconfigured conversion (e.g. an LP configuration that aliases the user account) is only discovered after the intent is durable and submission has been attempted.

Acceptance criteria

  • preflight (or build_legs) rejects any request where a single leg's debit and credit account coincide: user_source == lp_source, fee_amount > 0 && fee_account == Some(user_source), and lp_dest == user_dest — with an error naming the conflicting roles.
  • A rejected request writes no intent record and submits nothing (store lookup for the conversion ID returns none after prepare() errors).
  • New tests cover all three coincidences plus a control showing a normal request (with and without a fee leg) still prepares and posts.
  • All existing currency_poc tests pass unchanged.

Related: #2289 (the PoC this executor belongs to). Distinct from #4071 (RateAuthorisation body validation in pkgs/id/src/currency.rs, a different module).

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    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
    priority/normalOrdinary priority (the default)
    app/idPackage/app: id (pkgs/ or apps/ in this repo)
    effort/sSmall: expected under 2 hours of work
    on Nov 2, 2026
  2. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

    • Agent: new-issue-creator job (lane A, new-issue-creator-code-a)
    • Session / ID: cron run 2026-11-01 20:05 CST (2026-11-02T02:05Z)
    • Found via: filed this run
    • Task: Filed issue id(currency_poc): conversion preflight never checks same-account legs — user==LP, fee==user, LP-dest==user-dest pass prepare, fail in submit, and strand a Submitted intent #4124 — conversion preflight never checks same-account legs: user_source==lp_source and fee_account==user_source pass prepare() (intent persisted), then submit() fails AccountsMustBeDifferent and the record strands in Submitted. Verified this run on the laptop with verbatim currency_poc modules from main caacf3a (md5 executor.rs d12b3b971441cb271cec2abc9ada0798); normal-request control executes to Posted.
    • Expectation: effort/s — preflight/build_legs rejects the three same-account coincidences (T1, fee leg, T3) naming the roles, writes no intent on rejection, plus tests; existing currency_poc tests unchanged.
    • Links: probe ~/git/fleet/probe-2026-11-01-laneA-currencypoc/ (laptop); queue packet enqueued this run (repo-health-code-pilot).
  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. 29 remaining items

  5. added
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    status/parkedDeliberately paused with a recorded resume condition
    and removed
    queue/claimedQueue worker lifecycle state
    on Nov 2, 2026
  6. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    agent-note:v1 — state=parked; task_id=6330; worker=repo-worker-code; lane=c29174f1-48e0-4b13-b4ff-9b73ab67e9ab; at=2026-11-02T20:47:54Z. Freshly verified issue #4124 and PR #4520 are OPEN; PR #4520 is authored by dezren39 (Drewry Pope), with the basic guard QUEUED and CodeRabbit NEUTRAL. This lane did not inspect or modify the PR and did not act on CI. Task parked for 30 minutes; resume only after PR #4520 is no longer Drew-owned/open or Drew confirms this lane may proceed.

  7. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    Agent note — picked up (OpenChamber scheduled lane)

  8. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    Agent note — parked (OpenChamber scheduled lane)

  9. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    agent-note:v1
    event: picked_up
    task: 6330 (repo-health-code-pilot; worker repo-worker-code; OpenChamber scheduled lane 02cafe76-eafd-4d0b-aaa6-2b7a737d68dc)
    fresh evidence: issue #4124 OPEN, no hard-stop labels; PR #4520 OPEN, authored by dezren39 (Drew), branch queue/6330-4124-same-account-preflight; basic guard pass, CodeRabbit skipping.
    action: claim revalidated; no inspection, modification, or merge of the Drew-owned PR per protocol.

  10. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    agent-note:v1
    event: parked
    task: 6330 (repo-health-code-pilot; worker repo-worker-code; OpenChamber scheduled lane 02cafe76-eafd-4d0b-aaa6-2b7a737d68dc)
    reason: PR #4520 remains OPEN and Drew-authored; fleet must not touch/merge it. Parked for 30 minutes.
    resume: after Drew releases/merges PR #4520 or ownership is explicitly reassigned; revalidate PR/check state and targeted-test evidence.

  11. added a commit that references this issue on Nov 2, 2026
  12. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done

    Merge evidence, all fresh at merge time: PR #4520 head bff2434; basic: OpenCode metered-provider guard PASS; Codex review at that head completed with has_any_findings: false; targeted tests from the implementing lane — cargo test --manifest-path pkgs/id/Cargo.toml --lib currency_poc 24 passed, 0 failed, 1 ignored; diff limited to pkgs/id/src/currency_poc/executor.rs + tests.rs. Post-merge verification on main: the three preflight rejections are present at executor.rs:144 (user/LP source), :150 (fee/user source), :156 (LP/user destination).

  13. removed
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    work/assignedQueue worker lifecycle state
    on Nov 2, 2026
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