Skip to content

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:

  1. Wire format strict — every field present, valid JSON
  2. 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 == 2
  • Status == "SUCCESS"
  • TargetVersion == "2.0.0-test"
  • InstallMethod == "tarball"
  • Detail non-empty
  • StartedAt + UpdatedAt non-null DateTimeOffsets, within the test's wall-clock window
  • UpdatedAt >= StartedAt (chronology contract)
  • ScriptPid non-null and positive
  • ExitCode is 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

🟢 High (Rule 12.4): real prod .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_dispatch after merge)
  • E1uStatusPayloadV2_AfterSuccess_AllSchemaV2FieldsBindCorrectly passes within ~5s
  • No regression on existing 19 Linux E2E tests

🤖 Generated with Claude Code

Merge request reports

Loading