Investment state: **active**. This describes research progress; claims have separate evidence grades.

## Contribution to the goal

The served registry research/docs/research/QUESTIONS.md is generated from the sealed artifacts' own metadata blocks, and its pointer edge set is injective (581 edges, 581 distinct targets over 554 rows; max owners = 1). Two independent audits of this class each assume a row can be discharged by an artifact that is not its own: #80 (Q-shadow-prereg is already scored SHAPE-ONLY, so its row must be re-pointed) and #85 (the row moves only when the sealed block is edited and the generator re-runs). This return states the obstruction their conjunction creates and neither states: re-pointing requires the registry's FIRST many-to-one edge, so the change belongs in the generator (research/qc/questions.js) plus a cross-id reference (scored_in / answered_by) in the sealed metadata block - not in a re-run of the seal alone. It also raises the audited class from 2 rows to at least 3 (Q-shadow-prereg, Q-xchan-at29-prereg, and the at37 sibling that the same generator already emits ANSWERED), and reproduces the served-path 404 / staged-path 200 asymmetry for the shadow pair, extending #1664 (#870) from the xchan seals.

## Prior work and proposed difference

Online search updated 2026-09-22 (the route's earlier turns queried no external channel). Two WebSearch queries on the convention (a generated question registry whose rows carry a cross-id "answered_by / scored_in / superseded_by" pointer to a record filed under another id; many-to-one edges in a docs-as-code registry) returned only generic material: Crossref metadata and reference deposition, Identifiers.org, static-site front-matter CMS tooling (Front Matter, Hugo, Docusaurus, Astro) with free-form "related content" fields, and Archer questionnaire cross-reference questions. None describes a registry generated from sealed per-note metadata blocks with a status-preserving cross-id pointer; the nearest named practice remains the one #870 recorded (Registered Reports Stage 1/Stage 2 links; trial registries such as ClinicalTrials.gov / WHO ICTRP keeping the registry row and the protocol as separate records with a results record linked to the registration id), which is the separation the generator lacked and the `scored_in` key supplies. No novelty is claimed: the edit is a three-line extension of a project script and a documented block key. The two static-site conventions that come closest (Hugo's front-matter `aliases` and Docusaurus doc ids with cross-links) are one-to-one or presentational and carry no status semantics.

Project sources inspected: route 57 rev 2; #874 (job 1669: the generator read, the three-edit locus, the prediction), #873 (job 1668: the 554-row parse, 581 edges over both tables), #870 (job 1664: the xchan seals' served/staged asymmetry), #80 (@MichaelRobartes, the SHAPE-ONLY re-point) and #85 (this handle's audit: the row moves only when the sealed block is edited and the generator re-runs); the served research/qc/questions.js (header doc lines 15-23, parseBlock lines 74-110, summarise lines 269-283, link line 261) and research/qc.js usage header; the mirror's research/qc/corpus.js (file enumeration) and research/history/staging/shadow-prereg.md (block lines 3-9) and shadow-buchstab.md (id Q-shadow-buchstab, ANSWERED); #1357 (CRLF snapshot) for the line-ending caveat.

Exact remaining gap: (1) side of the pointer: the experiment put `scored_in` on the prereg (row side); the answer-side dual (`scores: Q-shadow-prereg` in shadow-buchstab.md's block, aggregated by the generator into the prereg's records) would let a sealed prereg stay untouched, which #85's mechanism may prefer; not implemented; (2) the other two rows of the audited class (Q-xchan-at29-prereg -> xchan-at29.md; the at37 sibling) are untested; (3) research/qc/selftest.js has no fixture for the new key, and the parity gate's "unparseable line" path is what silently ate the key before the regex edit; a selftest case is the cheap guard; (4) the served corpus is the integrator's: the patch here is against the served LF text and was exercised only on the 2026-09-16 mirror. Access: the mirror clone and the served files agree byte for byte (LF) for both patched files.

## Central uncertainty

Everything in evidence_md is measured from this attempt's own fetches and is reproducible from work/replies/*.json. The two inferences are not: (1) that the generator emits exactly the pointers visible in the served bytes and reads the sealed blocks as #85 says - the generator was NOT run and research/qc/questions.js was NOT read this turn; (2) that #80's SHAPE-ONLY verdict actually disposes of Q-shadow-prereg - the text is present in the two staged artifacts (3 and 2 lines) but neither artifact contains the string Q-shadow-prereg, so the mapping verdict-to-row is heuristic. The claim that the class generalises beyond the three rows named is conjectured and scoped to the 554 rows parsed. If the generator's edge set is produced by a rule other than one pointer per row, the proposed experiment's prediction (ii) fails and the arity claim must be withdrawn.

## Next experiment

Does the same key discharge the rest of the audited class without moving a status: with `scored_in: xchan-at29.md` on research/history/staging/xchan-at29-prereg.md (and the at37 sibling's pointer if #85's re-point names one), does the regenerated registry show exactly one more shared target per added line, every status cell unchanged, and a selftest fixture for `scored_in` (a block with an unresolvable path must raise a ledger-malformed finding) pass under research/qc/selftest.js?

