Rendered 2026-11-03 04:15 UTC

Incident report · canonical source docs/incidents/2026-07-2026-08-host-incidents.md

Host incidents, July–August 2026: two host-suspend windows and a boot-environment database fork

Report date 2026-09-08 · status: final · audit rows fl_downtime 984–991 · replaces the interim operational notice published 2026-09-03 (commit 8320951).

Revision 2026-09-10: commit identifiers updated after the pre-publication history filter; see docs/09-publication-attestation.md.

Summary

The record's correctness was never affected. Every published outcome is the category that on-time scoring produces — provably so for the cross-domain outcomes, whose date-based windows read a referee store verified unchanged, and reproduced for the single-domain outcomes that were scored twice; the referee data saw zero revisions across both windows. The record's completeness and timeliness were affected, and both have been repaired and disclosed: late classifications carry their true classification timestamps against their deadlines in the CSV, and the rows lost to the database fork have been restored under new identifiers with their provenance recorded.

Incident 1 — host suspend, 2026-07-23 → 2026-08-10 and 2026-08-14 → 2026-08-16. The workstation hosting the entire engine was suspended, in six offline segments over eighteen days and once more for two days. While suspended nothing ran: 81 cross-domain classification deadlines passed without the scoring run executing, seventeen daily referee fetches and two weekly aggregates were missed. On the first scheduled run after the host returned (2026-08-11 09:00) the unmodified job scored the backlog — 84 cross-domain and 8 single-domain classifications, 81 of them 1–13 days late — against a referee store that had been verified byte-identical to its pre-incident state. The operational-day clock paused for 18 days in total.

Incident 2 — boot-environment database fork, 2026-08-21 17:36 → 2026-08-30 19:22. An operating-system patch created a new boot environment; the next boot mounted it, and because the PostgreSQL data directory lived inside the boot-environment dataset, nine days of database writes landed in a forked copy that the reboot of 2026-08-30 silently reverted. The engine ran correctly the whole time against the database it could see. Lost from the live database: 109 published firings, 10 scored outcomes, 9 referee fetch runs, 470 candidate rows and the input-history rows of the window. The phantom state was preserved in three held copies, its rows were extracted forensically, and on 2026-09-06 the 109 firings and the 10 outcomes were restored in one governed transaction — new identifiers, original publication timestamps, provenance columns, canonical live outcomes untouched. The structural cause was eliminated on 2026-09-02. The clock did not pause: the engine operated throughout; its persistence layer failed.

Incident 1 — host suspend

Timeline by segment

One fl_downtime row per continuous offline period. Boundaries are last-observed / first-observed cron-log evidence at 15-minute tick granularity; the final end of the July window is the host boot record. Inside the wake windows the engine ran normally (firings were captured; the 2026-07-27 09:00 scoring run executed), so a single spanning row would have contradicted the record.

row offline from → to (CEST) duration ended by clock pause-days
984 07-23 09:00 → 07-26 15:45 3 d 6 h 45 m wake 07-26 15:45 → 07-27 12:30 3
985 07-27 12:30 → 08-03 00:30 6 d 12 h wake 08-03 00:30 → 03:15 6
986 08-03 03:15 → 08-03 15:30 12 h 15 m wake 08-03 15:30 → 17:30 0 — below the 24-hour threshold (FR-016), logged per FR-021
987 08-03 17:30 → 08-04 20:15 1 d 2 h 45 m a single cron tick at 08-04 20:15 1
988 08-04 20:15 → 08-07 20:00 2 d 23 h 45 m wake 08-07 20:00 → 20:15 3
989 08-07 20:15 → 08-10 16:49 2 d 20 h 33 m host boot 08-10 16:49; cron resumed 17:00 3
990 08-14 06:00 → 08-16 13:45 2 d 7 h 45 m resume 08-16 13:45 (zero cron-log lines on 08-15) 2

