feat(LinuxTentacleE2E): Phase 12.L.E.19 — last-upgrade.json schema v2 round-trip pin (upgrade-flow capstone)
Summary
Final upgrade-flow capstone. After this lands, BOTH the structured event log (events.jsonl, J.L.E.16/17/18) AND the operator-visible status payload (last-upgrade.json) are pinned at:
- Wire format strict — every field present, valid JSON
-
Production parser binding —
[JsonPropertyName]attributes correctly wire the snake-camel field names to the C# record
Coverage gap before this test
Existing E2E tests assert .Status, .TargetVersion, .InstallMethod, .Detail. They do not assert:
| Field | Risk if unpinned |
|---|---|
SchemaVersion |
Server uses this to negotiate parse paths across v1/v2/v3 schema evolution; mismatch breaks compatibility branch silently |
StartedAt / UpdatedAt
|
DateTimeOffset binding from ISO 8601 Z; null = operator UI shows "completed at never" |
ScriptPid |
Operator-visible diagnostic for journalctl correlation; null = no audit trail |
ExitCode |
Schema v2 added field; null on success is intentional contract — pin so a future polish doesn't make it always-required |
Assertions
SchemaVersion == 2Status == "SUCCESS"TargetVersion == "2.0.0-test"InstallMethod == "tarball"-
Detailnon-empty -
StartedAt+UpdatedAtnon-null DateTimeOffsets, within the test's wall-clock window -
UpdatedAt >= StartedAt(chronology contract) -
ScriptPidnon-null and positive -
ExitCodeis null (success-path contract — failure paths set it, success path omits; backward-compat invariant for older agents)
Catches different regression class from J.L.E.18
| Regression class | Caught by |
|---|---|
Schema-version drift between .sh and C# parser |
This PR |
| DateTimeOffset binding for StartedAt/UpdatedAt | This PR |
| int? binding for ScriptPid/ExitCode | This PR |
Wall-clock skew bugs in .sh's date -u
|
This PR |
[JsonPropertyName] for events fields |
J.L.E.18 |
| events.jsonl wire format strict | J.L.E.16 |
| events.jsonl Phase-A truncate | J.L.E.17 |
Fidelity tier
.sh emits real wire format, real production parser deserializes it. Full round-trip pin matching J.L.E.18's pattern.
Expected runtime: ~5s (E1.h + parse + 9 assertions).
What's next
After this lands, upgrade flow is fully boxed in. Pivoting to Section A install-tentacle.sh as the next phase — currently 0 Linux E2E coverage despite the user's original goal explicitly listing "install" as priority 1.
Test plan
-
Linux E2E workflow runs (manual workflow_dispatchafter merge) -
E1uStatusPayloadV2_AfterSuccess_AllSchemaV2FieldsBindCorrectlypasses within ~5s -
No regression on existing 19 Linux E2E tests