7.2 KiB
Web history deletion HTTP 500 corrective evidence
Date: 2026-09-18
Incident and root cause
The disposable Program rollout session
se_6bb66f59-a6fb-4759-a75d-c1a79ea107e8 was confirmed read-only as the
completed execution of Séance jetable de bascule in the already deleted
Program BASCULE PROGRAM EXECUTION V1 — JETABLE — 2026-09-18. It contained one
occurrence and one performed set.
On an SQLite backup copy, the deployed history detail endpoint returned HTTP
500 before the deletion mutation was reached. SQLite reported primary code 1,
extended code 1, and no such column: se.max_weight_kg while preparing the
history-entry query. The field belongs to max_results, not session_entries.
The same inspection also found no current causal revision for this imported
session and showed that completed Program execution provenance needed an
explicit terminal lifecycle state before its session could be removed.
Corrective contract
The history detail query reads max_results. Mobile history import seeds a
missing causal live revision with the canonical deterministic snapshot
revision. Desktop schema v28 permits a Program execution to move from
completed to terminal deleted in the same transaction as the revision-
guarded session deletion and tombstone publication. An old completed execution
fact is dominated only by a matching durable deleted causal state. Browser
responses distinguish stale, absent, invalid and internal failures, while
bounded server diagnostics include the SQLite codes and failing stage without
exposing SQL or paths.
Automated evidence
- GCC Meson: 91/91 tests passed.
- Clang built the complete target set; the five affected native tests passed.
- ASan/UBSan: the five affected native tests passed.
- Web TypeScript, 99 Vitest tests, production build and two real-Firefox Sessions scenarios passed.
- Android debug assembly and unit tests passed without Android source or schema changes.
- JSON/import validators, changed-C formatting and
git diff --checkpassed.
The rollout-specific backup hashes, deployed commit, causal operation, generation/ACK identifiers and restart/non-resurrection results are appended after the controlled private rollout. No public tag or release is created.
During the first post-delete hardware exchange, the desktop correctly rejected
Android's still-live snapshot with causal protection refuses legacy/live replay. This exposed a missing full-envelope dominance rule: Android publishes
its half before consuming the desktop tombstone in the same conversation. The
production full-generation consumer now filters only live facts already
dominated by causal state applied from that complete envelope; standalone
snapshot imports retain their strict rejection. A regression exercises this
ordering and proves the tombstone and absent session remain unchanged.
The recovery envelope also republishes both consumed and rejected correlated
ACKs. This lets Android close an interrupted generation with the real durable
desktop rejection evidence and reclaim normal bounded capacity; no generation,
ACK, tombstone or archive is manually purged or synthesized.
Controlled private rollout
The rollout source was advanced through bounded corrective commits to
a8c50a9512d8754477a2491b73c0f9e0f92263bf. The coordinated desktop archive is
trainlog-0.1.2-a8c50a9-desktop-v28.tar.gz, SHA-256
e9e08b317fb97807987b8e027a072cc8235bd9ee6dff7d393df4acb8f04c8a46.
Android code and schema were unchanged; the installed private application
remained com.labfytools.trainlog, versionCode 17, versionName 0.1.2, schema
25.
Before deployment, the real desktop v27 database was backed up as
trainlog-desktop-v27.db, SHA-256
ffa4cd5c5fc832b4054ee6543ff89b74be7148b19ffb9af718aa0dfe787ab20a.
The real Android product backup was
trainlog-android-v25.tlbackup.zip, SHA-256
8d198dc5fb6ca5c410029342f0e3074d288dac844ce686e3a70782bdad9e8bce.
A populated copy migration changed only PRAGMA user_version from 27 to 28:
all 47 business-table counts and canonical typed hashes were identical,
integrity_check was ok, and foreign_key_check was empty. The production
migration reached the same integrity state.
The real deletion used the Web HTTP command with the session's current causal
revision. It removed only
se_6bb66f59-a6fb-4759-a75d-c1a79ea107e8, created operation
del_af8a82de-2825-45af-9f62-ec94cc61a556, and retained exactly one deleted
causal state. Its Program execution provenance moved from completed to
terminal deleted while preserving the original observation timestamp. The
session detail then returned the bounded absent response and reload did not
restore it.
The first hardware exchange exposed two real recovery defects before a final
PASS. Generation gen_42f8d681-373e-4f07-b08d-792a1e8e516d was rejected
because the live Android snapshot reached the importer after its tombstone;
the subsequent generation gen_c9d1fda8-8375-4d89-8c29-a5ae120c6c7d proved
that the equipment-association companion required the same dominance rule.
Later retries reported peer_capacity_exhausted: a product backup showed four
older immutable Android generations still waiting for ACK. Their retained
manifests were processed by the production consumer and received genuine
correlated stale-lineage rejection ACKs. The recovery envelope then accepted
that exact terminal evidence. No ACK, tombstone, generation, archive or ledger
row was fabricated, deleted or edited manually.
The final post-correction exchange completed as run
sy_b343619f-a1d2-4bea-a0af-c57aa5792aca: Android generation
gen_b2290cad-0554-46f9-bc30-0fbd0c8190e8 was consumed with
sqlite-commit-full, desktop generation
gen_21d7efa0-bd97-4843-a0d6-111e94d669b4 was acknowledged, no capability was
missing, and sessions_reconciled was zero. The second exchange completed as
run sy_58b38f82-a70c-48c3-834c-81436c687c8c, with generations
gen_65e4833c-5383-4374-b37f-d8add8a106e8 and
gen_76e15f8a-1258-49f1-ac0e-b04fe9e6a8a5, again with no missing capability
and zero reconciled sessions.
After both stores and the Web component were reopened, the Android product
backup trainlog-android-postrestart-20260919.tlbackup.zip had SHA-256
7ae8f88c60bd44355c19c89e7996d8c71ec178c75aabff0979065a5a70936ed5,
schema 25, integrity_check=ok, an empty foreign-key check, no disposable
session, and exactly one matching causal state and operation. Desktop remained
schema 28 with the same integrity results and terminal provenance. Canonical
hashes for every other session, occurrence, performed set, continuous result
and body observation were identical to both pre-rollout backups.
Finally, the retained pre-deletion generation
gen_965fdf80-4412-41fb-9a46-28744a3e115b was replayed through the production
consumer. It returned its byte-identical stored ACK; the before/after tuple was
unchanged (target session=0, target operation=1, deleted provenance=1,
all sessions=9). Real Firefox against the deployed C server passed at
1440x1000 and exactly 390x844 with no horizontal overflow. Evidence screenshots
are retained privately under
~/.local/state/trainlog/history-delete-http500-fix-v1-web-evidence/; their
SHA-256 values are respectively
9c9fd42aa58b0818488ef9f8b3412abfb56e96ea3f9d30c8da2fcfe3da8c10f1 and
70838305fe6d17485f3d5d80cf60e2639baf4ed87d148f4b55e4b6e8c25fdbca.