The July window was offline for about 17 of its 18.3 days. Segment 985 spanned 2026-07-29, the first classification deadline under Amendment 5 (firings 5966 and 5967).

Cause

The host — a personally-operated laptop that carried capture, scoring, rendering, alerting and backups — was suspended. The engine itself, its configuration and its data were unchanged.

Impact

Recovery and the late scoring

Same-day recovery on 2026-08-10, in order: the 17:30 scanner breach-alerted the overdue queue; a manual OFR fetch at 19:43 (run 110) brought the store current — 6,731 rows, 11 new, 6,720 verified, 0 revisions; batch scoring was deliberately left to the unmodified scheduled run of 2026-08-11 09:00, because the store was fresh, the selector picks up its backlog on its own, and the scoring windows are date-based, so late execution changes neither row selection nor window contents.

That run produced 92 classifications, all successful: 84 cross-domain (81 breached — 6 by one day, 8 by two, 8 by three, 31 by six, 7 by seven, 5 by eight, 6 by nine, 8 by ten, 2 by thirteen — plus 3 due that day), every one STRESS_NONE; and 8 single-domain (5 NORMALIZED, 3 SUSTAINED_REGIME). Six single-domain classifications ran on schedule inside the window's awake moments: three at the 09:00 run of 2026-07-23, immediately before the first suspend (firings 5348, 5350, 5351), and three at the 09:00 run of 2026-07-27 inside the wake window (5371, 5616, 5631). Each late row's classification timestamp stands against its deadline in the public CSV.

Why the late cross-domain outcomes are identical to on-time outcomes

The cross-domain scorer reads the OFR Financial Stress Index for a window fixed by dates (firing date to deadline). Late execution reads the same dates. Identity with on-time scoring therefore reduces to one question: did the referee values change between the day they would have been read and the day they were read?

The 8 single-domain outcomes use closed price windows that are equally date-fixed, but one evidence field — the cumulative-return percentile, ranked against a trailing three-year history that advances with the run date — is run-date-dependent. The same effect appears in Incident 2, where firing 6943 was scored twice ten days apart: the percentile and volatility ratio differed, the category did not. No on-time reference scoring exists for the July single-domain rows, so their identity is stated as a reproduction of category, not of every evidence value.

Operational clock

Under FR-016, engine-offline periods of 24 hours or more pause the operational-day clock (UTC-date clipping). Segments 984, 985, 987, 988, 989 and 990 pause; segment 986 (12 h 15 m) does not. The July window contributed 16 pause-days (calendar day 97 → operational day 81 as of 2026-08-10), the August window 2 (operational day 105 → 103 as of 2026-09-03). As of this report: calendar day 126, operational day 108, 18 pause-days in total.

Incident 2 — boot-environment database fork

Mechanics

On 2026-08-21 an operating-system patch session (pkg upgrades including the PostgreSQL server 17.10 → 17.11, plus freebsd-update) created boot environment 15.0-RELEASE-p12_2026-08-21_150946. The 17:34 boot mounted that boot environment instead of default. The PostgreSQL data directory lived at /var/db/postgres, inside the boot-environment dataset, so from the moment the database server started (~17:36) every write landed in a copy invisible to default. The host was rebooted on 2026-08-30 at 19:24 into default, which silently reverted all persistence of the window to its 2026-08-21 state. Everything else — cron, logs on /home, backups on /home — was shared between the two boot environments and continued uninterrupted.

Discovery

2026-08-31, morning: the public CSV stood at 1,372 rows where the last render before the reboot (2026-08-30 19:15) had published 1,471. The reversion was traced the same morning to the boot records — 17:34 on 2026-08-21 into the patch-created boot environment, 19:24 on 2026-08-30 back into default — and to the data directory's location inside the boot-environment dataset. The nightly dumps of 2026-08-21 → 2026-08-30, taken on /home and therefore outside the fork, were secured first; the phantom boot environment was snapshotted, held and streamed to the evidence vault that evening.

