Files
psf-memo/docs/reviews/txid-wire-encoding-summary.md
2026-09-16 15:31:38 -07:00

179 lines
9.8 KiB
Markdown

# txid-wire-encoding — Architect Review
Task: `txid-wire-encoding`
Components: `psf-memo-client`, `psf-memo-db`
Base: `bf7edd5` (last merged architect review); inbound refactorer commit `a76fcee`
## What was reviewed
Inbound refactorer batch (priority 10), merged onto `swarmforge-architect` by
fast-forwarding to `a76fcee`. The linear chain reviewed:
- **`bb0c9ab`** — specifier: *Spec txid wire encoding and database repair*.
Adds `psf-memo-client/specs/txid-wire-encoding.feature` (4 scenario outlines
pinning the like, reply, poll-option, and poll-vote wire order) and
`psf-memo-db/specs/repair-txid-encoding.feature` (6 scenario outlines for the
byte-reversed-reference repair utility).
- **`dedb7a1`** — coder: *Fix txid wire encoding and add txid repair utility*.
Adds `txidToWireBytes` and reverses the txid in `buildTxidTextPayload`, so
likes, replies, poll options, and poll votes now embed the referenced txid
little-endian; adds the `repair-txid-encoding` library (`correctReference`,
`repairTxidEncoding`), the `util/txid` CLI wrapper, unit tests, the DB
acceptance fixture/handlers, and decodes the wire txid in the client
acceptance handlers.
- **`a76fcee`** — refactorer: *Refactor txid wire encoding and repair library*.
Generalizes the four near-identical repair loops into `repairStore` plus a
`txidIndex` key/value adapter, replaces the reply-only `buildReplyPayload`
with the shared `buildTxidTextPayload(txid, text, label)`, and adds client
and DB property tests (round trip, involution, idempotence, conservation,
resolution, index rebuild).
**Architect review commit: `a2229f7dd4`** — the `mutate4javascript` footer
manifests (client `hex.js`, `memo-like.js`, `memo-reply.js`; DB
`src/lib/repair-txid-encoding.js`), the soft `gherkin-mutator`
acceptance-mutation manifests on both touched feature files, the hardening
tests below, and the extracted shared test double. The summary and verification
records are committed on top, so `git diff a2229f7dd4 HEAD` touches only
`docs/`. The records' `git_sha` is `a2229f7dd4`, the commit that contains the
verified source state.
## Architectural findings and fixes applied
The refactorer's structure is sound and required no further boundary change.
The hardening work was mutation- and duplication-driven.
1. **UI/Core separation.** `src/services/hex.js` is a pure leaf (byte decode,
reverse, payload assembly) with no React, DOM, wallet, or IO. `memo-like.js`
and `memo-reply.js` are core services that receive the wallet/feed/thread
behind injected adapter doubles. `src/lib/repair-txid-encoding.js` is a pure
async core that receives the LevelDB handles; only
`util/txid/repair-txid-encoding.js` opens real stores. Every piece of core
behavior is exercised without a browser, network, or database file.
2. **Dependency rule.** The client services depend inward on `hex.js`; the DB
CLI wrapper depends inward on `src/lib`. Nothing in the pure modules reaches
out to React, the router, LevelDB, or the file system.
3. **Information hiding.** The little-endian wire order now has exactly one
definition (`txidToWireBytes`), and the txid+text payload shape has one
definition (`buildTxidTextPayload`). The refactorer's `repairStore` +
`txidIndex` hide the per-store field/key/value differences behind a small
adapter, and `correctReference` hides the "keep / reverse / leave" decision.
No persistence structure leaks into callers.
4. **Minor coupling, accepted.** `buildTxidTextPayload` defaults its label to
`'Poll txid'`, a residue of its poll-only origin. It is now generic and the
reply caller passes `'Parent txid'` explicitly; the poll callers rely on the
default. A fully neutral default was not worth churning the poll call sites.
5. **Test boundaries.** The task-local `FakeDb`/`makeLevel` double was
duplicated verbatim across the new unit and property tests, so it was
extracted to `psf-memo-db/test/support/level-double.js` (a test helper, kept
out of `test/unit/**` and `test/property/*.test.js`). The util CLI wrapper is
an environmentally unsuitable adapter shell and is deliberately excluded
from the tools that run tests/coverage/mutation.
6. **Mutation hardening (see below).** Six pre-existing boundary survivors in
`memo-like.js` and one in `memo-reply.js` were killed with focused boundary
assertions rather than design changes.
## Verification results
### Language mutation (`mutate4javascript`, `--mutate-all` where the wrapper
detected differential under-selection, `--max-workers 8`)
| File | Sites | Killed | Survived | Uncovered |
|------|------:|-------:|---------:|----------:|
| `psf-memo-client/src/services/hex.js` | 7 | 7 | 0 | 0 |
| `psf-memo-client/src/services/memo-like.js` | 34 | 34 | 0 | 0 |
| `psf-memo-client/src/services/memo-reply.js` | 2 | 2 | 0 | 0 |
| `psf-memo-db/src/lib/repair-txid-encoding.js` | 6 | 6 | 0 | 0 |
The first `memo-like.js` run left 9 survivors (`true -> false` on the
`validate`/`validateTip` return values, the `validateTip`
`tipSats > spendableSats` boundary, the `_validateTipAmount` max-tip boundary,
the `getSpendableSats` `?? 0` default, the `_requireTipAddress` `<= 0` and
`.length > 0` boundaries, the `_buildTipOutput` `tipSats > 0` boundary, and the
`_incrementPostCount` `|| 0` default). `memo-reply.js` left the
`isTooLong` `> MAX_REPLY_BYTES` boundary. Each was a missing boundary/default
assertion, not a design fault; focused unit assertions were added and the
re-runs report 34/0 and 2/0. `hex.js` and the DB repair library were clean on
the first full run.
`psf-memo-db/util/txid/repair-txid-encoding.js` is not mutated: it is the
file-system adapter shell (opens real LevelDB stores, no unit coverage by
design), and the tool that runs tests is scoped to the testable core.
### DRY (`dry4javascript`)
Scoped runs over the changed production files, tests, and adapters.
- **DB.** The verbatim `FakeDb`/`makeLevel` duplication across the two new test
files was extracted to `test/support/level-double.js`; the scoped report
dropped from 72 to 70 duplicate blocks. Every remaining block is pre-existing
`acceptance/lib/handlers.js` step-handler boilerplate, except one structurally
identical but semantically distinct pair in the unit test (like/postLikes vs
reply/postChildren index-rebuild assertions), left as deliberate test
boilerplate.
- **Client.** The task-local candidates are two `makeWallet` doubles
(`test/property/poll-services.property.test.js` vs
`test/unit/memo-reply.test.js`) and one structurally identical pair of
like-rejection tests in `memo-like.test.js`. `makeWallet` appears in 28 client
test files as an established per-suite fixture convention, so extracting it
would be a broad cross-suite refactor beyond this task; both are recorded as
documented pattern-boilerplate. The remaining blocks are pre-existing
`acceptance/lib/handlers.js` boilerplate.
### CRAP / cyclomatic complexity (`crap4javascript`)
- **Client:** every changed function at or below CRAP 6.0 with ~100% coverage —
`MemoLike._validateTipAmount` (CC 6, 100%, 6.0), `hexToBytes` (CC 5, 5.0),
`MemoLike._incrementPostCount` (CC 5, 5.0), `MemoLike.getSpendableSats`
(CC 4, 4.0), the rest CC ≤ 3. `MemoReply.reply` is 90% covered but CRAP 2.0.
- **DB:** `repairStore` (CC 5, 100%, 5.0), `correctReference` (CC 4, 4.0),
`hasRecord` (CC 4, 4.0), `reverseTxid` (CC 3, 3.0), `repairTxidEncoding`/
`txidIndex` (CC 1). All below the 8.0 threshold.
### Soft Gherkin acceptance mutation (`gherkin-mutator --level soft`)
- **`txid-wire-encoding.feature`** (client): **14 executed, 8 killed,
6 survived, 0 errors**. All six survivors are single-character case mutations
of values that are never asserted: reply `message` (`hello memo`,
`a second one`), poll `option` (`yes`, `no`), and poll-vote `comment`
(`yes`, `I choose this`). Each scenario asserts the Memo prefix and the
referenced txid, not the carried text, so the mutated text is an intrinsic
equivalent / weak example-to-assertion link. The wire-order txid mutations
(the point of the feature) were killed.
- **`repair-txid-encoding.feature`** (DB): **21 executed, 18 killed,
3 survived, 0 errors**. All three survivors mutate the `reversedPostTxid`
example value in scenarios 0, 1, and 5, which use it only in the negative
assertion "the postLikes store contains 0 entry whose key starts with
`<reversedPostTxid>`". Since the key is absent by construction, any mutated
value still yields 0 entries — a genuine negative-assertion equivalent. All
positive repair and index assertions killed their mutations.
These survivors are specifier-side feature-quality items (weak text
assertions), not implementation gaps; no implementation change is warranted.
### Suite status
Canonical records, both against review commit `a2229f7dd4523e6397418e2bb8ac43800940b902`:
- `swarmforge/scripts/verify.sh client --record
docs/reviews/txid-wire-encoding-verification.json --task txid-wire-encoding`
-> **pass (5/5)**: unit **417 pass / 0 fail**, property **80 pass / 0 fail**,
acceptance **all 30 suites passed**, lint **ok**, build **ok**.
- `swarmforge/scripts/verify.sh db --record
docs/reviews/txid-wire-encoding-db-verification.json --task txid-wire-encoding`
-> **pass (4/4)**: unit **371 passing**, property **54 pass / 0 fail**,
acceptance **all 13 suites passed**, lint **ok**.
This is the first recent task to touch two components, so the client record
uses the canonical `<task>-verification.json` name and the DB record uses
`<task>-db-verification.json`; both carry the same review `git_sha`.
## Handoffs sent
- End-of-chain `git_handoff` to the specifier (task `txid-wire-encoding`) with
the review commit `a2229f7dd4` so it can merge `swarmforge-architect` into
`master`.
- No coder/refactorer handoff: the review is mutation hardening plus a test-double
extraction with no follow-up work for those roles.
By architect.