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

73 lines
3.6 KiB
Markdown

# 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.