Fork desk digests: door-form legal_retraction golden (#75)
Some checks failed
CultMesh QUIC native / Build win32-x64 (push) Has been skipped
CultMesh QUIC native / Build linux-x64 (push) Has been skipped
CultMesh QUIC native / Release native binaries (push) Has been skipped
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (push) Has been cancelled
CultMesh QUIC native / Verify committed binaries match SHA256SUMS (push) Has been cancelled

This commit is contained in:
Libby 2026-11-10 18:19:26 +00:00
commit e12ee63840
5 changed files with 32 additions and 10 deletions

View file

@ -364,7 +364,7 @@
{
"name": "legal_retraction",
"status": "main_7ea11b4_must_not_move",
"why": "a legal retraction: choice and note null, retracts the full stored key (Huginn's pinned test names Q1.1)",
"why": "a legal retraction as Huginn main pins it: retracts is `Q1.1`, a string no door produces (the key grammar is .n<seq>); kept for the frozen-form test",
"input": {
"fork": "eureka-state:question:Q1",
"options_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
@ -395,6 +395,23 @@
"canonical": "{\"fork\":\"eureka-state:question:Q1\",\"options_hash\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"answered_at\":\"2026-11-10T12:00:00Z\",\"choice\":\"A\",\"note\":null,\"retracts\":\"eureka-state:desk_answer:Q1.n1\",\"illegal_retraction\":true}",
"sha256": "69074cc60829ef5edb12358746f7549ad08da97653d59359c945cea102f53565"
},
{
"name": "legal_retraction_door_form",
"status": "main_7ea11b4_must_not_move",
"why": "a legal retraction as the door produces it: the wire's n1 converted to the full stored key Q1.n1 before the digest; the golden that stands for the real form (digest unaffected by .n<seq> vs .1)",
"input": {
"fork": "eureka-state:question:Q1",
"options_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"answered_at": "2026-11-10T12:00:00Z",
"choice": null,
"note": null,
"retracts": "eureka-state:desk_answer:Q1.n1",
"illegal_retraction": null,
"retracted": null
},
"canonical": "{\"fork\":\"eureka-state:question:Q1\",\"options_hash\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"answered_at\":\"2026-11-10T12:00:00Z\",\"choice\":null,\"note\":null,\"retracts\":\"eureka-state:desk_answer:Q1.n1\"}",
"sha256": "84576840247830b9b75754b460dc4ea52883ca1e410f9f011521e0f69b99eee1"
},
{
"name": "illegal_ii_on_main",
"status": "main_7ea11b4_must_not_move",
@ -414,7 +431,7 @@
},
{
"name": "illegal_i_retraction_with_an_answer_retracted_true",
"status": "pending_eureka_merge_cut-desk-digest-retracted.r2",
"status": "pending_eureka_merge_huginn_30_cut-desk-digest-retracted.r2",
"why": "(i) answer present, retracted true, target Q1.n1",
"input": {
"fork": "eureka-state:question:Q1",
@ -431,7 +448,7 @@
},
{
"name": "illegal_iii_plain_answer_with_a_target_retracted_false",
"status": "pending_eureka_merge_cut-desk-digest-retracted.r2",
"status": "pending_eureka_merge_huginn_30_cut-desk-digest-retracted.r2",
"why": "(iii) the same bytes as (i) with retracted false: another body, so request_id_conflict",
"input": {
"fork": "eureka-state:question:Q1",
@ -448,8 +465,8 @@
},
{
"name": "illegal_i_retraction_with_an_answer_and_no_target_retracted_true",
"status": "computed_here_by_the_same_rule_not_in_the_order_doc_pending_eureka_merge",
"why": "(i) with the target null (the order doc: retracts set or null); verified with sha256sum, but Eureka has not published this golden",
"status": "pending_eureka_merge_huginn_30_cut-desk-digest-retracted.r2",
"why": "(i) with the target null (the order doc: retracts set or null); Eureka re-derived it with sha256sum (order doc addendum); the fix branch's pinned test does not yet carry it",
"input": {
"fork": "eureka-state:question:Q1",
"options_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
@ -465,7 +482,7 @@
},
{
"name": "illegal_ii_no_answer_no_target_retracted_true",
"status": "pending_eureka_merge_cut-desk-digest-retracted.r2",
"status": "pending_eureka_merge_huginn_30_cut-desk-digest-retracted.r2",
"why": "(ii) no answer, no target, retracted true",
"input": {
"fork": "eureka-state:question:Q1",

View file

@ -67,7 +67,7 @@ The stamped `authority` field of a stored answer is an **attribution**: the role
**The mind's record and this document.** Huginn keeps the answer as its own record kind (`epiphany.pipeline.desk_answer.v2`, key `<campaign>:desk_answer:<question-label>.n<seq>`, the outcome as one externally tagged value, the fork as `fork`). The second door projects it to this document and nothing else crosses: `answer_id` is the key's `n<seq>` part, `fork_id` is the stored `fork`, the tagged outcome flattens into `outcome`, `of`, `retracts` and `code`, and the six refusal codes are spelt as in the schema (snake_case; the record's enum spells them in the mind's own PascalCase). On the wire an answer is named by its `n<seq>` alone, always inside the fork whose `fork_id` the document carries; a runtime that holds the full record key converts at the door, in one place. `body_sha256` and any other digest Huginn keeps to decide "same body" stay in the record: Huginn alone decides a replay or a `request_id_conflict` and Bifrost never compares bodies, so no digest is a field of this document. A digest is a valid way to decide it only if it covers `fork_id`, `options_hash`, `answer.choice`, `answer.note`, `retracted`, `retracts` and `answered_at` (every field of the request but `request_id`), is computed by Huginn and never read from a client.
**Huginn's digest, pinned in the vectors (`bodyDigests`; informative, not a wire document).** SHA-256 of one compact JSON object as `serde_json::to_string` writes it (UTF-8, no spaces), lowercase hex, keys in this order, `null` for a missing value, strings verbatim: `fork`, `options_hash`, `answered_at`, `choice`, `note`, `retracts` (the **full stored key** `<campaign>:desk_answer:<label>.n<seq>`: the door converts the wire's `n<seq>` before the digest), then, **only for the three illegal forms** (R1: a retraction carrying an answer, a retraction naming no target, a plain answer carrying a target), `illegal_retraction: true` and `retracted: true|false`. `request_id` is not in it. A plain answer and a legal retraction carry neither trailing key, so their digests are the ones Huginn main pins (`8a4d5b2e`, `9f807538`) and do not move. Source: Eureka's `desk-body-digest-order-20261110.md`, computed with `sha256sum`; the generator, the C# suite and the TypeScript suite recompute every digest from the input fields. **Status:** the three `retracted` goldens (`935cab1a`, `82b24ccc`, `34ef86ed`) are the fix branch's form (`cut-desk-digest-retracted.r2`) and are **pending Eureka's merge**; Huginn main (`7ea11b4`) writes `illegal_retraction` alone, so forms (i) and (iii) collide there (`69074cc6`, pinned as `illegal_i_and_iii_alike_on_main`). The wire contract does not wait on it: the conflict scenarios are pinned in `scenarios`.
**Huginn's digest, pinned in the vectors (`bodyDigests`; informative, not a wire document).** SHA-256 of one compact JSON object as `serde_json::to_string` writes it (UTF-8, no spaces), lowercase hex, keys in this order, `null` for a missing value, strings verbatim: `fork`, `options_hash`, `answered_at`, `choice`, `note`, `retracts` (the **full stored key** `<campaign>:desk_answer:<label>.n<seq>`: the door converts the wire's `n<seq>` before the digest), then, **only for the three illegal forms** (R1: a retraction carrying an answer, a retraction naming no target, a plain answer carrying a target), `illegal_retraction: true` and `retracted: true|false`. `request_id` is not in it. A plain answer and a legal retraction carry neither trailing key, so their digests are the ones Huginn main pins (`8a4d5b2e`, `9f807538`) and do not move. `9f807538` is pinned for a target no door produces (`Q1.1`); the golden for the real legal retraction is the door's form, the wire's `n1` converted to `Q1.n1` (`84576840`, vector `legal_retraction_door_form`, also main-valid). Source: Eureka's `desk-body-digest-order-20261110.md`, computed with `sha256sum`; the generator, the C# suite and the TypeScript suite recompute every digest from the input fields. **Status:** the three `retracted` goldens (`935cab1a`, `82b24ccc`, `34ef86ed`, and `79eee623` for form (i) with a null target, which Eureka re-derived) are the fix branch's form (`cut-desk-digest-retracted.r2`) and are **pending Eureka's merge of Huginn #30**; Huginn main (`7ea11b4`) writes `illegal_retraction` alone, so forms (i) and (iii) collide there (`69074cc6`, pinned as `illegal_i_and_iii_alike_on_main`). The wire contract does not wait on it: the conflict scenarios are pinned in `scenarios`.
### Counting rules (in order; the first failure refuses with that code)

View file

@ -150,6 +150,8 @@ test("fork card: the body digest is SHA-256 of the compact JSON in the stated ke
}
assert.ok(byName.get("plain_answer_choice_A").sha256.startsWith("8a4d5b2e"));
assert.ok(byName.get("legal_retraction").sha256.startsWith("9f807538"));
assert.ok(byName.get("legal_retraction_door_form").sha256.startsWith("84576840"));
assert.ok(byName.get("legal_retraction_door_form").input.retracts.endsWith(".n1"), "the door's form: the wire n<seq> converted to the full key");
assert.ok(byName.get("illegal_i_retraction_with_an_answer_retracted_true").sha256.startsWith("935cab1a"));
assert.ok(byName.get("illegal_iii_plain_answer_with_a_target_retracted_false").sha256.startsWith("82b24ccc"));
assert.ok(byName.get("illegal_ii_no_answer_no_target_retracted_true").sha256.startsWith("34ef86ed"));

View file

@ -487,15 +487,16 @@ def dg(name, status, sha_expected, why, **kw):
b = digest_bytes(i); got = sha(b)
assert got == sha_expected, (name, got)
return {"name": name, "status": status, "why": why, "input": i, "canonical": b.decode("utf-8"), "sha256": got}
MAIN, FIX = "main_7ea11b4_must_not_move", "pending_eureka_merge_cut-desk-digest-retracted.r2"
MAIN, FIX = "main_7ea11b4_must_not_move", "pending_eureka_merge_huginn_30_cut-desk-digest-retracted.r2"
BODY_DIGESTS = [
dg("plain_answer_choice_A", MAIN, "8a4d5b2ed0b3452bfed2f3a276b2828974e6824a3edcc643cfe9cfcdda34173a", "a plain answer: choice and note as sent, retracts null; no illegal or retracted key", choice="A"),
dg("legal_retraction", MAIN, "9f8075382556898c0bea364ed7c6deb2a86d8c78288a1f34e06da6e9bf5f58a3", "a legal retraction: choice and note null, retracts the full stored key (Huginn's pinned test names Q1.1)", retracts="eureka-state:desk_answer:Q1.1"),
dg("legal_retraction", MAIN, "9f8075382556898c0bea364ed7c6deb2a86d8c78288a1f34e06da6e9bf5f58a3", "a legal retraction as Huginn main pins it: retracts is `Q1.1`, a string no door produces (the key grammar is .n<seq>); kept for the frozen-form test", retracts="eureka-state:desk_answer:Q1.1"),
dg("illegal_i_and_iii_alike_on_main", MAIN, "69074cc60829ef5edb12358746f7549ad08da97653d59359c945cea102f53565", "main writes `illegal_retraction: true` and no `retracted`, so (i) and (iii) collide: the defect the fix removes", choice="A", retracts=DT, illegal_retraction=True),
dg("legal_retraction_door_form", MAIN, "84576840247830b9b75754b460dc4ea52883ca1e410f9f011521e0f69b99eee1", "a legal retraction as the door produces it: the wire's n1 converted to the full stored key Q1.n1 before the digest; the golden that stands for the real form (digest unaffected by .n<seq> vs .1)", retracts=DT),
dg("illegal_ii_on_main", MAIN, "1a36c4aba0d1c30d0736f20243ebfdb84fe84a5eabd4f69709a096a32c3cc6e3", "(ii) no answer, no target, on main", illegal_retraction=True),
dg("illegal_i_retraction_with_an_answer_retracted_true", FIX, "935cab1a0ae34606b809d8ebca7cffa2889d20b3585d2622d32153a4d6898af1", "(i) answer present, retracted true, target Q1.n1", choice="A", retracts=DT, illegal_retraction=True, retracted=True),
dg("illegal_iii_plain_answer_with_a_target_retracted_false", FIX, "82b24cccb4ed2535a549f95f844fcd935074c3712b5b832f0b2cbd89c71fd9a6", "(iii) the same bytes as (i) with retracted false: another body, so request_id_conflict", choice="A", retracts=DT, illegal_retraction=True, retracted=False),
dg("illegal_i_retraction_with_an_answer_and_no_target_retracted_true", "computed_here_by_the_same_rule_not_in_the_order_doc_pending_eureka_merge", "79eee623fd6712db6537a4867cedf54ca49ee4885416c2c8e4acec59f811b894", "(i) with the target null (the order doc: retracts set or null); verified with sha256sum, but Eureka has not published this golden", choice="A", illegal_retraction=True, retracted=True),
dg("illegal_i_retraction_with_an_answer_and_no_target_retracted_true", "pending_eureka_merge_huginn_30_cut-desk-digest-retracted.r2", "79eee623fd6712db6537a4867cedf54ca49ee4885416c2c8e4acec59f811b894", "(i) with the target null (the order doc: retracts set or null); Eureka re-derived it with sha256sum (order doc addendum); the fix branch's pinned test does not yet carry it", choice="A", illegal_retraction=True, retracted=True),
dg("illegal_ii_no_answer_no_target_retracted_true", FIX, "34ef86ed63b9318459965d4b85e794d2636ffc15736da866036b087c9c4a30eb", "(ii) no answer, no target, retracted true", illegal_retraction=True, retracted=True),
]
fork = {

View file

@ -211,6 +211,8 @@ namespace GameCult.Networking.Tests
}
Assert.That(S(digests["plain_answer_choice_A"]["sha256"]), Does.StartWith("8a4d5b2e"));
Assert.That(S(digests["legal_retraction"]["sha256"]), Does.StartWith("9f807538"));
Assert.That(S(digests["legal_retraction_door_form"]["sha256"]), Does.StartWith("84576840"));
Assert.That(digests["legal_retraction_door_form"]["input"]!["retracts"]!.ToString(), Does.EndWith(".n1"), "the door's form: the wire n<seq> converted to the full key");
Assert.That(S(digests["illegal_i_retraction_with_an_answer_retracted_true"]["sha256"]), Does.StartWith("935cab1a"));
Assert.That(S(digests["illegal_iii_plain_answer_with_a_target_retracted_false"]["sha256"]), Does.StartWith("82b24ccc"));
Assert.That(S(digests["illegal_ii_no_answer_no_target_retracted_true"]["sha256"]), Does.StartWith("34ef86ed"));