Decision 150 (operator 13:25): sessions correct stale facts in instruction files themselves,
naming each edit; never loosen a rule. Added to unprompted-work.md §5 (all copies) and
PROMPT-TEMPLATE.md §9. CLAUDE.md gates paragraph (R-891), architecture pointer, docs/website
rules, the doubled R-286 sentence in the workspace CLAUDE.md.
Decision 149: vaultwarden's step proven on bench 9401 and box 9202 (evidence here).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
sysfacts reads the agent's top-level guest_disk_trim stanza (schedule + per-guest
last attempt: vmid, last_attempt_at, ok, bytes_trimmed, mounts, duration_seconds,
last_ok_at, error) into a field-by-field mirror. The System page's new 'Last disk
trim' column shows the last successful trim and the GiB it freed; amber when the
newest attempt failed (error shown) or last_ok_at is older than 14 days (judged on
the success time, never the attempt time); '—' when the agent sends no stanza.
wire_contract_gate: SUBTREE_MIRRORS checks guest_disk_trim field by field BOTH
ways against sysfacts.DiskTrim; decoys (ok renamed, last_ok_at dropped) in
test_gate_decoys.py. Decision 139.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
iso_bootstrap_gate.py runs scripts/iso/test/bootstrap-modes.sh in felhom-iso-assistant:trixie
(staged copy, read-only mount, --network none), registered fast=False in repo_gates.py so the
pre-push hook and CI (both --fast) never run it (decision 147). No docker / no image / docker
error -> exit 2 NOT CHECKED. Every green run is followed by a built-in decoy: the harness must
FAIL a bootstrap whose pairing banner never paints (R-496 shape), or the gate convicts the
instrument as blind. Docker-free decoys in test_iso_bootstrap_gate.py (fake docker on a
one-directory PATH), run from test_gate_decoys.py (COVERS).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
controller/offbox-rename kept its EXEMPT entry after R-425 gave it decoys, so
the debt list read longer than the debt. The gate now prints a STALE EXEMPTION
line for any exempt gate that has a decoy (named, not convicted: the decoy lands
in a sibling repo, and failing this repo's push for it would couple the two).
A check in test_gate_decoys.py plants a stale entry and asserts it is named;
seen to fail with the notice removed.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
DONE/IDEA were searched over the whole state cell, so an open ROADMAP row whose
state named ANOTHER row's verdict (`READY — split out after R-86 CLOSED`)
escaped as shipped — R-87's shape from R-378, one file over. The gate now reads
register_table.leading_verdict(state); all 28 current rows classify unchanged.
Decoys: that shape (seen to PASS against the old gate), an id present in the
register only as prose (seen to PASS when `have` is loosened to any mention),
the existing suffix-id row; genuine: a row with a counterpart, and an `idea` row
asserted BY DESIGN — R-424's hole, accepted by the operator 2026-10-05.
EXEMPT drops `felhom.eu/one-register`.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Four cases in test_gate_decoys.py, planted inside the real DUE-CHECKS block:
an orphaned check whose id survives only in prose, the due-TODAY boundary with
due-tomorrow as the genuine article (today pinned via FELHOM_GATE_TODAY to a
date before every real check), and the block's marker named in prose (exit 2).
Each convicting decoy was seen to PASS under a mutation of the gate (row match
loosened to the bare id; `<=` to `<`; duplicate-marker refusal removed). The
gate itself is unchanged — no hole found. EXEMPT drops `felhom.eu/due-checks`.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
The R-185 check counted the bare word `felhom-backup-target-apply grant`, so the
dry-run echo stood in for a deleted real grant (2 resolutions, 2 "grants"); now
only path-qualified invocations at a command start count. The age check matched
a commented-out install; now comment lines are excluded. Decoys (plus a version
const in a new hub sub-package, and a comment naming the identifier that must
pass) live in test_gate_decoys.py; all three convicting decoys were seen to PASS
against the pre-fix gate. EXEMPT drops `felhom.eu/hostinstall`.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
DeleteHost already removed the host's wg_peers row, but only the 5-minute reconciler tick pushed the
list to the off-site endpoint. The host delete now triggers the push as the customer delete does
(R-600) and logs that it was requested.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
When the box reports the vouched agent version, its running binary's sha256 is compared with the
vouched AgentSHA256: same bytes -> "matches vouched", different -> amber DRIFT. An empty hash (older
agent) or another version is not comparable and shows nothing.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
PATH_RE gains .md. The false-positive walk across all four repos found one: an audit document cited
by app-catalog's REUSE.md, hidden by the evidence-copy exclusion — excluded trees are now walked for
.md documents only, so a .go evidence copy there still never satisfies a citation. The KNOWN HOLE
decoy now expects a conviction.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Once retrieval_promise_gate.py imports customer_copy_vocab and drops its STEMS literal, the drift
check reads "ok — single source" instead of failing on the missing literal. An import only in a
comment, or beside a stale literal, is still drift. Pinned by scripts/test_hub_copy_drift.py.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
The escrow/retained and escrow-ACK wires are now compared path-by-path against the receiver's named
mirror type, so the measured mutation (agent renames superseded_at, the old name still a local-API map
key) convicts. Decoy pair in test_gate_decoys.py, seen failing with the mirror removed.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
After a guest rebuild the hub (set-only claim, by design) said "Claimed" while the box showed its
first-run setup page. The latest report's `claimed` field is now shown; hub-claimed + box-not-claimed
renders an amber warning. No claim state is changed.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Opens the four sealed box-secret columns back to plaintext (all or nothing; keeps api_key_hash;
idempotent; counts only in the log) and exits before any start-up sealing, so hub 0.137.0 can run on
the database again.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
A sibling subdomain can no longer toss a session cookie the hub reads first. Operators are logged out
once; plain-HTTP browser login no longer holds a session; Basic auth for scripts is unchanged.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
hosts.api_key, customer_configs.api_key / retrieval_password and host_pbs_secrets.value now hold the
R-821/R-133 seal (enc:v1:, OFFSITE_SECRET_KEY). The two API keys get an api_key_hash lookup twin
(SHA-256, backfilled keyless in migrate()), so box authentication never needs the sealing key; a row
with no hash is matched on its plaintext only while it is plaintext. SealLegacyBoxSecrets seals legacy
rows at start-up (idempotent, non-fatal). A sealed value that does not open sets SecretsUnreadable:
serve/compare paths answer 500, saves refuse the record, the PBS token is not burned.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
The check itself shipped with R-297 (golden_local_matches_manifest: sha256 or controller version
against the hub manifest; mismatch re-fetches, a named --golden is refused). This adds the
disclosure line the row also asked for and tests that pin the check's place before the adopt.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS