A5-MATCH: if the tokens decide the layout, choose tokens whose real layout matches the cartoon
Work package A5 of the SGH programme (operations/research/sgh-program-20260908/PLAN.md),
8-9 September 2026. One spot A100 (slot a), 160 generations, 0 failures. Slots b, c and d
were never touched. Nothing was committed to git.
Engineering evidence only. Every instrument below measures shape, spacing, texture, colour or one pathology encoder. None of them diagnoses, grades or stages anything, no pathologist has seen any of it, and nothing here is "solved". Every "better" or "worse" names its instrument and its n.
One-sentence answer: the mechanism works exactly as designed, delivers essentially all of its own
ceiling, and the ceiling is too low to matter for layout — but it is the best realism result in the
programme. Layout-matched retrieval nearly doubles the cartoon lumen IoU at start_index 3
(0.0835 vs 0.0452 for the single real donor, better on 39 of 40 paired cartoons), which is
99-114% of what a perfect reproduction of the chosen donor windows could score (the assignment
mosaic, 0.081) and 12% of the cartoon ceiling (0.694). It is still far below the F2 adapter's
2.6-5.4x-over-two-pass layout control. What it does buy is realism: envelope distance
3.86 against 6.30 for the single donor and 3.51 for real held-out fields, better on 37 of 40
paired cartoons, with 35 of 40 outputs inside the real held-out band against 14 of 40. The A4
repetition collapse is fixed (bag-diverse SSIM 0.61-0.67, not 0.98) and its envelope gain survived
(D 3.35, better than A4's 3.96). The --inflamed arm failed for a reason worth recording: it ranked
candidates on a density index that is anti-correlated with the density the evaluation reports.
0. What was built and run
| deliverable | path | size |
|---|---|---|
| generator, one new token source + per-job bag seeding | code/pixcell_generate.py |
959 lines (was 781) |
| CPU tests, 13 of them | code/test_generate_cpu.py |
956 lines (was 768) |
| the matcher | code/layout_match.py |
509 lines (new) |
| manifest writer | code/make_a5_jobs.py |
192 lines (new) |
| assignments, 40 + 10 inflamed | assignments/*.json, summary.json, summary-inflamed.json |
520 KB |
| match sheets | assignments/match-sheet.png, match-sheet-inflamed.png |
|
| fleet package | packages/a5/ (code/, jobs/, assignments/, worker.sh, STAGE_LIST, MANIFEST-ORDER.txt) |
1.2 MB |
| drivers | packages/a5/run_a5.sh, wait_and_retrieve.sh, retrieve_a5.sh, eval_a5.sh |
|
| merged records, 160 | packages/a5/retrieved/records.jsonl + coverage.json |
|
| retrieved outputs, 160 PNGs | packages/a5/retrieved/out/a5/pass{1,2}/<category>/ |
2.2 GB |
| the exact VM label maps the generator saw | packages/a5/retrieved/aux/cartoons-vm/a1v2/ (60 maps) |
18 MB |
| A5 measurements | packages/a5/analyse_a5.py (793 lines) -> packages/a5/eval/ |
|
| harness output | eval/features.csv, scores.csv, screen-embed.csv, screen-ncc.csv, stage-*.json |
|
| A5 tables | eval/a5-table.csv/.json, a5-headline.json, a5-layout-iou.csv/.json, a5-paired-vs-a1v2.csv/.json, a5-within-arm-ssim.json, a5-mosaic-features.csv/.json, a5-seams.csv, density-instrument-check.json |
|
| contact sheets | eval/sheets/a5-<category>.png, a5-zoom-<category>.png, a5-mosaic-vs-output.png |
Commands, in order, all from the programme root
(P=../../../research/sgh-synthetic-histopathology/.venv/bin/python):
$P code/test_generate_cpu.py # 13/13
$P code/layout_match.py --out assignments # 40 assignments, 8.2 s
$P code/layout_match.py --out assignments --inflamed # 10 more, 0.8 s
python3 code/make_a5_jobs.py --verify # 160 jobs, verify OK
./packages/a5/run_a5.sh # up, stage, symlink, render a1v2 x12, launch, watch
./packages/a5/wait_and_retrieve.sh # -> retrieve_a5.sh: retrieve, VM label maps, down
./packages/a5/eval_a5.sh # merge, features|screens|sheets, analyse_a5
1. The code (deliverable 1)
token_assignment — a new primary token source
"token_assignment": "assignments/<stem>.json"
The file names, for every canvas window origin, the real donor window that supplies that window's tokens:
{"cartoon": "...", "category": "...", "canvas": [4096, 2048], "windows": [
{"origin": [0, 0], "file": "[local]/train<cat>/<field>.png",
"oy": 0, "ox": 3072, "transform": "rot90",
"score": 0.298531, "lumen_iou": 0.114297, "epithelium_iou": 0.368, "candidates": 4032}, ...]}
transform is one of id, rot90, rot180, rot270, flipH, flipV, transpose, antitranspose; the
generator applies it with PIL's Image.transpose to the 1024 px donor crop before the UNI2-h
encode. The token cache key carries the transform ((sha, oy, ox) for id, (sha, oy, ox,
transform) otherwise), so one donor window encoded two ways is encoded twice and the two grids can
never share a slot. Windows are grouped by donor file so a 4096x2048 PNG is decoded once per job.
Recorded per job: token_assignment, token_assignment_sha256, token_assignment_windows,
token_assignment_donor_fields, token_assignment_donors, and token_assignment_picks — one entry
per canvas window with origin, window_index, donor_file, donor_sha256, donor_origin,
transform and the matcher's own score. A bare list, and a dict keyed "<oy>,<ox>", are also
accepted. A file that does not cover every window, names an unknown transform, or asks for a crop
outside the donor, raises rather than falling back.
Precedence is now token_file > token_patchbag > token_assignment > token_library >
token_blend > token_reference > reference, then token_shuffle as a post-step. The
pre-existing chain is unchanged, so no old manifest changes behaviour.
Per-job patch-bag seeding
A4's token_patchbag drew from default_rng(seed*1000 + window_index), which does not depend on
the cartoon, so all five cartoons of a cell got the identical 21 token grids and collapsed to one
image (A4_RESULT.md section 4: within-arm SSIM 0.98). A5 adds two optional fields and leaves the
default stream exactly as A4's so A4's records stay reproducible:
"token_patchbag": {"library": "token-library/", "category": "<cat>",
"per_window": true, "seed": 52001} // or "salt": "job_id"
seed replaces the job seed as the bag's base; salt adds an integer, or with the string
"job_id" adds int(sha256(job id)[:16], 16) % 10**6 (hash() is per-process salted and cannot be
used). Recorded: token_patchbag_seed, token_patchbag_salt, token_patchbag_salt_source,
token_patchbag_seed_base. The a5-bag manifest sets one distinct seed (52001-52020) per cartoon.
Two supporting edits: code/resolve_jobs.py resolves and existence-checks token_assignment, and
packages/sweep-v1/evaluate_sweep.py's _library_origins_for / _library_field_count read
token_assignment_picks (section 10).
Tests — 13/13 pass
code/test_generate_cpu.py under research/sgh-synthetic-histopathology/.venv/bin/python, stub
encoder and stub pipeline, no weights. The 11 A4 tests are unchanged and still pass; two are new.
PASS old-schema jobs are byte-for-byte unchanged against the canonical generator
PASS token_library picks the nearest composition per window
PASS token_blend weights and 'self' resolve to the weighted sum
PASS token_file broadcasts one grid to every window, both accepted shapes
PASS token_shuffle permutes the 16 tokens of every window, from the documented RNG
PASS token_patchbag assembles 16 real patches drawn from the library, per window or once
PASS token_assignment encodes the named donor window under the named transform, per canvas window
3 windows -> 2 donor fields; transpose/id on one crop give different grids; 8 distinct
dihedral images; picks carry origin, donor, transform and the match score
PASS token_patchbag seeds differ per job when the manifest says so, and A4's default is unchanged
default stream identical to A4 (32 draws verified); explicit per-cartoon seeds and
salt=job_id both give distinct bags; salt is reproducible and bounded
PASS precedence is token_file > token_patchbag > token_assignment > token_library > token_blend
> token_reference > reference
PASS every record carries the provenance fields evaluation needs
PASS resume skips ids already in records.jsonl and adds nothing on a rerun
PASS resolve_jobs resolves PASS1 against out/ and drops jobs whose inputs are missing
PASS split_manifests keeps every pass-2 job on its pass-1 slot and places every job
13/13 passed
The assignment test is arithmetic, not aesthetic. The stub encoder is a pure function of the pixels
it is shown, so the test re-does the crop and the PIL transform itself and demands the tensors handed
to the denoiser equal those. Two canvas windows name the same donor crop under transpose and
id: their grids must differ (the cache-key check). All eight named transforms are checked against
PIL.Image.transpose and shown to produce eight distinct images. The seeding test re-derives A4's 32
draws independently and demands the default stream still produces them.
2. The assignments (deliverable 2)
code/layout_match.py, CPU, 8.2 s for all 40 cartoons + 0.8 s for the 10 inflamed ones.
For each of the 40 a1v2 across/oblique cartoons (4 categories x seeds 11-15 x 2 cuts) and each of
its 21 canvas windows, the cartoon's label map gives a lumen mask (label 1) and an epithelium mask
(labels 2, 3, 4). Every candidate is one window of one real training field of the same category
under one of the eight dihedral transforms: 24 fields x 21 origins x 8 = 4032 candidates per
window, scored IoU(lumen) + 0.5 * IoU(epithelium) at 1/8 resolution. Donor label maps are F2's
segmentations (pairs/data/train/<cat>/<stem>_labels.png).
Masks are downsampled by 8x8 block mean thresholded at 0.5, not by subsampling, because a block
mean commutes exactly with the dihedral group on a 1024 px window (128 whole blocks a side) —
verify_transforms() asserts both that commutation and that every one of the eight numpy transforms
is pixel-identical to the PIL method the generator will apply. Empty-vs-empty windows score IoU 1.0.
How good are the matches?
Per window, over all 40 x 21 = 840 windows:
| statistic | value |
|---|---|
| best-candidate lumen IoU, median (p10 / p90 / max) | 0.121 (0.077 / 0.188 / 0.340) |
| best-candidate epithelium IoU, median | 0.304 |
| best-candidate score, median | 0.277 |
| median candidate's score | 0.101 |
| gain of the best over the median candidate | +0.176 |
| 99th-percentile candidate's score | 0.218 |
| category | median best lumen IoU | median best epi IoU | donor fields per canvas |
|---|---|---|---|
| normal | 0.107 | 0.279 | 11 |
| hpylori_gastritis | 0.123 | 0.320 | 10 |
| intestinal_metaplasia | 0.132 | 0.327 | 10 |
| mixed | 0.128 | 0.295 | 12 |
gastritis, --inflamed pool |
0.111 | 0.325 | 8 |
Per canvas the assignment uses a median of 11 distinct donor fields (range 5-14, verified in the records) and 20 distinct donor windows out of 21 — no single field supplies the canvas, which was the point. A median of 7 of the 8 transforms appear in one canvas and only 2 of 21 windows keep the identity, so the dihedral group is doing real work.
The search works: the chosen candidate beats the median candidate by 0.176 of score and beats the 99th percentile of candidates by 0.06. But the best real window available is still only 0.12 IoU from the cartoon window it is matched to. That is a property of the data, not of the search: over 4032 real candidates there is no gastric window whose lumen mask lines up with a synthetic one.
The match sheet, described honestly
assignments/match-sheet.png (4 cartoons, one per category, 3 windows each; cartoon window label map
| best donor window, transformed | its label map, transformed):
- Column 1 (cartoon) is the same drawing everywhere: a pink ground with regularly spaced dark bean-shaped stromal nuclei and round-to-oval white lumens each ringed by a nuclear palisade. IM and mixed add pale-blue goblet vacuoles.
- Column 2 (the chosen real window) is real gastric mucosa and looks nothing like a field of isolated rosettes: long branching gland profiles, surface folds with a continuous columnar palisade, whole windows of loose lamina propria.
- Column 3 (the donor's label map) makes the mismatch explicit. Real lumen is a set of long thin slits running along gland walls; cartoon lumen is a set of compact blobs. The matcher is choosing donors with roughly the right amount and spread of lumen, not the right shapes.
Sheet windows score lumen IoU 0.084-0.186 and epithelium IoU 0.29-0.50.
assignments/match-sheet-inflamed.png is the same under the inflamed restriction; its middle row is
visibly more cellular (dense small dark nuclei filling the lamina propria between two glands), the
other two rows are not obviously different from the unrestricted pick.
Were the assignments computed on the right bytes?
They were computed from the local Mac label maps; the generator conditions on the VM's own
render. Recomputing all 40 assignments from the sweep-v2 VM-rendered a1v2 label maps changes
3 of 840 windows (99.6% identical) and leaves the median best lumen IoU at 0.1221 to four
decimals. (sweep-v2 had already measured VM-vs-Mac a1v2 lumen-mask IoU at median 1.000, min 0.99998.)
Every IoU in section 5 is measured against retrieved/aux/cartoons-vm/a1v2, the exact bytes slot a
rendered and conditioned on.
3. The manifests and the run (deliverable 3)
code/make_a5_jobs.py, 160 jobs in five manifests, --verify clean (no duplicate id or output,
every PASS1/ reference produced by an earlier manifest, every token_assignment file present with
21 windows of the right category, 20 distinct patch-bag seeds).
| manifest | jobs | what |
|---|---|---|
a5-pass1 |
40 | a1v2 across+oblique, img2img si12 seed 51001 on the cartoon, token_assignment — arm match |
a5-pass2 |
80 | each cartoon at si6 (seed 51002) and si3 (seed 51003) from its own pass-1 output, same assignment — arms match-si6, match-si3 |
a5-inflamed-pass1 |
10 | the 10 gastritis cartoons, pass 1, inflamed assignment — arm inflamed |
a5-inflamed-pass2 |
10 | those at si3 — arm inflamed-si3 |
a5-bag |
20 | the 20 across cartoons, si3 from the a5 pass-1 output, token_patchbag per-window with a distinct seed 52001-52020 — arm bag-diverse |
Deviation from the brief: the inflamed manifest is split in two. resolve_jobs.py checks every
input before a manifest runs, so a pass-2 job whose pass-1 output is produced by the same manifest
would be dropped as missing. Same 20 jobs, two files, run consecutively.
Setup and timeline (UTC, 8 September)
| what | time |
|---|---|
instances start issued / GPU ready |
20:25:53 / 20:26:49 (56 s) |
| stage, 6 items (1.2 MB incl. 50 assignments) | 20:26:57-20:28:43 |
symlink token-library, token-stats, cartoons/{a1,d1,e2,b1} from slot a's slot0 |
20:29:06 |
render cartoons/a1v2 on the VM, 12 parallel chunks |
20:29:06-20:35:53 (397 s) |
| worker start / first generation | 20:36:10 / ~20:36:30 |
a5-pass1 done (40) |
21:00:55 |
a5-pass2 done (80) |
22:06:39 |
a5-inflamed-pass1 done (10) |
22:12:42 |
a5-inflamed-pass2 done (10) |
22:22:09 |
a5-bag done (20); WORKER_DONE, exit 0, {"ran":5,"skipped":0,"failed":0} |
22:37:38 |
retrieve 2.2 GB, SHA256SUMS OK (160 files) |
22:38:35-22:51:16 (761 s, 2.9 MB/s) |
| retrieve 60 VM label maps (18 MB) | 22:51:17-22:51:27 |
down a -> TERMINATED, confirmed by fleet.sh status |
22:51:32-22:52:30 |
No preemption; fleet.sh watch polled 96 times and exited on WATCH WORKER_DONE 0.
cartoons/a1v2 did not exist on slot a (sweep-v2 rendered it on slots b and c), so it had to be
built here. Rendering it serially would have idled the A100 for 60-75 minutes; twelve concurrent
render_chunk.py quarters on the 12 vCPUs did all 60 cartoons in 6.6 minutes, and
manifest_dir.py published the set atomically so worker.sh could never see a half-built one.
Throughput and cost
| stage | n | median s |
|---|---|---|
pass 1, si12, token_assignment |
40 | 35.97 |
| pass 1, si12, inflamed assignment | 10 | 34.05 |
pass 2, si6, token_assignment |
40 | 49.47 |
pass 2, si3, token_assignment |
40 | 48.16 |
| pass 2, si3, inflamed assignment | 10 | 54.34 |
pass 2, si3, token_patchbag |
20 | 43.84 |
Total generation 7063 s = 1.96 GPU-hours over 160 jobs, mean 44.1 s. The ~4 s that a
token_assignment pass-1 job costs over A4's 32 s single-donor pass 1 is the UNI2-h encode of up to
21 separate 1024 crops (against one cached donor field), which is cheaper than expected because the
crops are cached across the jobs of a manifest.
Powered 20:25:53 to 22:52:30 = 146.3 min = 2.44 h = USD 5.17 at USD 2.12/h, plus about USD 0.27 of egress for the 2.2 GB retrieved. GPU utilisation 1.96 / 2.44 = 80%; the 20% is the 6.6-minute render and the 12.7-minute retrieval, both unavoidable on this slot.
Coverage and provenance: clean
packages/a5/merge_records.py: 160 records read, 160 written, 0 duplicates. Per manifest,
expected = records = PNGs (40/80/10/10/20), no missing ids. All 160 PNGs were re-hashed on this Mac
and all 160 match their record's output_sha256; no zero-byte file. All 160 token_source
values equal their extra.expected_token_source — token_assignment 140, token_patchbag 20.
Every one of the 140 assignment records carries exactly 21 windows and 5-14 distinct donor
fields; all 20 patch-bag records carry 20 distinct seeds, so A4's shared-seed collapse cannot
recur by construction.
4. The ceiling: what layout-matched retrieval could possibly deliver
This measurement needs no GPU and it frames everything else. For each assignment, analyse_a5.py
builds the assignment mosaic: the 21 transformed donor crops pasted at their canvas origins (hard
edges, later windows over earlier — not the generator's Hann blend). The mosaic is real tissue,
arranged exactly as the matcher chose, so it upper-bounds what any generator that reproduced its
assigned donor windows perfectly could score.
Measured on the harness's own instrument (analysis_masks lumen against the cartoon's own VM label
map, exactly as evaluate_sweep._d1_one measures every other arm in the programme). chance is the
same output against the same label map rotated 180 degrees:
| what | n | lumen IoU | chance (rot180) | epithelium IoU |
|---|---|---|---|---|
| the cartoon PNG itself (instrument ceiling) | 40 | 0.6940 | 0.0173 | 0.3231 |
| the assignment mosaic | 40 | 0.0807 | 0.0373 | 0.3093 |
| the inflamed mosaic | 10 | 0.0760 | 0.0452 | 0.3071 |
| a1v2 pass 1 si12, single donor | 40 | 0.6170 | 0.0162 | 0.1773 |
| a1v2 pass 2 si6, single donor | 40 | 0.0065 | 0.0000 | 0.3010 |
| a1v2 pass 2 si3, single donor | 40 | 0.0452 | 0.0283 | 0.3122 |
By category the mosaic ceiling is normal 0.083, gastritis 0.090, IM 0.065, mixed 0.085.
Read that carefully. Layout-matched retrieval, executed perfectly, is worth 0.081 against a single-donor two-pass 0.045 and a cartoon ceiling of 0.694. The idea can at most take the programme from 6.5% of the cartoon ceiling to 12% of it.
The epithelium IoU is uninformative on this instrument: everything from the cartoon itself (0.323) to
the si6 arm (0.301) sits in a 0.02 band, because analysis_masks' epithelium is the closed
hematoxylin band and that band covers most of any tissue-filled field. Only the lumen IoU
discriminates. Note also that the single-donor si3 arm's 0.0452 is only 1.6x its own chance level
of 0.0283 — the number the programme has been quoting as "the layout survives a little" is barely
above rotating the label map.
The mosaic is realistic, because it is real
The same C1/C2 feature pass, on the mosaics (eval/a5-mosaic-features.json):
| set | n | ring frac | ring/mm2 | nuc/mm2 | stromal nuc/mm2 | Laplacian | pale frac |
|---|---|---|---|---|---|---|---|
| assignment mosaic, all | 40 | 0.877 | 120 | 6212 | 3510 | 21.4 | 0.272 |
| — normal / gastritis / IM / mixed | 10 each | 0.904 / 0.951 / 0.846 / 0.813 | 97 / 132 / 116 / 137 | 6142 / 6212 / 5818 / 7069 | 4191 / 3426 / 3448 / 2842 | 21.8 / 21.3 / 18.7 / 22.9 | 0.272 / 0.288 / 0.299 / 0.215 |
| gastritis, inflamed mosaic | 10 | 0.951 | 141 | 5968 | 3169 | 20.7 | 0.312 |
| real held-out, for scale | 22 | 0.71-0.85 | 5996-6762 | 2134-4588 | 20.4-23.6 | 0.176-0.229 |
The mosaic sits in the real range on ring fraction, nuclear density and Laplacian detail, as it must. So the ceiling above is not an artefact of a broken ceiling image.
5. The table
Per category x arm, n = 10 except bag-diverse (5, across only) and the reference rows;
medians. IoU is cartoon lumen IoU against the own (VM) label map; band counts outputs inside the
category's real held-out envelope band; cos is phikon-v2 max cosine against 118 real reference
fields (fail line 0.95); src is the fraction of the canvas's phikon tiles whose nearest real field
is one the tokens actually came from. Full file: packages/a5/eval/a5-table.csv.
| cell | n | ring | env D | band | lap | str nuc/mm2 | IoU | cos | src |
|---|---|---|---|---|---|---|---|---|---|
| normal | |||||||||
| a5 match (pass 1, si12) | 10 | 0.149 | 6.90 | 0 | 16.83 | 1119 | 0.666 | - | - |
| a1v2 pass1 (si12) | 10 | 0.247 | 7.92 | 0 | 17.50 | 1339 | 0.695 | - | - |
| a5 match-si6 | 10 | 0.000 | 8.52 | 0 | 17.91 | 1339 | 0.005 | 0.754 | 0.250 |
| a1v2 si6 donor | 10 | 0.000 | 9.43 | 0 | 18.80 | 1710 | 0.001 | 0.859 | 1.000 |
| a5 match-si3 | 10 | 0.438 | 4.37 | 5 | 18.80 | 1843 | 0.069 | 0.771 | 0.438 |
| a1v2 si3 donor | 10 | 0.702 | 6.77 | 0 | 19.70 | 3187 | 0.041 | 0.873 | 1.000 |
| a5 bag-diverse | 5 | 0.417 | 3.87 | 3 | 20.38 | 2096 | 0.040 | 0.804 | 0.000 |
| real held-out | 4 | 0.884 | 3.33 | 4 | 22.78 | 4211 | - | - | - |
| hpylori_gastritis | |||||||||
| a5 match (pass 1) | 10 | 0.164 | 10.89 | 0 | 16.96 | 1483 | 0.683 | - | - |
| a5 inflamed (pass 1) | 10 | 0.182 | 10.89 | 0 | 16.88 | 1457 | 0.681 | - | - |
| a1v2 pass1 (si12) | 10 | 0.233 | 10.76 | 0 | 17.38 | 1458 | 0.675 | - | - |
| a5 match-si6 | 10 | 0.018 | 9.22 | 0 | 17.65 | 1064 | 0.018 | 0.782 | 0.375 |
| a1v2 si6 donor | 10 | 0.000 | 8.28 | 4 | 18.60 | 1199 | 0.001 | 0.862 | 0.875 |
| a5 match-si3 | 10 | 0.324 | 5.76 | 10 | 18.20 | 1311 | 0.089 | 0.805 | 0.438 |
| a5 inflamed-si3 | 10 | 0.347 | 5.83 | 10 | 17.92 | 1237 | 0.080 | 0.810 | 0.438 |
| a1v2 si3 donor | 10 | 0.225 | 6.28 | 10 | 19.13 | 1325 | 0.046 | 0.864 | 1.000 |
| a5 bag-diverse | 5 | 0.281 | 5.86 | 5 | 19.33 | 1428 | 0.033 | 0.825 | 0.500 |
| a4 bag-window si3 (a1 cartoons) | 5 | 0.276 | 5.14 | 5 | 19.90 | 1815 | - | 0.822 | 0.500 |
| real held-out | 6 | 0.807 | 4.19 | 6 | 20.40 | 4588 | - | - | - |
| intestinal_metaplasia | |||||||||
| a5 match (pass 1) | 10 | 0.246 | 8.56 | 0 | 15.53 | 1083 | 0.545 | - | - |
| a1v2 pass1 (si12) | 10 | 0.227 | 8.60 | 0 | 15.95 | 1052 | 0.548 | - | - |
| a5 match-si6 | 10 | 0.053 | 5.24 | 0 | 15.86 | 1087 | 0.073 | 0.794 | 0.563 |
| a1v2 si6 donor | 10 | 0.000 | 5.72 | 0 | 18.61 | 1090 | 0.003 | 0.857 | 0.875 |
| a5 match-si3 | 10 | 0.425 | 3.36 | 10 | 16.32 | 1399 | 0.084 | 0.818 | 0.500 |
| a1v2 si3 donor | 10 | 0.454 | 4.81 | 4 | 20.08 | 2692 | 0.046 | 0.871 | 0.938 |
| a5 bag-diverse | 5 | 0.432 | 3.24 | 5 | 18.15 | 1218 | 0.038 | 0.785 | 0.250 |
| a4 bag-window si3 (a1 cartoons) | 5 | 0.489 | 2.84 | 5 | 17.70 | 1259 | - | 0.790 | 0.125 |
| real held-out | 6 | 0.774 | 2.58 | 6 | 23.62 | 2134 | - | - | - |
| mixed | |||||||||
| a5 match (pass 1) | 10 | 0.174 | 9.99 | 0 | 17.29 | 1588 | 0.574 | - | - |
| a1v2 pass1 (si12) | 10 | 0.219 | 10.30 | 0 | 17.23 | 1610 | 0.569 | - | - |
| a5 match-si6 | 10 | 0.032 | 5.84 | 0 | 18.95 | 1241 | 0.077 | 0.810 | 0.438 |
| a1v2 si6 donor | 10 | 0.106 | 8.08 | 0 | 19.95 | 1125 | 0.075 | 0.860 | 0.875 |
| a5 match-si3 | 10 | 0.323 | 3.65 | 10 | 20.18 | 1523 | 0.097 | 0.825 | 0.313 |
| a1v2 si3 donor | 10 | 0.472 | 5.75 | 0 | 21.65 | 1525 | 0.044 | 0.871 | 1.000 |
| a5 bag-diverse | 5 | 0.287 | 3.22 | 5 | 21.01 | 1558 | 0.039 | 0.800 | 0.500 |
| real held-out | 6 | 0.774 | 3.66 | 6 | 22.77 | 3528 | - | - | - |
Pooled over the four categories (eval/a5-headline.json):
| arm | n | ring | env D | band | lap | str nuc | IoU | chance | cos | src | seam |
|---|---|---|---|---|---|---|---|---|---|---|---|
| a5 match (pass 1, si12) | 40 | 0.174 | 8.97 | 0/40 | 16.87 | 1286 | 0.6062 | 0.0152 | - | - | 1.083 |
| a1v2 pass1 (si12) | 40 | 0.227 | 9.00 | 0/40 | 17.15 | 1386 | 0.6170 | 0.0162 | - | - | 1.073 |
| a5 match-si6 | 40 | 0.026 | 6.76 | 0/40 | 17.82 | 1203 | 0.0366 | 0.0020 | 0.790 | 0.375 | 1.056 |
| a1v2 si6 donor | 40 | 0.000 | 7.91 | 4/40 | 18.69 | 1497 | 0.0065 | 0.0000 | 0.860 | 0.875 | 1.052 |
| a5 match-si3 | 40 | 0.354 | 3.86 | 35/40 | 18.76 | 1494 | 0.0835 | 0.0260 | 0.809 | 0.375 | 1.037 |
| a1v2 si3 donor | 40 | 0.450 | 6.30 | 14/40 | 19.88 | 1979 | 0.0452 | 0.0283 | 0.871 | 1.000 | 1.034 |
| a5 inflamed-si3 | 10 | 0.346 | 5.83 | 10/10 | 17.92 | 1237 | 0.0799 | 0.0315 | 0.810 | 0.438 | 1.034 |
| a5 bag-diverse | 20 | 0.355 | 3.35 | 18/20 | 19.80 | 1527 | 0.0382 | 0.0257 | 0.801 | 0.375 | 1.041 |
| a1 si3 donor (OLD a1 cartoons) | 60 | 0.503 | 5.64 | 22/60 | 19.05 | 2193 | - | - | 0.868 | 1.000 | - |
| a4 bag-window si3 (a1 cartoons) | 10 | 0.405 | 3.96 | 10/10 | 18.76 | 1508 | - | - | 0.801 | 0.312 | - |
| real held-out | 22 | 0.841 | 3.51 | 22/22 | 22.77 | 3862 | - | - | - | - | 1.027 |
Paired deltas, a5 minus the a1v2 single-donor arm on the same cartoon at the same depth
Medians of 40 per-cartoon deltas; n+ counts positive deltas (eval/a5-paired-vs-a1v2.csv). For IoU
and ring, positive is better; for D, negative is better.
| si | arm | n | d IoU | n+ | d D | n+ | d ring | n+ | d lap | n+ | d str nuc | n+ |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 12 | match (pass 1) | 40 | -0.0056 | 17 | -0.05 | 19 | -0.071 | 9 | -0.19 | 10 | -87 | 16 |
| 6 | match-si6 | 40 | +0.0171 | 24 | -0.20 | 16 | +0.005 | 20 | -1.33 | 10 | -162 | 13 |
| 3 | match-si3 | 40 | +0.0370 | 39 | -1.66 | 3 | -0.133 | 16 | -1.81 | 9 | -379 | 16 |
At si3 the layout gain is positive on 39 of 40 cartoons and the envelope gain on 37 of 40. Neither is noise. A4's best paired IoU delta was +0.0030, positive on 6 of 10.
6. Diversity: the A4 collapse is fixed
Mean pairwise SSIM inside each (category, arm) cell, 10 images / 45 pairs unless noted
(eval/a5-within-arm-ssim.json):
| set | normal | gastritis | IM | mixed |
|---|---|---|---|---|
| real held-out (n=4-6) | 0.038 | 0.047 | 0.035 | 0.034 |
| a1v2 pass1 (si12) | 0.377 | 0.435 | 0.398 | 0.435 |
| a5 match (pass 1) | 0.390 | 0.433 | 0.408 | 0.447 |
| a1v2 si6 donor | 0.620 | 0.585 | 0.557 | 0.565 |
| a5 match-si6 | 0.643 | 0.610 | 0.599 | 0.641 |
| a1v2 si3 donor | 0.619 | 0.574 | 0.548 | 0.557 |
| a5 match-si3 | 0.631 | 0.597 | 0.578 | 0.639 |
| a5 inflamed-si3 | - | 0.583 | - | - |
| a5 bag-diverse (n=5) | 0.672 | 0.642 | 0.606 | 0.651 |
| (A4 bag-window si3, shared seed) | - | 0.986 | 0.981 | - |
bag-diverse uses the same construction A4's bag-window did — 16 random (library window, patch)
draws per canvas window — with one distinct seed per cartoon. A4's within-arm SSIM was 0.98; A5's
is 0.61-0.67, the same range as every other pass-2 arm. The one-line seeding change removed the
collapse completely, and the envelope gain that made A4 flag bag-window for follow-up survived it:
D 3.35 pooled against A4's 3.96, 18 of 20 inside the band.
No arm is anywhere near real diversity (0.034-0.047). Layout-matched retrieval is slightly more repetitive than the single donor (+0.03 to +0.08 SSIM), which is expected: the assignment is a deterministic function of the cartoon and neighbouring cartoons of the same category draw on overlapping donor sets.
7. Window-boundary seams: measured, and absent
A4 recorded seams as "not measured, not absent"; A5's construction (21 windows from up to 14 slides
on one canvas) is the one most prone to them. analyse_a5.py adds the instrument: for every column of
the full-resolution grey image, the mean absolute horizontal difference, then the value at each window
boundary (x = 512, 1024, ...) divided by the median of the 32 columns around it.
| set | n | at window boundaries | off-boundary null | max |
|---|---|---|---|---|
| real held-out (no windows at all) | 22 | 1.027 | 1.066 | 1.27 |
| a1v2 si3 donor | 40 | 1.034 | 1.032 | 1.26 |
| a5 match-si3 | 40 | 1.037 | 1.046 | 1.21 |
| a5 match-si6 | 40 | 1.056 | 1.047 | 1.29 |
| a5 inflamed-si3 | 10 | 1.034 | 1.058 | 1.21 |
| a5 bag-diverse | 20 | 1.041 | 1.033 | 1.24 |
| assignment mosaic (hard pastes — positive control) | 40 | 5.953 | 1.035 | 10.92 |
| inflamed mosaic | 10 | 6.872 | 1.050 | 10.33 |
The mosaics score 6-7x their own off-boundary null, so the statistic detects seams when they exist. Every generated a5 arm sits at 1.03-1.06, indistinguishable from real fields and from the single-donor arm. The Hann-tapered MultiDiffusion blend absorbs a 14-slide mosaic without leaving a visible boundary. This is the cleanest positive result in the package after the envelope.
8. Gastritis: the inflamed pool was selected on the wrong instrument
The gastritis question is EVAL_RESULT.md (j): the infiltrate that defines the category is largely
absent — stromal nuclear density 1325/mm2 for the a1v2 single-donor si3 arm against a real held-out
4588/mm2 — and the pale candidates game the ring metric by fading. A5's --inflamed arm restricts
the pool to the densest 30% of gastritis windows and asks whether the infiltrate comes back.
It did not, and the reason is that the selector and the evaluator do not measure the same thing.
| instrument | what it is |
|---|---|
token-library index composition[3] |
stroma_nucleus_density_norm: nucleus area as a fraction of stroma area, from build_token_library.window_composition, divided by 0.35 and clipped. This is what --inflamed ranks on. |
| F2 label map | label-6 pixels over labels 5+6 — nucleus area fraction of stroma, from pairs/segment_real.py |
morphometrics.extract str_nuc_density_per_mm2 |
nucleus instance count per mm2 of stroma. This is what every table in the programme reports and what "4588" is. |
Measured on 60 gastritis windows drawn evenly across the whole library-index range
(eval/density-instrument-check.json), each 1024 px window scored by all three:
| pair | Spearman | Pearson |
|---|---|---|
library index vs str_nuc_density_per_mm2 |
-0.341 | -0.029 |
| library index vs F2 label-6 fraction | +0.199 | -0.067 |
F2 label-6 fraction vs str_nuc_density_per_mm2 |
-0.637 | -0.633 |
Selecting the top 30% by the library index gives windows whose str_nuc_density_per_mm2 median is
6816/mm2 against 10532/mm2 for the rest — the "inflamed" pool is less nucleated by the
instrument the evaluation uses. (Window-level densities are much higher than canvas-level ones
because morphometrics computes its percentile thresholds within the image; they are comparable to
each other, not to the 4588 field-level figure.)
The mosaics confirm it end to end at canvas scale: the gastritis inflamed mosaic carries 3169 stromal nuclei/mm2 against the unrestricted assignment mosaic's 3426 — the restriction moved the source tissue away from the target, before any generation happened. And the outputs follow:
| gastritis si3 arm | n | str nuc/mm2 | nuc/mm2 | pale frac | ring | ring count | env D |
|---|---|---|---|---|---|---|---|
| a5 inflamed-si3 | 10 | 1237 | 5915 | 0.137 | 0.347 | 82.5 | 5.83 |
| a5 match-si3 | 10 | 1311 | 5989 | 0.128 | 0.324 | 73.5 | 5.76 |
| a5 bag-diverse | 5 | 1428 | 6073 | 0.122 | 0.281 | 81.0 | 5.86 |
| a1v2 si3 donor | 10 | 1325 | - | 0.154 | 0.225 | 58.0 | 6.28 |
| real held-out | 6 | 4588 | - | 0.229 | 0.807 | 43.5 | 4.19 |
The inflamed arm is 74/mm2 below the unrestricted arm and 3351/mm2 below real. The mechanism was
mis-aimed. This is a fault in this package's execution, not a property of the generator: the fix is to
rank candidate windows on a nucleus count measure, which is one line in
layout_match.inflamed_windows.
One thing the a5 arms do fix: the fading. The a1v2 single-donor gastritis arm has pale tissue fraction 0.154 and only 58 detected rings; the a5 arms are less pale (0.122-0.137) and carry 73-83 rings. But real gastritis has 43.5 rings and pale fraction 0.229, so a5 has moved away from real on both counts in the other direction — it over-produces small rings rather than fading into large ones.
9. The contact sheets, described honestly
eval/sheets/a5-<category>.png — five rows (cartoons s11-s15 across), eight columns: cartoon |
match (pass 1) | match-si6 | match-si3 | inflamed-si3 (gastritis only) | bag-diverse | a1v2 si3 donor
| real held-out, 768 px crops at the same canvas position (1664, 640, 2432, 1408), each labelled ring
/ D / lap / IoU. Four a5-zoom-* sheets show one cartoon at ~640 px, and a5-mosaic-vs-output.png
puts the assignment mosaic beside what pass 2 made of it.
a5-mosaic-vs-output.png (cartoon | assignment mosaic | a5 match-si3 | a1v2 si3 donor | real).
The mosaic column is unmistakably a tiling: in a 768 px crop you see two to four real windows meeting
at hard vertical and horizontal edges, with abrupt changes of stain and scale across them. The
match-si3 column beside it has no seams at all and reads as one continuous field — which is the
1.037 seam ratio, seen rather than computed. Comparing columns 3 and 4: the a5 output is
consistently paler and less saturated than the single-donor output, which in normal, IM and mixed
is a strongly over-stained purple. That difference is the -1.66 D.
a5-hpylori_gastritis.png. Column 2 (match, pass 1 si12) is a soft, low-contrast, faintly
blurry version of the cartoon — a smoothed drawing, not tissue, exactly as its IoU 0.60-0.69 says.
Column 3 (match-si6) is the worst region of the package: rows 2 and 4 are pale and almost
structureless (ring 0.000-0.034). Column 4 (match-si3) is the first that reads as gastric mucosa:
row 1 shows crowded epithelial cells with clear vacuoles and a cluster of red cells, row 2 a coiled
gland with red cells in its wall, row 3 dense small dark nuclei among pale cells that reads as
inflamed lamina propria, row 5 a cellular field with red cells. Column 5 (inflamed-si3) is of the
same character and in rows 3 and 5 slightly more cellular. Column 7 (a1v2 si3 donor) is the
comparison that matters: row 2 is a heavily over-stained purple gland, and row 4 is a very pale
washed-out honeycomb at ring 1.000 — the exact metric-gaming failure EVAL_RESULT.md (j) flagged, and
it does not appear in any a5 column. Column 8 (real) is still more organised and more delicate than
any generated column, with wider gland lumens and thinner strands.
a5-zoom-intestinal_metaplasia.png at ~1:1.2. match-si3 shows crowded epithelial cells around
a clear lumen with dark basally-placed nuclei and pale vacuolated cytoplasm; bag-diverse shows a
gland with a columnar lining and a lumen; the a1v2 si3 donor is a dark over-stained purple palisade.
The real column is paler and more delicate than all of them, with a wide gland lumen and thin
fibrillar strands, and its nuclei carry speckled chromatin that none of the generated cells
reproduces (lap 23.6 against 16.3-20.1).
What is still absent from every pass-2 cell. The cartoon's geometry. In none of the 120 pass-2 images can I find the cartoon's lumen blobs in their cartoon positions; the improvement from 0.045 to 0.084 is real and systematic on the instrument but is not visible as "this output follows that drawing". The pass-1 column, by contrast, visibly is the cartoon.
10. Harness and instrument changes made in this package
Four, all additive; none can change a sweep-v1, sweep-v2 or a4 number.
packages/sweep-v1/evaluate_sweep.py:_library_origins_for()and_library_field_count()now also readtoken_assignment_picks, so a mosaic arm reports the fraction of tiles nearest any of its assigned donor fields and the true donor-field count per canvas, instead of the structural zero those functions returned for a source they had never seen. No sweep-v1/v2 or a4 record has that key.- A rot180 chance level on every layout-IoU cell (
_d1_onealready supportedunrot_labels; nothing supplied it before). It turns "IoU 0.045" into "0.045 against a chance level of 0.028". - The assignment mosaic, which turns "what is the ceiling of this idea?" into a measurement.
- A window-boundary seam statistic, calibrated on real fields (1.027 at boundaries, 1.066 off-boundary) and validated on the hard-pasted mosaics (5.95).
evaluate_sweep.py's sheets stage is still hard-wired to a1/d1/a2/a3/d2/e2/b1 and produced 0
sheets for a5 (it logs no data: for each and exits 0), exactly as for a4. The sheets are written
by analyse_a5.py, which also labels every cell with its layout IoU.
11. Verdicts
(1) Does layout-matched retrieval give cartoon-like layout at si3, at two-pass-si3 realism?
It gives a real, systematic, and small layout gain, and a large realism gain.
- Layout: 0.0835 against the single-donor 0.0452 on the same 40 cartoons and the same instrument,
better on 39 of 40 (paired median +0.0370). Against its own rot180 chance level of 0.026 that is
3.2x chance, where the single-donor arm is 1.6x chance. By category,
match-si3reaches 84% (normal), 99% (gastritis), 128% (IM) and 113% (mixed) of its assignment-mosaic ceiling — the generator reproduces the chosen donors' layout as well as, and in IM and mixed better than, a hard paste of those donors would (the mosaic's seams break its own lumen components; the blended output keeps them). The mechanism is not lossy. The ceiling is the limit. - That ceiling is 0.081 against a cartoon ceiling of 0.694, so the arm reaches 12% of the ceiling where the single donor reaches 6.5%. Against the F2 adapter's 0.26-0.56 it is not close — with the caveat that F2 measured a different mask (IoU of the output ring mask against the cartoon lumen mask) and scored the two-pass recipe at 0.10-0.13 where this instrument scores it at 0.045; the like-for-like statement is adapter 2.6-5.4x two-pass against A5's 1.85x.
- Realism: better, not merely preserved. Envelope D 3.86 vs 6.30, better on 37 of 40
paired cartoons, and 35 of 40 inside the real held-out band against 14 of 40. Pooled real
held-out D is 3.51, so
match-si3is essentially at the real envelope. Per category it is the best D the programme has produced for normal (4.37 vs a1v2's 6.77), IM (3.36 vs 4.81) and mixed (3.65 vs 5.75). - What it costs: ring fraction 0.354 vs 0.450 (worse on 24 of 40), Laplacian detail 18.76 vs 19.88 (worse on 31 of 40), stromal nuclear density 1494 vs 1979 (worse on 24 of 40). The outputs are paler and less nucleated than the single-donor arm; they are also much less over-stained, which is where the envelope gain comes from.
So: yes on realism, marginally on layout, and the ceiling says this cannot become the layout
answer. If the programme wants layout, the F2 adapter or region_img2img's per-label lock remain
the only candidates. If it wants the most realistic 4096x2048 field it can get from the two-pass
recipe, match-si3 and bag-diverse are now the best arms in the programme.
(2) Did inflamed windows restore the gastritis infiltrate without fading?
No, and the arm could not have worked. inflamed-si3 gives stromal nuclear density
1237/mm2 against match-si3's 1311 and a real held-out 4588 — it is 74/mm2 below the
unrestricted arm. Section 8 shows why: the library index the pool was ranked on is anti-correlated
(Spearman -0.341, n=60) with the str_nuc_density_per_mm2 the evaluation reports, and the
inflamed mosaic itself is less nucleated (3169) than the unrestricted mosaic (3426) before any
generation happens. The arm did not fade (pale fraction 0.137 vs the single donor's 0.154, and it
carries 82.5 rings against the single donor's 58), so the fading failure mode is absent — but that is
a property of layout-matched retrieval in general, not of the inflamed restriction. Re-run with a
nucleus-count ranking before drawing any conclusion about infiltrate.
(3) Is bag-diverse both diverse and realistic?
Diverse relative to A4, not relative to real; and yes, realistic. Within-arm SSIM 0.606-0.672
against A4's 0.981-0.986 — the per-cartoon seed removed the collapse entirely, and the five outputs of
a cell are now as different from one another as every other pass-2 arm's. They are nowhere near real
(0.034-0.047), and bag-diverse is in fact the most repetitive arm in this package by a small
margin. On realism the A4 finding survived and improved: pooled D 3.35 against A4's 3.96, with
18 of 20 inside the band, and per category IM 3.24 and mixed 3.22 — the two best envelope
distances anywhere in the programme. It buys nothing on layout (IoU 0.038 against its own chance level
of 0.026), which is expected: its tokens do not depend on the cartoon at all.
(4) Copy and inheritance position of the mosaic outputs
Cleaner than every single-donor arm, on every screen.
- phikon-v2 max cosine over all 880 candidate tiles is 0.862 against a 0.95 fail line
(min copy margin 0.088,
match-si3); per-arm medians 0.790-0.810 against the single-donor arm's 0.860-0.871. 0 flagged tiles. - Pixel NCC max 0.794 over 220 (candidate, reference) pairs against a 0.92 flag; 0 flagged
windows, 0
COPIED_PATCHES_FOUND. - Provenance spreads exactly as intended: the fraction of a canvas's phikon tiles whose nearest real field is one of its own token sources is 0.375-0.500 for the assignment arms against 1.000 for the single-donor arm. Each canvas is conditioned on a median of 11 distinct training fields rather than one, and no single field supplies it.
Read honestly, this cuts both ways. It lowers the risk that any one output resembles any one real field, and it raises the number of real slides each output touches from 1 to ~11 — which is the more conservative reading for the parked PixCell/UNI licence question, not the less.
12. What this changes, and what to do next
- The two-pass recipe now has a better default.
token_assignmentat si3 dominates the single-donor arm on the envelope (37/40), on layout (39/40) and on every copy screen, at the cost of ring fraction and Laplacian detail. Nothing about it needs a GPU beyond the generation itself: the matcher is 8 seconds of CPU for 40 cartoons. - The layout question is closed for retrieval. The assignment-mosaic ceiling of 0.081 is a
property of the real data — over 4032 candidates per window there is no real gastric window whose
lumen mask matches a synthetic one better than IoU 0.12. No smarter search over the same 24 fields
per category can beat it by much. Widening the pool (all 118 fields, or whole slides rather than
96 pre-cut fields) would raise the ceiling somewhat; conditioning that the denoiser reads per
position (F2's adapter,
region_img2img'slock_mask) is the only thing that can raise it a lot. - The seam risk is dead. A 14-slide mosaic on one canvas produces no measurable window-boundary artefact after the Hann blend (1.037 against a real-field null of 1.027). Future work can compose conditioning per window freely.
- Fix the inflamed ranking and re-run 20 jobs. One line in
layout_match.inflamed_windows, ~15 minutes of A100. Until then nothing is known about whether more infiltrate in gives more infiltrate out. - Stromal cellularity is now the biggest single gap and it is not category-specific: real 2134-4588/mm2 against 1218-2096 for every a5 arm and 1325-3187 for the single-donor arm. The programme has no lever aimed at it.
bag-diversedeserves the follow-up A4 asked for, now that it works. D 3.22-3.87 in three of four categories with no donor field, no encoder pass and no single slide, at SSIM 0.61-0.67. A combination — patch-bag tokens for appearance, assignment for layout — is one manifest away and was not tried here.
13. Caveats
- n = 10 per (category, arm) cell (5 seeds x 2 cuts), 40 cartoons, four categories, two cuts;
bag-diverseis n = 5 per category. Every median is a median of 10 or 5. The paired deltas are medians of 40 and are the trustworthy statistic. - Layout is measured only as lumen IoU against the cartoon's own label map on
ring_topology's analysis raster. It is coarse: a rearranged-but-plausible gland field scores 0 exactly as a wash does. The epithelium IoU is saturated (0.29-0.34 for everything from the cartoon down) and carries no signal here. - The F2 adapter's 0.26-0.56 is a different mask (output ring mask vs cartoon lumen mask) and a different cartoon set (a1, not a1v2); it scored the two-pass recipe at 0.10-0.13 where this instrument scores it at 0.045. Only the ratios inside each table are comparable.
- The paired control is not byte-identical at pass 1. A4 could reproduce a1's pass-1 output bit for bit because it reran the same job on the same slot. A5's control is sweep-v2's a1v2 arm, rendered on slots b/c; slot a's own render of the same cartoon has a different sha256 (PNG output is not bit-reproducible even for identical pixels, SWEEP_RESULT.md section 4), and the a5 pass-1 arm uses assignment tokens rather than a single donor, so both legs of the pass-2 comparison differ. The cartoons' geometry is the same (VM-vs-Mac lumen mask IoU median 1.000, and the assignment is 99.6% identical under either render).
- The assignment mosaic is a hard-edged paste, not the Hann-blended MultiDiffusion. Its seams
cost it some
ring_topologystructure, which is whymatch-si3can exceed it in IM and mixed; the ceiling it establishes is therefore approximate and slightly pessimistic. - The seam statistic is a column-gradient ratio, not a perceptual judgement. It catches a hard intensity step at a window boundary; it would not catch a smooth stain or scale discontinuity spread over a hundred columns.
- The inflamed arm was selected on the wrong instrument (section 8). Its outputs measure what they measure but they do not test "more infiltrate in, more infiltrate out". The density-instrument check is n = 60 windows, one category, per-window rather than per-field.
- The envelope, ring, Laplacian and copy screens are the C1/C2 instruments unchanged.
- Real donor licence (PixCell/UNI) is parked per PLAN.md. A5 makes the question sharper rather than softer: every output is conditioned on ~11 real fields rather than one.
- No pathologist has seen any of this. Yash has not reviewed a sheet. Nothing here is a clinical claim, and nothing here is "solved".
- Slots
b,canddwere not touched. Slotais TERMINATED, confirmed byfleet.sh statusafterdown. Powered 146.3 min this session (USD 5.17).