Merge docs/phase2-stop-condition: Phase 2 Task 0 STOP condition resolved (does not fire)
v2-ci / build (push) Successful in 4m54s
v2-ci / unit-tests (push) Failing after 9m57s

Claude-Session: https://claude.ai/code/session_01GASWkNEi68FSCtvr6rLoEW
This commit is contained in:
Joseph Doherty
2026-07-21 03:46:56 -04:00
@@ -56,6 +56,29 @@ auth already in place, no in-memory SQLite in tests, cp-triplet-only rig inspect
(**STOP condition:** a BLOB payload column cannot be registered — it must become base64 TEXT in
the new schema, and the recon must size the largest realistic payload against the 171 KB-ish
chunk guidance; alarm events are small JSON, so expect this to be fine, but verify).
> **PRE-ANSWERED 2026-07-21 (verified against `master` `d218282c`) — the STOP condition does NOT
> fire.** Re-verify cheaply during recon, but do not expect a surprise:
>
> - **Executed DDL:** `SqliteStoreAndForwardSink.cs:657-667`. It matches the class doc-comment at
> `:17-26` exactly — no drift between the documented and executed schema.
> - **No BLOB.** All 8 columns are TEXT/INTEGER; the payload column is **`PayloadJson TEXT NOT
> NULL`**. So no base64 conversion is needed and the chunk-size sizing is moot.
> - **Payload is small and bounded by shape.** `PayloadJson` is a serialized
> `AlarmHistorianEvent` — 10 scalar fields (`AlarmId`, `EquipmentPath`, `AlarmName`,
> `AlarmTypeName`, `Severity`, `EventKind`, `Message`, `User`, `Comment?`, `TimestampUtc`).
> No collections, no nesting. Realistic worst case is low single-digit KB (operator `Comment`
> is the only unbounded-ish field), far under the 171 KB chunk guidance.
> - **PK IS autoincrement** — `RowId INTEGER PRIMARY KEY AUTOINCREMENT`. This confirms the
> plan's own prediction: LocalDb cannot replicate an autoincrement key, so the migrator
> **must** mint deterministic `mig-{node}-{legacyId}` ids, and the new table needs a TEXT
> GUID PK as already specified in the Architecture note.
> - **One index to carry across:** `IX_Queue_Drain ON Queue (DeadLettered, RowId)` (`:667`) —
> the drain's covering index. The `alarm_sf_events` equivalent wants the same shape over
> (dead-lettered flag, insertion order) so the drain query stays index-covered.
> - **Legacy column list for the migrator's `pragma_table_info` intersection check:**
> `RowId, AlarmId, EnqueuedUtc, PayloadJson, AttemptCount, LastAttemptUtc, LastError,
> DeadLettered`.
2. The public seam: the interface the drain worker and producers use (e.g. `IAlarmHistorianSink` /
enqueue+dequeue+markDelivered+deadLetter methods), so the rewire can keep it byte-compatible.
3. Semantics to preserve: `Capacity` (1,000,000) enforcement, `MaxAttempts` (10), dead-letter