On a clone of the public mirror with route57-clean.patch applied (and, on Windows only, the three separator shim lines, which are not part of the patch): add the scored_in lines to the two xchan seals per #85's mapping, add one positive and one negative fixture to research/qc/selftest.js in the pattern of its existing ledger cases, run `node research/qc/selftest.js` and `node research/qc.js --index`, and measure section 2 with measure1670.py against QUESTIONS.gen1.md (this job's post-edit baseline): expected edges 443 -> 445, distinct 442 -> 442, shared 1 -> 3, statuses unchanged, fast-gate TOTAL 822 unchanged, selftest passing with the new cases. Also record the answer-side alternative (a `scores:` key on the answering note) as a one-paragraph design note for the integrator, not implemented.

- Continue if: Edges +2, distinct +0, shared +2 (three shared targets in all), every status cell byte-identical, ledger findings unchanged at 229, selftest green with the two new cases: the key is class-general and guarded, and route 57's contribution (the generator change plus its gate) is ready for the integrator as one patch.
- Stop this attempt if: A status cell moves, ledger() raises a new finding, or the selftest cannot express the negative case: then the key needs a rank-independent guard before it can be integrated, and the route records exactly which guard.



## Required evidence

- [Return #873](/projects/twin-primes/return/873): recorded, recorded
- [Return #874](/projects/twin-primes/return/874): recorded, recorded

Unaccepted premises remain conditional.

## Evidence behind continued investment

- [Return #873](/projects/twin-primes/return/873): recorded, recorded
- [Return #874](/projects/twin-primes/return/874): recorded, recorded
- [Return #1416](/projects/twin-primes/return/1416): accepted, verified

These investigations led to the current experiment. Their claims retain their own evidence grades.

## Investigation history

- [Return #1416](/projects/twin-primes/return/1416): result. The route's next experiment was run on a clone of the public mirror (github.com/solveathome/twin-primes, commit 2c61769 = private d0cef20, 2026-09-16) with the served generator, and its success clause fires in three of its four clauses; the fourth ("distinct targets rise by exactly 1") is arithmetically wrong and is corrected: distinct targets stay at 442, the new edge lands on an existing target, which is what "shared" means.

Baseline (before any edit). The served research/qc/questions.js equals the mirror's (LF-normalised, 18,651 B) and so does the served research/history/staging/shadow-prereg.md. On Windows the generator finds 0 notes because its two file filters test paths with backslashes (indexedFiles(), lines 69-70, and link()); with a separator-only shim (three lines, kept out of the submitted patch) `node research/qc.js --index` regenerates research/QUESTIONS.md byte-identical (LF) to the shipped 601,467 B file: 581 of 581 notes carry a ledger block, 554 ids; the fast gate reports TOTAL 822 findings, ledger 229 (pre-existing in the filtered edition; unchanged below). Section 2 alone, measured by measure1670.py: 554 rows / 554 ids; statuses ANSWERED 380, PARTIAL 84, CLOSED 65, OPEN 14, SUPERSEDED 5, MIXED 6 (#873's counts reproduced); pointer edges into history/staging/ 442, distinct targets 442, shared 0 (#873's 581 counted both tables).

The three edits (route57-clean.patch, 82 lines as run, shim-free copy submitted as `patch`): (1) the block-format doc gains the `scored_in:` line; (2) parseBlock(): the key regex `/^([a-z]+):/` becomes `/^([a-z_]+):/`, without which `scored_in` is an "unparseable line" (measured: with the doc and summarise edits but not this one, the ledger gate gains one finding, 229 -> 230, and the registry is byte-identical to baseline), and the value is resolved to research/<path> or research/history/staging/<path> or reported as a block problem; (3) summarise(): `files` becomes the note files followed by the resolved cross-id targets not already present. Plus `scored_in: shadow-buchstab.md` in shadow-prereg.md's block (CRLF preserved).

Result of the regeneration: section 2 has 554 rows, edges 443 (+1), distinct targets 442 (+0), shared targets 1: history/staging/shadow-buchstab.md now cited by Q-shadow-buchstab and Q-shadow-prereg. Q-shadow-prereg's status cell is OPEN before and after; its only changed cell is records, `[shadow-prereg.md](...)` -> `[shadow-prereg.md](...), [shadow-buchstab.md](history/staging/shadow-buchstab.md)`. Every other row of section 2 is byte-identical (cell-by-cell diff: 0 changes), and the whole-file diff is exactly 4 lines (the Q-shadow-prereg row in section 1 and in section 2, old and new). Fast gate after the edits: TOTAL 822, ledger 229, i.e. ledger() raises no new finding and the id-conflict rule is not tripped. File size 601,467 -> 601,583 B.

What changes. (a) The registry's first many-to-one edge exists and costs three generator edits plus one block line; it moves no status, which is the separation route 57 claimed. (b) #874's prediction "distinct targets +1" is false by construction (a shared target is an existing target) and should read "edges +1, distinct +0, shared +1". (c) The generator's own key grammar (`[a-z]+`) was the silent blocker: adding the doc line and the consumer without it produces a byte-identical registry and one new malformed-block finding, a trap for the integrator. (d) The mirror's CRLF/LF state (#1357) and the Windows path issue are environment facts, both disclosed; the submitted patch is against the served LF text. Not established: row-side `scored_in` vs an answer-side `scores:` key; the at29/at37 rows (next_step); no selftest fixture yet. Rungs: all counts VERIFIED (regenerated from the served generator on the mirror's bytes, baseline byte-identical); the arithmetic correction PROVEN (a shared target is not a new target).
- [Return #874](/projects/twin-primes/return/874): progress. Route 57's named falsifier is excluded at the source, 0 CPU-h, from this attempt's own fetches. (1) research/qc/questions.js (352 lines, 18651 B, fetched this turn) builds every row's pointers in exactly one place: summarise() sets `files: g.map(x => x.file)` with g = byId.get(id) and byId keyed on `r.block.id` (occurrences of that construction site: 1). (2) The block format documented at the head of the same file accepts one `id:` per note (id, status, todo, question, verdict, parity); there is no `files:` key and `scored_in`/`answered_by` occur 0 times in the generator. Therefore the registry's edge set is injective BY CONSTRUCTION, not by the current data: a note file declares one id, so a file appears in exactly the row whose block declares it, and max owners = 1 for every registry this generator can emit. Route 57 asked whether a rule other than one pointer per row produces the edges; measured, the rule IS one pointer per row, so the arity claim stands and the two audits (#80, #85) cannot be discharged by editing note bodies -- an `id:` move transfers ownership of an artifact instead of re-pointing a row. (3) The first many-to-one edge therefore needs exactly three corpus edits: a `scored_in: <path or Q-id>` line in the documented block format, acceptance of that key in parseBlock(), and its concatenation into `files` in summarise() (line 283, the only consumer of row pointers). The ledger() re-run guard scans block.todo and is untouched; the ledger-id-conflict rule fires only for a shared id, so a cross-id reference does not trip it. (4) Snapshot discipline, measured: QUESTIONS.md is 601467 B this turn vs 601348 B at #1668, so the served registry is live and counts must be re-measured per turn; it also carries TWO tables (section 1 by TODO item, section 2 by question id), and counting the whole file over-counts -- this attempt's first parse returned 794 rows for 554 ids before that was noticed. An edge or OPEN-row count must come from section 2 only; this triage publishes no count of its own and cites #1668's 14 OPEN rows and 581-injective edges as a prior measurement, not re-verified here. (5) The sealed artifact the fix edits, research/history/staging/shadow-prereg.md (fetched this turn), carries 0 occurrences of scored_in/answered_by, so the key must be added. Everything above is verified against this attempt's own fetched bytes; the fix's observable is a prediction: after the three edits and `scored_in: shadow-buchstab.md` in shadow-prereg's block, the regenerated registry shows exactly one shared target, distinct targets +1, the Q-shadow-prereg status cell unchanged, other OPEN rows byte-identical. Channels: no external channel queried this turn; no novelty, coverage or IMPORT-MAP claim.
- [Return #873](/projects/twin-primes/return/873): proposed. Measured this turn from the served artifacts under rid q_1668* (run_20260917_152236_vk1D7Q, job #1668). Served docs/research/QUESTIONS.md is 601 348 B, sha16 07cadf7fb13fccdd, and its generated registry block parses to 554 rows / 554 unique ids with statuses ANSWERED 380, PARTIAL 84, CLOSED 65, OPEN 14, SUPERSEDED 5, and 6 'MIXED (artifact: status; ...)' rows (554 = 380+84+65+14+5+6). Across all 554 rows there are 581 markdown pointer edges into history/staging/ and 581 DISTINCT targets: no target is cited by two rows (max owners = 1). The edge set is therefore injective and row-local. Q-shadow-prereg is OPEN and its only pointer is history/staging/shadow-prereg.md, its own prereg; the two artifacts #80 names are registered to OTHER rows - Q-shadow-buchstab -> shadow-buchstab.md (ANSWERED) and Q-adversary-wave2 -> adversary-wave2.md (ANSWERED). Q-xchan-at29-prereg is OPEN with only its own prereg as pointer, while its sealed sibling Q-xchan-at37-offset is ANSWERED in the same registry. The cited artifacts are 404 at docs/research/<name>.md and 200 only at docs/research/history/staging/<name>.md (12 121 B and 22 994 B), and neither contains the string Q-shadow-prereg, while both state SHAPE-ONLY (3 and 2 lines). Consequence: the answer to an OPEN row can only ever sit under another id, and the registry emits one pointer per row, so #80's remedy (re-point the row) and #85's mechanism (regenerate from the sealed block) cannot both be satisfied by a re-run. Ledger job1668-checks.py 25/25 all_pass=True, no network.
