feat(LinuxTentacleE2E): Phase 12.L.E.18 — events.jsonl production-parser symmetry pin
Summary
Closes the binding-layer gap between .sh wire format (pinned by J.L.E.16) and what the production parser actually returns to the server-side timeline UI. The two layers can drift independently:
| Layer | Pinned by |
|---|---|
| Wire format strict (every line is valid JSON, fields present, kinds in order) |
J.L.E.16 (E1uEventsContract) |
| Production parser binds wire format → C# record correctly | This PR |
What this catches that J.L.E.16 doesn't
The production parser uses [JsonPropertyName] attributes to map:
| Wire format | C# property |
|---|---|
"t" |
Timestamp (DateTimeOffset?) |
"phase" |
Phase |
"kind" |
Kind |
"msg" |
Message |
If those attributes are silently dropped (refactor accident, IDE auto-format reorders annotations off, "modernization" to a JsonNamingPolicy), JsonSerializer falls back to property-name matching → Timestamp/Message/Phase/Kind don't match wire t/msg/phase/kind → every event deserializes to all-defaults (null Timestamp, empty strings).
J.L.E.16's strict parser would NOT catch this — the wire format is still valid JSON; only the C# binding broke. Server UI timeline shows blank cards even though the wire data is fine. This is exactly the regression class this PR pins against.
Test mechanism
- Run E1.h-Linux end-to-end.
- Strict-parse events file via test-infra parser (J.L.E.16 path).
- ALSO production-parse via
UpgradeStatusPayload.TryParseEvents. - Assert symmetry: counts match (production parser didn't silently drop a line strict-parse accepted).
- Assert binding: every event has bound
Phase+Kind(non-empty), AND every event has a non-nullTimestamp(catches DateTimeOffset regression). - Cross-check terminal
"success"is the last event.
Fidelity tier
.sh emits real wire format, real production parser deserializes it. Full round-trip pin.
Expected runtime: ~5s (E1.h + ms for parse).
Test plan
-
Linux E2E workflow runs (manual workflow_dispatchafter merge) -
E1uEventsProductionParser_AfterSuccess_BindsWireFormatToCSharpRecordpasses within ~5s -
No regression on existing 18 Linux E2E tests