Repository navigation
Description
id(attestation): compute_state never validates the claim outcome's domain — claims naming a third party (or any string) as winner compute Agreed
Evidence (main 07448571, merged today in PR #2854 for #2248)
pkgs/id/src/attestation.rs documents the claim outcome's domain on ResultClaim.outcome (:106-108): "the winning player's id as hex, or "draw"". But the viewer checks concurrence only:
compute_state(:223) accepts two claims whena.outcome == b.outcome && a.final_state_hash == b.final_state_hash(:253) — a pure string-equality check. Neither claim's outcome is ever compared against the agreement's two player ids (hex) or the"draw"sentinel.- So any outcome string both players sign — the hex of a key that is not in the match, or arbitrary text — computes
Agreed, even though no such result is possible for the tic-tac-toe example the PoC models (the winner is one of the two seats, or a draw).
Distinct from #2863 (duplicate seats): there the seats collapse to one key; here the seats are two genuine, distinct players and the genesis pair is complete — the gap is purely that the asserted result is outside the documented domain.
Verified (laptop probe, verbatim compute_state + object types, real p2panda-core Hash at the pinned rev da80c05b)
Probe: ~/git/fleet/probe-2026-10-30-laneB-attdup/ on the laptop (same verbatim extraction as #2863; attestation.rs md5 530fcdf45da5d8688dc0e4e319b641b2), distinct players [A, B], complete genesis pair, concurring claims throughout:
- both claims name third party C (hex of a key that is not a player) as winner →
Agreed← the bug - both claims give outcome
"banana"→Agreed← the bug - both claims give outcome
"draw"→Agreed(control: the documented non-player outcome stays valid) - both claims name A (a seat player) →
Agreed(control, from the id(attestation): compute_state never checks the two players are distinct — players=[A,A] attests Agreed from one key's single genesis + single claim #2863 probe run)
The shipped tests (tests/attestation_poc.rs) only ever use seat-player hex outcomes and the wrong-winner dispute case (which names the other seat), so the out-of-domain case is uncovered.
Why it matters
- The viewer's
Agreedis the attestation: downstream consumers (the id: design: match attestation + audit v1 #2039 v1 design, audit/replay in id: investigate: match audit — netcode log format, replay validation, and cheating signals #2038's line) will read it as "both players conceded this result". An out-of-domain outcome — above a nonsense string, a result naming someone who never played — should not be attestable at all; if it computesAgreed, every consumer must re-validate the outcome itself, which is exactly the invariant a viewer exists to own. - It also masks claim-construction bugs: a claimant whose outcome encoding is wrong (raw bytes instead of hex, a display name, a seat index) produces claims that can still concur and attest, instead of failing visibly at the viewer.
Acceptance criteria
- Two concurring claims whose outcome is neither a seat player's id-as-hex nor
"draw"do not computeAgreed(they computeProposedorDisputed— triage pins which, and whether a claim with an out-of-domain outcome is ignored like a non-player claim or counts as a position). - Documented outcomes unchanged: seat-player hex (either seat) and
"draw", with concurring final-state hash, still computeAgreed; differing in-domain outcomes still computeDisputed. - A regression test covers the probe matrix: third-party hex, arbitrary string, and the
draw/seat-hex controls. - Triage pins the encoding contract being enforced (lowercase hex of the 32-byte player id, as produced by the example/tests) so the check and claim constructors cannot drift apart.
Activity
dezren39 commentedon Oct 30, 2026
🤖 Agent note · filed
compute_state(pkgs/id/src/attestation.rs:253) checks claim outcomes only for string equality: with two distinct players and a complete genesis pair, concurring claims naming a third party C as winner — or giving outcome "banana" — computeAgreed, though ResultClaim documents the outcome as a seat player's hex or "draw" (:106-108). Probe: laptop~/git/fleet/probe-2026-10-30-laneB-attdup/(verbatim compute_state + types, real p2panda-core Hash); "draw" and seat-hex controls Agreed as documented. Enqueued as a triage packet for the repo-health-code-pilot queue.dezren39 commentedon Oct 31, 2026
Agent note (design-architect, 2026-10-30): this issue is Phase 2 of design #2898 ("reconstruction is admission", design doc
files/designs/code-id-reconstruction-validation.mdin the keep-the-four-repos-healthy goal workspace), together with #2863.Recorded decisions for triage — do not re-file, re-scope, or split this issue; record the phase structure instead:
winnerisNoneor names one of the agreement's two players. A claim naming anyone else is ignored undercompute_state's existing non-player-claim rule and can never contribute toAgreed.dezren39 commentedon Oct 31, 2026
Correction (design-architect, same pass): my earlier note misstated the outcome domain. Per this issue's evidence, the documented domain of
ResultClaim.outcomeis a seat player's id as hex, or the"draw"sentinel — there is noNonewinner. A claim outside that domain can never contribute toAgreed; whether it is ignored (like a non-player claim) or counts as a position (ProposedvsDisputed), and the exact hex-encoding contract, are this issue's triage pins, as its acceptance states. Design #2898's doc has been amended to match.7 remaining items
dezren39 commentedon Nov 1, 2026
Agent note — picked up (queue task 5449)
compute_stateand add the probe-matrix regression tests.queue/5449-2868-attestation-outcome-domaindezren39 commentedon Nov 1, 2026
Agent note — handed off (queue task 5449)
182fdd45ee86343ccd41d960e847a44d025e422ecargo test --manifest-path pkgs/id/Cargo.toml --test attestation_poc— 7/7 pass; changed-file rustfmt andgit diff --checkpass.dezren39 commentedon Nov 1, 2026
Agent note — done (queue task 5449)
compute_statenow ignores out-of-domain outcomes without letting them occupy a player position or create a fork.pr_opened; CI remains pending and can proceed without a held claim.Merge pull request #3508 from developing-today/queue/5449-2868-attest…