Skip to content

Storage TTL policy, outstanding-share finalization, TWAP accumulator, and batched redemption (#16, #14, #11, #15) - #156

Open
Teescom wants to merge 10 commits into
OracleDesk:developfrom
Teescom:wave/ttl-outstanding-twap-batch
Open

Teescom wants to merge 10 commits into
OracleDesk:developfrom
Teescom:wave/ttl-outstanding-twap-batch

Conversation

@Teescom

@Teescom Teescom commented Oct 16, 2026

Copy link
Copy Markdown

What this does

Implements four scheduled issues, one commit per issue, based on develop @ 67faa89:

commit issue
79efc79 #16 — close-time-aware storage TTL policy (all four contracts)
f016b98 #14 — outstanding-share tracking + finalize_market residual sweep
0aec7d3 #11 — cumulative-price TWAP accumulator + agent PRICE_SOURCE=twap
99da493, 460c22a #15 — batched redemption + measured MAX_BATCH
56bbf26, ff7724b regen housekeeping (lockfiles kept at develop's bytes; resolver/reasoning-registry bindings brought current for #16's spec docs)

Closes #16
Closes #14
Closes #11
Closes #15

Checklist

  • cargo fmt --check and cargo clippy --workspace --all-targets -- -D warnings pass
  • cargo test --workspace passes
  • If contract interfaces changed: bindings regenerated (make bindings) and included in this PR
  • If market-core's interface changed: resolver/treasury's vendored market_core.wasm refreshed
  • New behavior has a test; changed behavior's test was updated, not deleted
  • docs/STATUS.md updated if this changes what's verified/unverified

Gates (run locally on the final tree, unpiped)

$ cargo fmt --check                                         # exit 0
$ cargo clippy --workspace --all-targets -- -D warnings     # exit 0
$ cargo test --workspace
    market-core         51 passed      reasoning-registry   7 passed
    resolver             9 passed      treasury            14 passed
    → 81 passed, 0 failed (3 ignored measurement harnesses)
$ python3 model/fpmm_model.py   → all property checks passed
$ (cd x402 && npm test)         → 5 pass / 0 fail,  tsc clean
$ (cd agents && npm test)       → 28 pass / 0 fail, tsc clean

CI-equivalent drift checks, run exactly as the workflows steps do:

$ stellar contract build && cp target/wasm32v1-none/release/market_core.wasm \
    contracts/resolver/market_core.wasm contracts/treasury/market_core.wasm && stellar contract build
$ git diff --exit-code -- contracts/resolver/market_core.wasm contracts/treasury/market_core.wasm
    → exit 0 (vendored wasm matches a fresh build)
$ ./scripts/gen-bindings.sh
$ git status --porcelain -- packages/bindings
    → only the four package-lock.json deletions the script's --overwrite causes
      (pre-existing trap in scripts/gen-bindings.sh, fixed by the open PR #153;
      this branch keeps the lockfiles at develop's bytes and does not touch the script)
    → src/index.ts is current in all four packages

Note: the nested oracledesk-stellar/.github/workflows/ci.yml does not run on GitHub (only repo-root workflows do), so no check runs are expected on this PR — the block above is that workflow's full job set, replicated locally with stellar CLI 27.1.0 (the CI pin).


#16 — close-time-aware storage TTL policy

Four archival defects shared one root cause (entries living on a fixed 60-day bump or a deploy-time default instead of per-market needs):

  • market-core bumped Market/Position to a flat 60 days regardless of close_time — a market closing further out archived before it could be resolved, and the resolver's get_market cross-call then failed until someone paid for a restore.
  • resolver / treasury / reasoning-registry never extended instance storage, so Admin/MarketCore/Config/Daily/NextId lived only as long as the deploy default; an archived instance makes the whole contract uncallable.
  • resolver extended only Price/Signers; Attestation/Count/Proposal/State were never extended — and a missing Count reads as 0 via unwrap_or(0), silently resetting an in-progress signer tally.
  • registry extended Agent only at register_agent, so an agent publishing for 60+ days was archived and publish_trace failed with AgentNotRegistered.

Each contract now carries an identical src/ttl.rs (duplicated because contract crates cannot path-depend on each other — ADR 0002): per-market entries target close_time + 90-day settlement horizon, clamped to max_ttl(); threshold = target/2 so a frequently touched entry is rarely rewritten; instance target 30 days / threshold 7. All six enumerable resolver keys are extended has()-guarded in bump_market_entries; treasury exposure entries are keyed to close_time; the registry refreshes Agent on every successful publish_trace; state() stays a pure view.

Rent cost comparison (old vs new policy) — measured

Same harness, same call, three builds (one 1-USDC buy at ts=1000 on a market created identically, closing 10,000,000 s ≈ 115.7 days out; the committed measure_buy_resources harness, run with --ignored --nocapture):

build bump for this market persistent_rent_ledger_bytes bumps
67faa89 — pre-#16 flat 60 days = 1,036,800 ledgers 219,801,600 1
79efc79 — #16 until close + 90d = 3,555,000 ledgers (205.7 days) 753,660,000 1

The bumped entry is 212 bytes: 212 × 1,036,800 = 219,801,600 vs 212 × 3,555,000 = 753,660,000 — ×3.43. The test environment's max_entry_ttl (6,312,000 ledgers) does not bind here; on mainnet the same market clamps to max_ttl() = 3,110,400 ledgers (180 days), i.e. ×3.0.

That multiplier is the price of actually coming due: the old 60-day cover cannot reach a market that closes beyond 60 days — archival then blocks resolution until a restore operation (which itself rents the restored TTL). Steady state is unchanged where the old policy did work: extend_ttl is a no-op while remaining TTL ≥ target/2 (both rows above are exactly 1 bump — a freshly touched entry), so a market touched at least once per half-target pays only on real re-extensions. Instance storage: old extended to 60 days on every instance read with a 30-day threshold; the new 30/7 pair rents half the ledgers per extension and bounds a quiet contract's pre-paid cover at 30 days.

Full constants and the CAP-0066 rent formula are in the ttl.rs module header (identical in all four crates); the policy is documented in docs/architecture.md § Storage TTL policy, linking docs/wave-issues/14-storage-ttl-keeper.md.

Regression tests advance ledger sequence and timestamp together and assert TTLs through testutils, including signer_vote_entries_survive_dispute_period (a signer tally survives 61 days of silence — the silent-reset bug), market_entry_survives_until_close_plus_horizon, position_ttl_tracks_close_time, instance_survives_after_long_idle, bump_market_entries_skips_missing_keys, instance_ttl_extended_on_agent_buy, agent_entry_extended_on_publish.


#14 — outstanding shares + finalize_market

  • SettleState side entry per market tracks outstanding_yes/no, finalized_at, horizon_secs, paid_out, closed; the Market XDR is untouched (side-entry design, same as Add a cumulative-price TWAP accumulator to market-core for manipulation-resistant reads #11 below).
  • The reserve_<side> + outstanding_<side> == sets_minted invariant is asserted in tests after each operation type.
  • finalize_market is permissionless and deadline-gated: before the deadline it reverts (RedemptionWindowOpen); after it, it sweeps the market's entire remaining balance — expired claims, void rounding dust, unclaimed LP residue — in one transfer, proven by a balance-delta test (finalize_after_deadline_sweeps_exact_residual).
  • Pre-deadline redeemers receive identical payouts with or without later finalization (redeem_before_deadline_never_underpaid).
  • After finalization, redeem / claim_pool_remainder revert with RedemptionClosed; a second finalize_market also reverts.
  • Legacy markets (created before the SettleState bookkeeping) are explicitly unsupported for finalization (NotSupported, tested via legacy_market_cannot_finalize) and stay redeemable through the old paths (legacy_market_still_redeems_without_deadline).
  • The admin horizon setter is bounded (180–1825 days, snapshot-tested) with a wrong_caller_cannot_set_default_redeem_horizon test; sell now uses checked_sub on sets_minted.
  • docs/architecture.md and docs/STATUS.md describe the deadline and the dust sweep.

#11 — TWAP accumulator, and the buy deltas

Design: a Uniswap-v2-style cumulative-price accumulator updated in buy/sell before reserves change, with same-timestamp semantics handled exactly as the v2 oracle does; a bounded 16-slot observation ring stored as side entries (Market XDR unchanged); observe, twap and price_cumulative views with interpolation and ObservationTooOld; overflow analysis documented in code and exercised by a simulated 10-year-lifetime test; a single-ledger manipulation test proving twap is unaffected by one trade. docs/frontend-integration.md documents the reads and their caveats (observation coverage, counterfactual extension after the last trade, NotSupported for pre-accumulator markets); agents read PRICE_SOURCE=twap through readMarket with unit tests (.env.example documents the knob).

CPU and write-byte deltas for buy (required by the issue)

Same test body (measure_buy_resources, committed) on the parent build f016b98 and on this branch; the call is a 1-USDC buy at a new timestamp — the full accumulator write path (worst case; same-timestamp trades only update in place):

metric f016b98 (parent) this branch delta
instructions 455,584 569,672 +114,088 (+25.0%)
mem_bytes 65,283 80,668 +15,385
memory_read_entries 10 12 +2
write_entries 6 8 +2
write_bytes 1,712 2,156 +444 B (+25.9%)
contract_events_size_bytes 620 620 0
persistent_rent_ledger_bytes 753,660,000 1,379,340,000 +625,680,000

The rent delta is the new observation ring entry being bumped alongside the market (bumps 1 → 2) at the shared close-time-aware target; the accumulator adds no event payload. In absolute terms the whole buy costs 569,672 instructions — 0.14% of the 400M mainnet budget — and 2,156 write bytes (1.6% of the budget) per new-timestamp trade.

#15 — batched redemption, MAX_BATCH measured

  • redeem_many(market_ids) and claim_pool_remainders(market_ids) take ascending, deduplicated ids (else InvalidAmount), reject empty/over-cap batches (BatchTooLarge), skip per-market problems with explicit reasons (SkipReason::{NotFound, NotFinal, NoPosition, NotCreator, AlreadyClaimed}) instead of aborting the call, and pay via one aggregated transfer per call — balance-delta test proves after − before == result.total and result.total == Σ item payouts.
  • Per-market Redeemed / PoolRemainderClaimed events are emitted identically from the single-market entrypoints; mixed settlement kinds (Yes/No/void) are tested in one batch; open markets are skipped; Settle/Position bookkeeping is the same code path as single redeem.
  • Treasury forwarders redeem_many / claim_pool_remainders exist and are agent-gated (wrong_caller tests for both, plus a cap-sized success test through the treasury at max_batch()).
  • docs/frontend-integration.md documents chunking via the new max_batch() view, simulating before signing, and restoring archived entries in a separate transaction.

MAX_BATCH = 46, backed by the committed measurement table

Measured with the committed measure_redeem_many_resources harness; mainnet limits are the SDK's 2026-07-10 snapshot (InvocationResourceLimits::mainnet(), enforced by the test env). Direct market-core path, N redeemed markets:

   N |    insns |    mem B | reads | writes | footprint | write B | events B
   1 |  409,515 |   55,861 |    10 |      5 |        15 |     840 |      404
   5 |1,258,619 |  163,347 |    22 |     13 |        35 |   2,120 |    1,076
  10 |2,586,602 |  355,282 |    37 |     23 |        60 |   3,720 |    1,916
  20 |6,133,201 |  931,077 |    67 |     43 |       110 |   6,920 |    3,596
  30 |10,687,968|1,762,772 |    97 |     63 |       160 |  10,120 |    5,276
  40 |16,308,578|2,850,367 |   127 |     83 |       210 |  13,320 |    6,956
  45 |19,448,071|3,490,127 |   142 |     93 |       235 |  14,920 |    7,796
  46 |20,093,044|3,625,756 |   145 |     95 |       240 |  15,240 |    7,964

Treasury wrapper path at the same N=46: 147 reads, 95 writes, 242-footprint, 33.1M insns, 15,240 write B, 7,964 event B (the wrappers add a constant two read entries and no extra writes).

At 46 the binding axis (footprint, 5N+10) is 240/400 = 60% of the ledger-entry limit; write entries 95/200 = 47.5%; events 7,964/16,384 = 49%; instructions 5–8%, memory 9–13%. Worst case, if every touched entry had to be restored from archived state, the 3N+7 reads would be 147/200 = 73.5% on the treasury path (145/200 = 72.5% direct) — under the 75% headroom line with room for tx overhead the harness cannot see (auth entries, source account). N=47 measures exactly 150/200 = 75.0% with zero margin, so 46 is the largest defensible cap (the issue's "50" was an illustration; this is the measured answer). The table and this rationale live in the MAX_BATCH doc comment; max_batch() exposes the cap so frontends chunk trustlessly instead of hardcoding it.

Deviations from the issue text (deliberate, documented in code)

  1. BatchResult.items uses BatchStatus::Settled | Skipped(SkipReason) instead of a struct with skip_reason: Option<SkipReason> — Option<T> inside a #[contracttype] struct does not compile in this workspace's test setup (the SDK 27 derive hits E0117/E0119 via testutils). The union carries identical information.
  2. Error numbering starts at 23 (ObservationTooOld 23, RedemptionWindowOpen 24, RedemptionClosed 25, NotSupported 26, BatchTooLarge 27): the open PR feat: fees, breakers, trade bounds, transfers + migration (#8 #10 #12 #13) #153's branch introduces its own codes 19–22. The sets are disjoint — whichever lands second keeps its own numbering, and only the generated bindings embed the values.
  3. MAX_BATCH is 46, not the issue's illustrative 50 — reason above.

Notes for the reviewer

  • Vendored market_core.wasm (26,137 → 47,533 bytes in contracts/resolver/ and contracts/treasury/) was rebuilt with stellar CLI 27.1.0 (the CI pin; homebrew's 28.x reorders spec entries) and the drift check above exits 0.
  • packages/bindings/*/package-lock.json files are byte-identical to develop's (see 56bbf26): regenerating bindings npm-installs the package, which churns them with registry drift that has nothing to do with the interface change.
  • ff7724b regenerates resolver/reasoning-registry bindings — Make storage TTL extension close-time-aware and add missing instance TTL bumps #16 touched doc comments on contractimpl methods, and those comments are part of the contract spec, so the committed bindings for those two packages were stale without it (found by running the CI job's own gen-bindings.sh step against the branch build).
  • New public surface for downstream consumers: error codes 23–27; views max_batch, observe, twap, price_cumulative, redeem_deadline; types SettleState, BatchResult/BatchItem/BatchStatus/SkipReason; entrypoints finalize_market, redeem_many, claim_pool_remainders (also mirrored on treasury), set_default_redeem_horizon.
  • docs/architecture.md (TTL policy + settlement design), docs/STATUS.md (deadline/dust sweep, TWAP, batch) and docs/frontend-integration.md (batch chunking + TWAP caveats) are updated in the same commits.

Teescom added 7 commits October 16, 2026 14:22
…cleDesk#16)

Soroban archives entries when their TTL lapses, and an archived entry is
unreadable until restored; an archived instance entry makes a contract
uncallable. Four defects shared that root cause:

- market-core bumped Market/Position to a fixed 60 days regardless of
  close_time, so a market closing further out could archive before it could
  be resolved and the resolver's get_market cross-call would fail.
- resolver/treasury/reasoning-registry never extended instance storage, so
  Admin/MarketCore/Config/Daily/NextId lived only as long as the deploy-time
  default TTL.
- the resolver extended only Price/Signers; Attestation/Count/Proposal/State
  were never extended, and a missing Count reads as 0 via unwrap_or(0) —
  silently resetting an in-progress signer tally.
- the registry extended Agent only at register_agent, so an agent publishing
  for 60+ days was archived and publish_trace failed with AgentNotRegistered.

Each contract now carries an identical src/ttl.rs (duplicated because
contract crates cannot path-depend on each other — ADR 0002) with the shared
policy: per-market entries target close_time + 90-day settlement horizon,
clamped to max_ttl(), threshold = target/2 so hot paths rarely rewrite;
instance target 30 days, threshold 7 days.

- market-core: Market/Position bumps use ttl::market_target(close_time).
- resolver: instance bumped via instance_get; all six enumerable per-market
  keys extended has()-guarded in bump_market_entries after every mutation;
  Attestation extended where written and, for configured signers, in
  finalize_signers; state() stays a pure view.
- treasury: instance bumped wherever instance storage is read; exposure
  entries keyed to close_time (create-market arg, or a get_market read).
- reasoning-registry: instance bumped on NextId access; Agent uses the
  long-lived target and is refreshed by every successful publish_trace.
- docs/architecture.md documents the policy and links the keeper doc.

Regression coverage: signer_vote_entries_survive_dispute_period (tally
survives 61 days of silence), market_entry_survives_until_close_plus_horizon,
position_ttl_tracks_close_time, instance_survives_after_long_idle,
bump_market_entries_skips_missing_keys, instance_ttl_extended_on_agent_buy,
agent_entry_extended_on_publish — all advancing sequence and timestamp
together and asserting TTLs through testutils.

Vendored wasm refresh and bindings regeneration land in the final commit of
this branch (one refresh for all four issues).
…ims residual collateral (OracleDesk#14)

A final market's collateral could sit in market-core forever: nobody could
tell 'fully settled' from 'waiting on holders' because there was no running
total of user-held shares, and void per-holder floors left rounding dust
unclaimable by design.

- SettleState side entry (DataKey::Settle(u64)): outstanding_yes/no,
  finalized_at, horizon_secs, paid_out, closed. Maintained by create_market,
  buy, sell (now checked_sub on sets_minted), redeem (both sides decrement,
  even at payout 0) and claim_pool_remainder; resolve/void_market stamp
  finalized_at.
- Per-market redemption horizon snapshotted at creation from an admin
  default (365 days, bounded to 180..=1825 days via
  set_default_redeem_horizon).
- finalize_market: permissionless after finalized_at + horizon; reserves
  settle_upper (aggregate floor >= sum of per-holder void floors, proven in
  math.rs and the Python model) plus any unclaimed LP remainder, then sweeps
  the market's entire remaining balance - expired claims, dust, remainder,
  fees - to the treasury and closes the market. Redeem/claim after close
  return RedemptionClosed; legacy markets without a Settle entry return
  NotSupported and stay redeemable.
- Views settle_state / redeem_deadline; MarketFinalized event;
  debug_assert of reserve+outstanding==sets on pre-payout transitions.
- Docs: architecture.md lifecycle + TTL interaction, STATUS.md limitation.
  Treasury redeem() doc notes the agent must redeem before the deadline.
…eads (OracleDesk#11)

Spot price alone is a single-trade-grabbable reference; anything automated
(agents, frontends) needs a price that requires holding a distortion.

- Per-market accumulator (Uniswap-v2 semantics): every buy/sell folds the
  pre-trade price into price_cumulative before reserves move. Same-timestamp
  trades only refresh the price - nothing accumulates within one ledger, so
  moving the TWAP requires holding the price across a ledger boundary.
- State lives in Oracle(id) and a 16-slot ring of Obs(id, k) entries, so the
  Market entry's XDR is unchanged and existing readers/decoders keep working.
- Views: observe(id, seconds_ago), twap(id, window_secs) (floor division) and
  price_cumulative(id) (= observe at now). Between bracketing observations
  the cumulative is chord-interpolated (exact - piecewise-linear between
  trades); past the newest observation the last price is extended
  counterfactually; earlier than the surviving ring returns ObservationTooOld;
  legacy markets return NotSupported.
- Overflow: checked i128 arithmetic throughout; the 10-year alternating-trade
  test bounds the accumulator at elapsed * MAX_PROB_BPS, ~24 orders of
  magnitude below i128::MAX.
- Oracle/Obs entries share the market's close-time-aware TTL target (OracleDesk#16);
  unwritten ring slots are never written and missing slots are skipped.
- agents: MarketState.twapBps and decideTrade({priceSource: "spot"|"twap"}),
  readMarket() in the stellar adapter, trader PRICE_SOURCE/TWAP_WINDOW_SECS
  env knobs, twapFromObservations() BigInt mirror + tests.
- Bindings regenerated; architecture.md, STATUS.md and
  frontend-integration.md document the semantics and caveats.
…n cap (OracleDesk#15)

redeem_many / claim_pool_remainders settle up to MAX_BATCH (46) markets in
one call, paying the total in a single token transfer. Validation rejects
empty, over-cap and non-ascending id lists; markets that cannot pay out
right now come back as per-item skips instead of aborting the batch, and
the per-market Redeemed / PoolRemainderClaimed events let the frontend
reconcile receipts without an indexer.

The cap is backed by a committed measurement table (see the MAX_BATCH
docstring; regenerate with `cargo test -p market-core
measure_redeem_many_resources -- --ignored --nocapture`): the direct call
measures 145 reads / 95 writes / 240 footprint at N=46, and the treasury
wrappers add exactly two read entries (147 / 95 / 242). 46 keeps the
worst-case disk-read interpretation at 147/200 = 73.5% of the mainnet
limit, under the 75% headroom line; N=47 would sit at exactly 75%.

Deviation from the issue sketch: `BatchItem.skipped: Option<SkipReason>`
does not compile as a `#[contracttype]` field — under the `testutils`
feature the derive needs `From<SkipReason> for ScVal`, which is unwritable
from this crate (E0119 against the derive's own TryFrom via core's blanket
impl; the orphan rule blocks impls on Option, E0117). The equivalent
`BatchStatus { Settled, Skipped(SkipReason) }` union is used instead and
documented on the type. Error codes continue at 23-27, reserving 19-22 for
the parallel fees/breakers PR OracleDesk#153.

Treasury: agent-gated redeem_many / claim_pool_remainders forwarders that
act for the treasury's own positions, with cap-sized and wrong-caller
tests. Bindings regenerated; market-core wasm re-vendored into
resolver/treasury; frontend docs gain the chunking, simulate-before-sign
and archived-entry-restore rules.
Regenerating the bindings npm-installs the package, which churned the
lockfile's root name (@oracledesk/bindings-market-core vs the stale
market-core) and bumped three transitive versions off registry drift.
None of that is required by the interface change, so the lockfile goes
back to develop's exact bytes; only src/index.ts carries the regen.
…k#16)

OracleDesk#16's TTL work touched doc comments on contractimpl methods, and those
comments are part of the contract spec — so freshly generated bindings
for these two packages carry new doc text (and the spec-entry order the
pinned stellar CLI 27.1.0 emits). market-core and treasury were already
regenerated with the same CLI earlier in this branch.

Found by running scripts/gen-bindings.sh (the command the nested CI
job runs) against the branch's built wasm.
@drips-wave

drips-wave Bot commented Oct 16, 2026

Copy link
Copy Markdown

@Teescom Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Teescom added 3 commits October 18, 2026 12:24
# Conflicts:
#	oracledesk-stellar/agents/bin/trader.ts
#	oracledesk-stellar/agents/core/strategy.test.ts
#	oracledesk-stellar/agents/core/strategy.ts
#	oracledesk-stellar/contracts/market-core/src/lib.rs
#	oracledesk-stellar/contracts/market-core/src/math.rs
#	oracledesk-stellar/contracts/resolver/market_core.wasm
#	oracledesk-stellar/contracts/resolver/src/lib.rs
#	oracledesk-stellar/contracts/treasury/market_core.wasm
#	oracledesk-stellar/contracts/treasury/src/lib.rs
#	oracledesk-stellar/docs/STATUS.md
#	oracledesk-stellar/docs/architecture.md
#	oracledesk-stellar/docs/frontend-integration.md
#	oracledesk-stellar/model/fpmm_model.py
#	oracledesk-stellar/packages/bindings/market-core/src/index.ts
#	oracledesk-stellar/packages/bindings/resolver/src/index.ts
#	oracledesk-stellar/packages/bindings/treasury/src/index.ts
Reconciles the four-issue batch (OracleDesk#16 close-time-aware TTL, OracleDesk#14 outstanding
shares + finalize_market, OracleDesk#11 cumulative-price TWAP, OracleDesk#15 batch redemption)
with develop's resolver AttestedOutcome migration and treasury rewrite.

Resolution notes:
- resolver: keep develop's AttestedOutcome/V2 data model and all of its
  tests; re-apply the OracleDesk#16 close-time-aware TTL bumps, and extend
  bump_market_entries to sweep the CountV2/ProposalV2/Snapshot keys that
  replaced the legacy Count/Proposal in the migration.
- treasury: adopt develop's bucketed-usage / agent-scoped-auth model and
  re-graft OracleDesk#15's redeem_many + claim_pool_remainders as thin delegates so
  the batch surface stays on the treasury contract.
- market-core / reasoning-registry: take the union of both sides.
Six of the twenty-one rustfmt hunks are this branch's own code (two in
market-core, four in resolver); the remaining fifteen are drift already on
develop in resolver/treasury, which rustfmt normalizes because the gate is
per-file. reasoning-registry is excluded: it does not parse on develop.
@Teescom

Teescom commented Oct 21, 2026

Copy link
Copy Markdown
Author

Merge conflicts resolved — remaining CI red is pre-existing on develop, not from this branch

Status: conflicts resolved, PR is now mergeable: true.

  • 37e05a2 — merge of develop @ d75e2fa
  • 04f9845 — cargo fmt on the three contract files this branch touches

How the conflicts were resolved

  • contracts/resolver/src/lib.rs — kept develop's AttestedOutcome / CountV2 /
    ProposalV2 data model and all of develop's tests, and re-applied Make storage TTL extension close-time-aware and add missing instance TTL bumps #16's close-time-aware
    TTL bumps on top. One thing worth flagging: bump_market_entries swept only the legacy
    Count/Proposal keys, which the AttestedOutcome migration replaced with CountV2/
    ProposalV2 — so the live tally keys were not being TTL-extended at all. This branch now
    sweeps CountV2 (Yes/No/Invalid), ProposalV2 and Snapshot.
  • contracts/treasury/src/lib.rs — adopted develop's rewritten bucketed-usage /
    agent-scoped-auth model wholesale rather than carrying our treasury-side TTL copy, and
    re-grafted Add resource-bounded batch redemption across many markets to market-core #15's redeem_many + claim_pool_remainders as thin agent-authenticated
    delegates into market-core.
  • contracts/market-core/src/lib.rs, reasoning-registry/src/lib.rs — union of both sides.
  • market-core error codes for this branch were renumbered to 27–31 to avoid colliding with
    codes filled in on develop since the branch was cut.

Why CI is still red — five defects already on develop

All of the following were reproduced on an untouched checkout of d75e2fa (i.e. they are
not introduced by this merge). develop's own push runs fail today:
CI run 39600436862,
plus Coverage and Local E2E on the same commit.

  1. The pinned toolchain is older than its own dependencies require.
    oracledesk-stellar/rust-toolchain.toml pins channel = "1.90.0" (issue Replace vendored market_core.wasm with a deterministic build and hash-pinned import #58, for
    bit-reproducible vendored wasm), but CI step 5 runs
    cargo install --locked stellar-cli --version 27.1.0, which needs rustc ≥ 1.93.0:
    error: cannot install package stellar-cli 27.1.0, it requires rustc 1.93.0 or newer, while the currently active rustc version is 1.90.0.
    soroban-sdk 27.0.6 in the lockfile separately needs ≥ 1.91. This is where CI dies — before
    compiling anything. Note rust-toolchain.toml overrides dtolnay/rust-toolchain@stable for
    any cargo invocation inside oracledesk-stellar/.
  2. scripts/verify-wasm-hash.rs cannot be include!d as written. It opens with 14 //!
    inner-doc lines and is include!d by both contracts/resolver/build.rs and
    contracts/treasury/build.rs → error[E0753]: expected outer doc comment ×14.
  3. develop's vendored wasm contradicts its own hash pin.
    contracts/resolver/market_core.wasm and contracts/treasury/market_core.wasm hash to
    ec19a91eb4cdf6a1bd9aee21442d77d330c4e2f994556ad75813cd030eb90dfa, but the committed
    market_core.wasm.sha256 says a3c539eb6de113f0988a8d208f92d92296bcbf91ad519fa1f1a7882b83a028a3.
    Verified against the git blob, so it is not a checkout/smudge artifact. Once (2) is fixed
    the build script panics on this.
  4. contracts/reasoning-registry/src/lib.rs does not parse. unexpected closing delimiter
    at line 948. A develop merge commit fused functions together: the impl block closes early
    at line 867; publish_trace_batch's signature (line 871) is glued to a validation helper's
    body (it checks action and ipfs_cid, which are not among its parameters, and returns
    Ok(())); and the real batch body is orphaned at module level (lines 895–948) with
    publish_trace's Add paginated multi-key trace queries to reasoning-registry #57 secondary-index lines and the TracePublished event spliced into it.
    Bisected: the file parses at 5a65370 and is broken at 2fd69a7 / 4ef7271
    ("Merge branch 'develop' into feat/multi-56-55-54-53"). develop has therefore been
    unbuildable since 2026-10-19.
    Repairing it means re-doing that five-feature merge, which
    belongs to the registry's authors — deliberately not attempted here.
  5. develop is cargo fmt --check red on its own: 15 hunks (resolver 13, treasury 2),
    identical under rustfmt 1.90 and 1.93, so not a toolchain-version artifact.

Also worth noting: the committed Cargo.lock is stale — cargo check --locked refuses with
"cannot update the lock file … because --locked was passed".

What this means for review

This branch's own code is formatted and its conflicts are resolved; no commit here touches
items 1–5, since they live in files outside the scope of #16/#14/#11/#15. Any PR into develop
will be red until the base is repaired. Happy to take any of these in a separate PR if a
maintainer would like them fixed — the first three are mechanical, item 4 needs the
reasoning-registry owners.

For reference, this fork's workflow runs park in action_required until approved, so the
checks on this head have not actually executed yet.

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