|
|
@@ -1,103 +0,0 @@
|
|
|
-# Claiming a Triumph: Web Service opcode 1801
|
|
|
-
|
|
|
-A record whose requirements are met reads **Ready to Claim** in the Triumphs screen. Clicking it
|
|
|
-sends opcode 1801. This build decodes that request and resolves the record it names; it does not yet
|
|
|
-change any state, so the entry stays claimable.
|
|
|
-
|
|
|
-## What the client does, measured
|
|
|
-
|
|
|
-Three claims were made in play and traced end to end at debug level:
|
|
|
-
|
|
|
-```
|
|
|
-ev=ws stage=request opcode=1801 transaction=0 payload_bytes=3 payload_hex=80DD00
|
|
|
-ev=ws1801 stage=claim result=ok reason=decoded record_index=221
|
|
|
-ev=transport stage=frame conn=1 type=1 bytes=37
|
|
|
-ev=bap svc=10 rsp=11 result=ok
|
|
|
-```
|
|
|
-
|
|
|
-The full opcode sequence around them:
|
|
|
-
|
|
|
-```
|
|
|
-39391 op104
|
|
|
-48358 op1801 claim
|
|
|
-52742 op1801 claim
|
|
|
-53212 op1801 claim
|
|
|
-67824 op701 14 s later, unrelated
|
|
|
-```
|
|
|
-
|
|
|
-**The client accepts the reply and asks for nothing else.** No retry, no state fetch, no follow-up
|
|
|
-opcode. A grep for push or queuez activity after the claims returns nothing, because the server
|
|
|
-sends nothing.
|
|
|
-
|
|
|
-That isolates the gap precisely: the reply shape is already correct, and the client is waiting on a
|
|
|
-**push** that never arrives. It is not rejecting the response, so a richer response payload is not
|
|
|
-what is missing.
|
|
|
-
|
|
|
-## The request
|
|
|
-
|
|
|
-Three bytes, the same shape as the opcode-1820 Collections pull: a presence bit then a fifteen-bit
|
|
|
-record row index.
|
|
|
-
|
|
|
-```
|
|
|
-payload_hex=80DD00 -> 0x80DD, presence 1, record row 221
|
|
|
-payload_hex=80E300 -> 0x80E3, presence 1, record row 227
|
|
|
-payload_hex=80E000 -> 0x80E0, presence 1, record row 224
|
|
|
-```
|
|
|
-
|
|
|
-The row indexes the records and lore table `0x81319339`. `opcode1801::parse_request` refuses an
|
|
|
-absent record, non-zero padding, a wrong length and a wrong opcode; 13 cases are covered by a
|
|
|
-standalone test run against payloads captured from real clicks.
|
|
|
-
|
|
|
-## Why the dispatch hook sits outside the chain
|
|
|
-
|
|
|
-`web_service_runtime.cpp` computes `prepared` from the outcome and answers any **dispatched** opcode
|
|
|
-that prepared no mutation with `kRefusedStatus`. A claim prepares nothing yet, so adding 1801 to the
|
|
|
-dispatch chain would convert today's silently accepted claim into an explicit refusal -- a
|
|
|
-regression wearing the shape of progress. The hook therefore runs before the chain and leaves the
|
|
|
-outcome untouched.
|
|
|
-
|
|
|
-Move it into the chain in the same commit that gives it a mutation, not before.
|
|
|
-
|
|
|
-## The records domain
|
|
|
-
|
|
|
-`state::build_data::records` exists so a claim can go from a record row to the bank index it has to
|
|
|
-set, without walking the mapping tables per request.
|
|
|
-
|
|
|
-```c
|
|
|
-struct Definition {
|
|
|
- uint16_t definitionIndex; // native record row, what the claim names
|
|
|
- uint16_t completionFlagIndex; // account flag bank row, or 0xFFFF when unaddressable
|
|
|
-};
|
|
|
-```
|
|
|
-
|
|
|
-`package_record_build.cpp` reads both tables at extraction time:
|
|
|
-
|
|
|
-- record row `+100` holds the unlock **slot** of the record's completion flag
|
|
|
-- the account flag mapping table (root slot 111, descriptor 8) maps a **destination slot** to the
|
|
|
- **row number** whose object byte feeds it
|
|
|
-
|
|
|
-**A slot is not an array index.** The byte that sets slot `s` lives at the row of the mapping table
|
|
|
-whose destination is `s`, so the resolution is done once here rather than per claim.
|
|
|
-
|
|
|
-## What is still missing
|
|
|
-
|
|
|
-Only the state transition and its push. Three pieces:
|
|
|
-
|
|
|
-1. **A mutable claimed-record set.** `state::unlocks` is an immutable policy by contract --
|
|
|
- `publish`, `get`, `clear` and nothing else -- so claimed records need their own store.
|
|
|
-2. **An encoder change.** The family-4 account encoder has to OR those flags into the account flag
|
|
|
- bank when it builds the object.
|
|
|
-3. **A push.** Follow the existing `Pending*` mutation pattern: add a `PendingRecordClaim` to the
|
|
|
- `Outcome::Mutation` variant, prepare it in `claim_record`, and let the established publication
|
|
|
- path carry the new Family-4 version to the client.
|
|
|
-
|
|
|
-## One thing to confirm before building step 2
|
|
|
-
|
|
|
-**It is not established that the completion flag is what marks a record claimed.** Every record
|
|
|
-completion flag can be set while the client still offers the claim, which was observed directly over
|
|
|
-a long session. What the traced claims prove is the *delivery mechanism* -- a push rather than a
|
|
|
-response -- not the payload.
|
|
|
-
|
|
|
-Steps 1 and 3 are needed for any per-record state change and are safe to build. Only step 2 depends
|
|
|
-on `+100` being the right field, and if it turns out to be a different one that is a small change at
|
|
|
-the end rather than a redesign.
|