Files
felhom.eu/documentation/tests/wire-contract-gate-2026-08-08/BEFORE.md
T
admin 3bf62b95bb
gates / gates (push) Successful in 33s
fix(gate): the wire-contract search shelled out to grep and read its failure as a finding
CI convicted ALL 174 checked tags while the pre-push hook was green. Cause, read from the run log
rather than guessed at the second attempt: the search used `grep -rnE --include=…`, and the CI
runner's image carries python3 and git and deliberately little else — its grep does not support
`--include`, so stdout was empty and the gate read empty as "the tag is absent".

That is a gate silently treating a tool failure as a finding, which is worse than no gate, and it is
exactly the error-swallowing this repo forbids. A green from it would have been just as untrustworthy
as the red.

Fixed by removing the dependency, not by working around it: the search is now pure Python — one
token index per receiving repo, built in a single pass, no subprocess. Faster too (one walk instead
of ~350 greps), and unreadable-file / empty-repo cases now exit 2 INCONCLUSIVE rather than reporting
absence.

THE BEFORE CAPTURE WAS RE-VERIFIED, NOT RE-GENERATED — the stronger claim. All 40 fields recorded in
BEFORE.md were re-tested against the new implementation: agree=40, disagree=0, i.e. exactly the four
this session fixed are now present and the other 36 still absent. The number 40 stands under both
implementations; only the mechanism changed. The whole-token property survives by construction — a
token index treats `healed_at` and `privsep_healed_at` as distinct tokens.

This is the THIRD instrument defect this gate's own controls caught before it was trusted, after the
substring false negative and the dr_recipe over-opacity. The first two were caught by re-finding the
known instances; this one by the CI-versus-hook disagreement the workflow's alarm mail explicitly
says outranks whatever the push was for.
2026-08-08 09:10:59 +02:00

108 lines
6.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# The wire-contract gate, run against the tree at b7fb211 on 2026-08-08
Deliverable 1 of the G-1/R-260 session: the gate FAILING, before any field was fixed.
Captured verbatim. If this had been green, the gate would not work and that would be the finding.
```
wire-contract gate — 210 tag(s) checked across 3 declared wire(s); 51 skipped (generic / opaque / allowlisted)
agent -> hub (POST /host-report)
the RECEIVER (hub) contains no occurrence of:
applied_at (emitted at pbs_dr.applied_at)
checked_at (emitted at guest_net.checked_at)
cpu_temp_c (emitted at host.cpu_temp_c)
damped (emitted at guest_net.guests.damped)
dhclient_alive (emitted at guest_net.guests.dhclient_alive)
disk_bytes (emitted at guests.spec.disk_bytes)
guest_net (emitted at guest_net)
has_route (emitted at guest_net.guests.has_route)
heal_succeeded (emitted at guest_net.guests.heal_succeeded)
healed_at (emitted at oob.healed_at)
healed_recently (emitted at mgmt_plane.healed_recently)
heals_last_hour (emitted at guest_net.guests.heals_last_hour)
last_handshake_age_s (emitted at wireguard.last_handshake_age_s)
last_heal_at (emitted at guest_net.guests.last_heal_at)
loadavg (emitted at host.loadavg)
memory_bytes (emitted at guests.spec.memory_bytes)
memory_total_bytes (emitted at host.memory_total_bytes)
memory_used_bytes (emitted at host.memory_used_bytes)
model_name (emitted at storage_targets.smart.model_name)
mount_inventory (emitted at restore_tests.mount_inventory)
mount_parity (emitted at restore_tests.mount_parity)
operator_key_configured (emitted at oob.operator_key_configured)
selfupdate_pending (emitted at selfupdate_pending)
selfupdate_pending_version (emitted at selfupdate_pending_version)
uptime_seconds (emitted at host.uptime_seconds)
wg_handshake_age_s (emitted at oob.wg_handshake_age_s)
controller -> hub (POST /report)
the RECEIVER (hub) contains no occurrence of:
config_hash (emitted at config_hash)
last_db_dump (emitted at backup.last_db_dump)
last_integrity_check (emitted at backup.last_integrity_check)
load_avg_1 (emitted at system.load_avg_1)
load_avg_15 (emitted at system.load_avg_15)
load_avg_5 (emitted at system.load_avg_5)
memory_total_mb (emitted at system.memory_total_mb)
memory_used_mb (emitted at system.memory_used_mb)
migrated_to (emitted at storage.migrated_to)
reporting_disabled (emitted at reporting_disabled)
stacks (emitted at stacks)
temperature_celsius (emitted at system.temperature_celsius)
uptime_seconds (emitted at system.uptime_seconds)
hub -> controller (report ACK, `escrow` object)
the RECEIVER (controller) contains no occurrence of:
escrow_stale (emitted at escrow_stale)
WIRE-CONTRACT GATE FAILED: 40 emitted field(s) cannot be received.
A tag whose literal string occurs nowhere in the receiving repo cannot be decoded
by any struct, named or anonymous — encoding/json discards it on arrival.
Fix: model the field on the receiving side and say what consults it — or, if it is
deliberately not consumed, add it to ALLOWLIST WITH A REASON. Never a quiet skip.
Blind spots (a green is not full coverage — see the module docstring):
generic tag names are not checked; reachability of a NAME is not use of a VALUE;
only the declared ROOTS are covered (hub desired-state and the agent local API
are NOT).
exit=1
```
## Self-test (a planted unreachable tag on a real root)
```
planted an unreachable tag on agent HostReport -> gate exit 1, planted tag convicted: True
unplanted tree -> gate exit 1
SELFTEST OK — the gate convicts a planted unreachable tag and the plant is the only difference.
exit=0
```
---
## ⚠ The search implementation changed AFTER this capture, and the capture was re-verified
The run above used `grep -rnE --include=…` to test whether a tag occurs in the receiving repo. That
**works on a workstation and returns nothing on the CI runner**, whose image carries python3 and git
and deliberately little else — its `grep` does not support `--include`. Empty stdout was then read as
"the tag is absent", so the gate convicted **all 174** checked tags and CI went red while the
pre-push hook was green (runs 260262).
That is the gate silently reading a tool failure as a finding, which is worse than no gate, and it is
the error-swallowing this repo's rules forbid. The search is now **pure Python**: one token index per
receiving repo, no subprocess, no external dependency.
**This capture was NOT re-generated — it is re-verified**, which is the stronger claim. Every one of
the 40 fields recorded above was re-tested against the new implementation:
```
fields recorded in BEFORE.md: 40
agree=40 disagree=0
```
— i.e. the new implementation finds exactly the four this session fixed (`operator_key_configured`,
`wg_handshake_age_s`, `healed_at`, `escrow_stale`) present, and the other 36 still absent. **The
number 40 stands under both implementations**; only the mechanism and its portability changed.
The whole-token property also survives by construction: a token index treats `healed_at` and
`privsep_healed_at` as distinct tokens, so the substring false negative that the control caught
cannot come back.