W8 HANDBACK (todlando -> doyle), feat/332-surfaces @ 288aae7d. Base 11a2f373 (current main). The branch is LOCAL only, not pushed. Worktree .worktrees/w8-surfaces, pool target (claimed w8-surfaces). COMMITS (6) d7eec601 chore(reqs): register 4 REQs, unactivated 7bbcbdc5 feat(docs): per-node Installed-on part in the docs sidebar (#332) REQ-DOCS-NODE-TOC doc/impl/unit/int 72e5e90b feat(web): //a//about for every installed adapter (#332) REQ-DOCS-ADAPTER-PAGE doc/impl/unit/int 07de5c8b feat(adapter): adapter list in adapters + Available on subnet (#269, #264) REQ-ADAPTER-LIST-SURFACE doc/impl/unit/int c53cfc83 feat(picker): adapter[:profile] v#.#.# (#259) REQ-PICKER-ADAPTER-VERSION impl/unit 288aae7d docs(context): adapter-profile entry now says the list nests : (a stale-sentence census found it) Commit trailers say fixes by issue. No "fixes #N" keyword is used yet; add one at PR time if you want auto-close. GATES (all run by me on HFENDULEAM, all captured to files in my scratchpad) xtask check: rc=0 at each slice (xcheck1-4.raw). The last run was at c53cfc83's tree. 288aae7d is a one-line CONTEXT change, checked with treqs only. traceable-reqs check: rc=0 at HEAD. clippy -D warnings, all-targets, for spt-net, spt-runtime, spt-daemon and spt: rc=0. xtask gen: regenerated cli/reference.md (the `adapter list` help text). changelog.md differed by line endings only, and I restored it. Regression, full lib/bin units under cargo test: spt-runtime 144/144, spt-net 214 (1 ignored), spt 830/830, spt-daemon 1097/1098. The one red is brainproc::the_ready_breadcrumb_tells_a_computed_hash_from_a_cached_one. It passes run alone. Its own comment says it needs nextest process-per-test (a process-global OnceLock), and cargo test shares one process. So it is a runner artifact, not W8. I did not run nextest. NOT RUN: Linux. The HEAVY e2es that got only a literal field (adapter_peer_e2e, source_ladder_e2e) compile under clippy all-targets but were not executed. BATTERY (new cells; each int has a negative control. Every mutation was applied and restored by script, and each restore was sha256-asserted) NODE-TOC unit: 8 cells in spt-runtime::surfaces: splice point, all 3 groups when empty, the fixed +4 shift, alpha sort + ../a//about hrefs, an unrecognised script is not spliced, HTML+JS-string escaping, toc-.js names. Control: PART_CHAPTERS 4->3 reds reference_numbering_shifts... int: docs_server_e2e::sidebar_script_carries_this_nodes_installed_part_per_request. Checks the fingerprinted name, no-store on that response only, the three types including a mixed Gateway listed once, a per-request refresh, a foreign script served verbatim, and index.html not no-store. Control: route interception off (`false &&`) reds at :237, the no-store assert. ADAPTER-PAGE unit: 6 cells in surfaces (all facts / no dim line; undeclared pre-W8 -> one dim line and NO via; unknown sidecar shape -> linked; escaping; source word round-trip where "file" is None; clock = the divulge clock) + relcache current_source round-trip with a pre-W8 sidecar reading None. int: docs_server_e2e::every_installed_adapter_has_an_about_page_from_its_records. Covers the declared page, the bare Gateway page with no via, the not-installed 404, and /changelog still 404 for an undeclaring adapter. Control: source read forced to None reds at :317, the "via subnet" assert. current_source is a REQUIRED field on RetainPending and on AdapterCandidate, so the compiler enforces that every retaining arm names its source. The arms: bundle add -> built-in; bundle UPGRADE through update_one_adapter -> built-in, not release; via-subnet -> subnet; peer update -> subnet; --release -> release; channel update -> release. ADAPTER-LIST-SURFACE unit: 4 cells in adapterlist, all literals: the plain layout, no jargon + no empty Available section, the #269/#264 colours, and Available excluding installed names with versions split. Plus roster_row_hostable_types_round_trip_across_versions (spt-net) and roster_rows_carry_the_manifests_hostable_types (relcache). OLD DECODER, stated as you asked: the pre-W8 row is a verbatim copy of main's AdapterRosterRow. It has no deny_unknown_fields, and serde's default for such a struct IGNORES an unknown key, so the old peer decodes every field it knows unchanged. The cell asserts that equality on a row carrying the key, and again on the row inside a real UpdRecord::AdapterOffer line. grep: no deny_unknown_fields anywhere in spt-net src. The carriers are AdapterRosterRow, NodeLabel, NodeLabelUpdate and UpdRecord. The only deny_unknown_fields hits in the tree are spt-store serving.rs:94/150/158, which are not roster carriers. An old row decodes on the new type with an empty vec (grouped by kind), and an empty vec is never written. int: json_emit::adapter_list_speaks_in_adapters_and_lists_what_peers_offer. The real binary over a seeded SPT_HOME and peer snapshot. It checks the whole piped stdout as one literal, CLICOLOR_FORCE colours, the --json old keys kept + new keys, and a negative arm where no snapshot means no section. Control: handler ignores row.hostable_types reds at :217, the layout literal (the remote Gateway lands under HARNESS). PICKER-ADAPTER-VERSION unit: model cell (versioned(); a local endpoint versioned from this node, a REMOTE one from ITS node's roster, no roster row -> no suffix) + view cell (tree root and leaf, the "New vN endpoint" title, the description pane). picker filter 86/86. Control: the remote branch reads the local map reds at model.rs:1786 "the remote node's version". HEAVY-AT-BIRTH: NO new test binaries. Every new cell is a fn inside an existing binary: docs_server_e2e: in-process start_serving on port 0. json_emit: runs `spt adapter list`, which reads files only. It makes no spt api call, no daemon run and uses no autostart verb. The rest are lib/bin units. None of them reaches spt api or daemon run. Pool census after the ints: 0 spt/spt-daemon processes from w8-surfaces. CHOICES YOU MAY WANT TO OVERRULE - The sidebar part title reads "Installed on ". The ruling wrote INSTALLED ON in caps, and I matched the case of the sibling part titles. The adapter rows link /about, not /changelog. - toc.html (the noscript iframe fallback) stays static. - Human `adapter list` hides soft-deregistered adapters; --json still carries them with active=false. - Available rows sort versions as strings. - The /about header's "installed" is registered_at_ms, which moves on every re-register, so it means when the current bytes were installed. ASK: push feat/332-surfaces and open the PR? Say if you want a rebase first.