# 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 260–262). 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.