Files
felhom.eu/documentation/audits/R411-R414-2026-09-01/00-part2.1-determination.md
T
admin 22e1c95e6a
gates / gates (push) Failing after 17s
the golden gate reads a fact not a name (R-410); R-133's collision resolved (R-406, R-416)
R-410. golden_currency_gate.py matched EVIDENCE_RE against os.listdir and read nothing
inside, so `mkdir documentation/tests/golden-9.9.9-2026-01-01` turned it green with no bake
behind it - noticed while the 0.230.0 bake was running, when the evidence directory existed
before the bake finished. It now reads the GOLDEN_SHA256= line out of that directory's bake
log: a directory name is a label, that line is a fact only a completed publish produces.
Still offline, still --fast, one file read. Directories that look right and hold nothing are
printed by name rather than silently ignored, so a half-finished bake is visible.

test_golden_currency_gate.py ships the red-proof with a POSITIVE CONTROL, without which
"it fails on an empty directory" would be satisfied by a gate that fails on everything:
  CASE 1 empty directory -> rejected and named
  CASE 2 log with no GOLDEN_SHA256 -> rejected
  CASE 3 real bake log -> counted, and its sha read      <- the control
  CASE 4 the tree is left byte-identical
Red-proofed: reverting the gate to name-matching fails cases 1, 2 and 3.

R-406. Citations MEASURED before choosing, which is what the row asked for: hub-uniqueness
had 3 references (all inside one audit doc), plaintext-break-glass had 5 (CONTEXT.md,
break-glass.md, hub/CHANGELOG.md, the capability map, a spike). The FEWER-cited one moved -
hub uniqueness is now R-415 - and all three citations were rewritten to "R-415 (was R-133)"
rather than silently swapped.

THIS IS THE OPPOSITE OF THE TASK'S LITERAL INSTRUCTION, which said renumber the second row on
the stated ground that "the older number has the longer reference trail". Measured, that
ground points the other way. The principle was followed and the letter was not, and the row
says so rather than leaving an unexplained diff.

R-416 filed: the within-register duplicate rule was deliberately NOT added in the same commit
that removed its only subject - a guard whose red-proof can only be a planted fixture is not
this project's standard. Now that the register is clean it can ship with the next real
duplicate as its first subject.
2026-09-01 10:19:55 +02:00

3.6 KiB

Part 2.1 — was the scratch resolver missed, or excluded on purpose?

ANSWER: neither label fits exactly. It was consciously OUT OF SCOPE for R-356, and it was never ruled out on the state-only grounds. The rule in §6.3 already covers it. → BRANCH (a).

The evidence, in the order it settles the question

1. R-356 itself says the scratch resolver was left alone — and says why

From R-356's own commit (08eb1a6, 2026-08-22), in its test file, twice:

"the prepared scratch still resolves to the registered storage path (offboxRestoreScratchDir step 2), so the srcs the fixture laid down are still where the code looks for them; only the DESTINATION moves, which is precisely what this change is about."

"The scratch was laid out against the drive namespace, which is where offboxRestoreScratchDir still resolves (the drive stays a registered storage path). Only the DESTINATION moves."

R-356 was about where restored data LANDS. It changed PlaceOffsiteRestore and ReconstituteFromOffsite. It did not consider the scratch resolver a subject — and it assumed, in every fixture, that step 2 succeeds because a registered storage path exists.

demo-felhom is the case R-356 never had: zero registered storage paths.

2. The one comment about a systemDataPath fallback was about a DIFFERENT function, and R-356 OVERRULED it

R-356's diff deleted this line from PlaceOffsiteRestore:

"NOT AppNamespaceRoot — its systemDataPath fallback would merge userdata onto the SSD system namespace."

That concern was about placing the customer's bulk userdata on the SSD, and R-356 decided against it deliberately. It was never a statement about a scratch.

3. The scratch resolver's own comment forbids a different path

"NEVER cfg.Paths.DataDir (the rootfs — the F-A1 filler)."

cfg.Paths.DataDir is the controller's data dir on the rootfs. cfg.Paths.SystemDataPath (/mnt/sys_drive) is a different filesystem. The documented exclusion is the rootfs, not the system data path.

4. And the decisive one: a driveless app's unit ALREADY lives there, permanently, by design

07-backup-architecture.md §7, [FACT]:

"GetAppDrivePath returns the app's HDD_PATH if it has one and systemDataPath otherwise — what appbackup/paths.go:26-27 calls the SSD-only system-data fallback. Nothing deletes a unit after it is copied onward … so for every app without a data drive, mp1 holds the kept copy indefinitely."

and:

"the same-device placement is intended, not a defect."

So a unit-only scratch on the system data path is at most a second copy of a unit that already sits there permanently and by design. Nothing anywhere states a restore scratch must not land there.

Therefore

Branch (a) — apply the rule that already exists, scoped by what is being restored, which is the distinction the register row did not carry and §2.2's state-only concern genuinely requires:

restore scratch fallback why
unit-only (the nightly proof, and the customer's default) may fall back to the system data path the unit already lives there permanently (§7); the scratch is bounded by the unit's own size
full (bulk userdata) keeps today's behaviour and refuses this is the case the deleted PlaceOffsiteRestore comment worried about, and the SSD is a state-only tier

One resolver answering two questions is the R-356 defect itself. Splitting it by restore scope is the same separation R-356 made between "installed?" and "where?".

§6.3's "one expression" sentence gains a fourth consumer and becomes true.