Impact

Lost from the live database at the reboot (counts from the forensic extraction of the phantom's final state, 2026-08-30 19:22):

what rows note
published firings 109 (phantom ids 7039–7147, contiguous) 41 cross-domain informational, 68 pending of which 66 WATCH; all carried public publication timestamps
scored outcomes 10 phantom ids 4295–4304, scored 08-22 → 08-30
referee fetch runs 9 (runs 119–127, 08-22 → 08-30) the same six new referee dates were re-observed live on 2026-08-31 with zero revisions
candidate-metric rows 470 audition inputs, never published
input-history rows 5,253 geopolitics, 966 energy, 177 financial metric rows; 895 fusion scores; 43 fusion alerts the detectors' own baseline history for the window

Public surface: the published CSV regressed from 1,471 to 1,363 rows at the 20:15 render of 2026-08-30 and stayed there until the 09:15 render of 2026-08-31 (about 13 hours). The primary-key sequences reverted with the data, so the live engine reissued identifiers from 7039 onwards to later, different events: of the 109 phantom numbers, 51 (7039–7075 and 7134–7147) are now live rows of other events captured 2026-08-30 → 2026-09-04, and 58 (7076–7133) were issued without ever becoming rows and are unused gaps below the sequence.

Engine conduct

The engine ran correctly for the whole window against the database it could see: detection, capture, scoring, rendering, alerting and backups all executed on schedule (cron log unbroken; first phantom tick 17:45:02 on 08-21, last 19:15:13 on 08-30). Ten firings whose deadlines fell inside the window were scored in the phantom and, after the reversion, scored again by the live engine on 2026-08-31: seven cross-domain and two geopolitics outcomes reproduced byte-identical evidence and category; firing 6943 (financial, NORMALIZED) reproduced its category with a recomputed cumulative-return percentile (20.17 → 16.27) and realised-volatility ratio (0.4966 → 0.5597), the run-date effect described above. The live scorings of 2026-08-31 are the canonical outcomes.

Evidence chain

Three held copies of the phantom's final state, all hashed:

  1. the phantom boot environment itself, snapshot zroot/ROOT/15.0-RELEASE-p12_2026-08-21_150946@incident-2026-08-21-fork-evidence, made undeletable with zfs hold incident-evidence;
  2. a full zfs send stream of that snapshot, 43,481,077,736 bytes (40.5 GiB), sha256 311b7d6d0ceb7601663eb7b72e91ddc6e9235b8d9adfb33506887f5ae7eea945, with a dry-run receive verified;
  3. the USB backup replica taken on 2026-08-31 (backup_pool, snapshot @full-20260831_182631).

From the held snapshot a forensic dump of the phantom database at 2026-08-30 19:22 was extracted on 2026-09-05 on a writable clone with a temporary, socket-only database server (the snapshot itself was never mounted read-write): systemate_phantom_2026-08-30_1922.dump, sha256 7b4fea11dcca09cadd6f720ea2d0c91a8203565fd36ed9f4de2bb8370a14155e. The ten nightly dumps of 2026-08-21 → 2026-08-30 are held alongside it, all in a sha256 manifest.

Remediation

On 2026-09-02 the PostgreSQL data directory was moved to its own dataset, zroot/data/postgres, outside zroot/ROOT, in a paused window with the copy verified file-by-file (1,753 files sha256-identical, control data identical, row counts identical before and after). Boot-environment switches can no longer fork the database: whichever boot environment is active, the same dataset is mounted over the data directory. The fork class is eliminated.

Operational clock

This was not engine downtime. The engine operated continuously and correctly; its persistence layer failed. FR-016 pauses the clock for engine-offline time, and there was none, so row 991 carries pauses_operational_clock = FALSE and zero pause-days (a spanning pause would have removed nine). The precedent is row 674, a subsystem-degraded event logged the same way. Row 991 anchors the restore as its FR-022 downtime window.

The governed restore (2026-09-06)

Design rulings

Execution

One transaction, executed on 2026-09-06 at 12:18 in a paused publication window after a dry run against the live database at 12:17 whose every assertion passed and which left no trace (Appendix D). The transaction locks the three tables, computes the identifier block after the lock (so a concurrent capture either precedes it and is inside the maximum, or waits), inserts the 109 firings and the 10 non-current outcomes with their 10 correction links, verifies its own result — row counts, a column-by-column round trip against the phantom rows, md5 digests of every pre-existing row, the ticker partition, the aggregate row set — and only then syncs the sequences and commits. An insurance dump was taken immediately before. The publication of the day comprised three write events in total: the migration, the restore transaction, and the sequence sync inside it.

Result on the public page: firings 1,433 → 1,542; informational +41, pending +68; classified, voided, the cross-domain scored count and both aggregate blocks unchanged; 109 rows marked RESTORED.

109 versus 108

Row 991 records firings_lost = 108. It was written on 2026-09-03 from the last nightly dump of the phantom (2026-08-30 02:10), with the row's own text declaring the figure a dump-counted floor pending the forensic extraction of the final 17 hours. The extraction found 109 — three more firings captured on 2026-08-30 between 02:10 and 18:15. The row is append-only and keeps its floor; this report and the mapping carry the count.

For holders of CSVs downloaded between 2026-08-21 and 2026-08-30

Identifiers 7039–7147 in a CSV from that period refer to the phantom rows. In the current export, 51 of those numbers (7039–7075 and 7134–7147) belong to different, later events captured 2026-08-30 → 2026-09-04, and 58 (7076–7133) do not appear at all; none of them is a restored row. The restored rows carry identifiers 7168–7276. The restored rows are the ones with restored_from_phantom_firing_id set; the phantom-to-current mapping is published alongside the current export as phantom-id-map.csv (sha256 c87b2f8beef4264179a309e6f3fadc73cb76ed00a8ac5fd9a5fbd86564522a2b), listing for each restored firing its phantom identifier, current identifier, domain, level, status, original publication timestamp, deadline and the phantom's alert and score identifiers, and for each restored outcome its phantom, restored and canonical identifiers. Always key on restored_from_phantom_firing_id; never on the old numbers.

Lessons

Both incidents were events of the host substrate — a lid and a patch — on a personally-operated laptop; neither was an event of the engine or of the methodology, and neither changed a scoring rule, a threshold or an outcome. The fork class is eliminated structurally: the database no longer lives where a boot-environment switch can reach it. The suspend class is a hosting question, under active review; no decision is claimed here.

Appendix A — downtime rows 984–991

row window (CEST) kind pauses clock pause-days notes
984 2026-07-23 09:00 → 07-26 15:45 host suspend, segment 1/6 yes 3 incident map and aggregate consequences recorded in this row
985 2026-07-27 12:30 → 08-03 00:30 host suspend, segment 2/6 yes 6 longest segment; spans the first Amendment 5 deadline (07-29)
986 2026-08-03 03:15 → 08-03 15:30 host suspend, segment 3/6 flag set; below threshold 0 12 h 15 m; logged per FR-021
987 2026-08-03 17:30 → 08-04 20:15 host suspend, segment 4/6 yes 1 ended by a single cron tick
988 2026-08-04 20:15 → 08-07 20:00 host suspend, segment 5/6 yes 3
989 2026-08-07 20:15 → 08-10 16:49 host suspend, segment 6/6 yes 3 ended by host boot; recovery sequence recorded in this row
990 2026-08-14 06:00 → 08-16 13:45 host suspend yes 2 zero deadlines breached
991 2026-08-21 17:36 → 08-30 19:22 boot-environment fork no 0 firings_lost = 108 (declared floor; forensic count 109); FR-022 anchor of the restore

Appendix B — commits

commit date change
5bd4cac 2026-07-19 banner retirement and never-blended STRESS surfaces (third deliberate pin flip) — the render state in force through both incidents
8320951 2026-09-03 interim disclosure of the incidents and the amended audit-scope clause ("… or, when the host is offline at a deadline, by the first scheduled scoring run after it returns; host downtime is recorded and disclosed") — fourth deliberate pin flip
082364f 2026-09-06 migration 007 (restore provenance columns, pairing check, uniqueness, guard extension) and the RESTORED marker
0d98ec4 2026-09-08 the interim notice replaced by the permanent incident-history pointer; this report and the mapping published — fifth deliberate pin flip
this commit 2026-09-10 commit identifiers in this appendix and the header updated to their public values after the pre-publication history filter; publication attestation added (see docs/09-publication-attestation.md)

Identifiers shown here are the public values. The commits were made under the local identifiers recorded in the pre-publication map (docs/prepublication-hash-map.txt); content, timestamps and messages are unchanged.

Appendix C — evidence hashes

artefact sha256 / identity
held snapshot zroot/ROOT/15.0-RELEASE-p12_2026-08-21_150946@incident-2026-08-21-fork-evidence, zfs hold incident-evidence
zfs send stream, 43,481,077,736 bytes 311b7d6d0ceb7601663eb7b72e91ddc6e9235b8d9adfb33506887f5ae7eea945
USB replica backup_pool/ROOT/15.0-RELEASE-p12_2026-08-21_150946@full-20260831_182631
forensic dump systemate_phantom_2026-08-30_1922.dump 7b4fea11dcca09cadd6f720ea2d0c91a8203565fd36ed9f4de2bb8370a14155e
restore inputs phantom_fl_firings.copy / phantom_fl_outcomes.copy c90374977ca65223735544b16172fd5453f09274448fb3260076a9e4cc3618c7 / 5ee62d8aea979239b2252a1eb4fcabef989f6787beb2e3360aeee81e364d9834
insurance dump before the restore, 2026-09-06 12:18 0695ea9feb3c83fc44e9ea0e1a2bab20f67ddd93db06304edb2e7d04b6781cc7
phantom-id-map.csv (published) c87b2f8beef4264179a309e6f3fadc73cb76ed00a8ac5fd9a5fbd86564522a2b

Appendix D — assertions of the live dry run (2026-09-06 12:17)

Each assertion is executed inside the restore transaction and aborts it on failure; the dry run rolled back, the execute run of 12:18 committed with identical results.

assertion result
migration 007 present; anchor row 991 present with its window; no restored rows yet pass
forensic extracts loaded 1,471 firings, 154 outcomes
phantom-only firings = phantom rows with no live row of the same (id, firing timestamp) 109, ids 7039–7147; 0 without publication timestamp; 0 outside the row-991 window; 0 unexpected status; 0 voided; epoch present
inserted with contiguous new ids 109 → 7168–7276
round trip, every verbatim column, restored row vs phantom row 0 mismatches
score-id pointers into live fusion scores (with the NULL ruling) 0
twice-scored set 10 phantom outcomes 4295–4304, each paired to a live current re-score of the same category and classifier; 9 evidence-identical, 6943 recomputed; none attached to a phantom-only firing
table deltas firings +109, outcomes +10, corrections +10; epochs, downtime, fusion alerts 0
pre-existing rows (md5 digest of every row with id ≤ the pre-restore maximum) firings, outcomes, corrections unchanged
ticker partition informational +41, pending +68, classified 0, voided 0; total 1,542 = sum
aggregate row set (the one query both the A–E and STRESS blocks read) 0 rows changed; STRESS 113 → 113, A–E 61 → 61
restored outcomes current / firings with two current outcomes / corrections wired 0 / 0 / 10 of 10
schedulers on the restored rows 0 due in either selector; earliest deadline 2026-09-09; scanner stages 0 / 0 / 0
sequences after rollback 7167 / 4377 / 789, unchanged