Skip to content

feat(v1.99 PR-20): update logging observability (G3-U15/U16/U17)#477

Merged
itcmsgr merged 2 commits intomainfrom
feat/v1.99-pr20-update-logging
Apr 19, 2026
Merged

feat(v1.99 PR-20): update logging observability (G3-U15/U16/U17)#477
itcmsgr merged 2 commits intomainfrom
feat/v1.99-pr20-update-logging

Conversation

@itcmsgr
Copy link
Copy Markdown
Owner

@itcmsgr itcmsgr commented Apr 19, 2026

Summary

Pure observability layer on top of PR-19. No behaviour changes, no new exec.Run calls, no new whitelist entries, no new state transitions.

Pinned sentence:

PR-20 is observability only: it may refine logging detail, phase markers, and idempotency visibility, but may not introduce any new state-mutation, recovery, authority, or validation behavior.

Scope

G3-U15 — Runtime state preserved in evidence

  • from → to version line emitted after preflight/origin detection, BEFORE any mutation invocation
  • End-of-run trailer: single machine-parseable line with mode, from, to, phases_passed, phases_failed, duration_ms, exit, final_state

G3-U16 — Idempotency visibility

  • When current == target, log "already up-to-date — rebuild will no-op"
  • No control-flow change — apply still invokes rebuild. Rebuild remains the single authority for "is this a no-op" (v1.96 atomic switch produces identical kernel state for identical input).

G3-U17 — Log/evidence adequacy

  • beginPhase / endPhase wrappers track wall-clock per phase
  • Each PhaseEnd emits phase <Name> duration_ms=<N> result=pass|fail

Implementation

One runtime file touched: cmd/nftban-installer/update_apply.go

  • New runMeta struct with beginPhase / endPhase / emitTrailer
  • runUpdateApply now uses named return (rc int) + defer meta.emitTrailer(log, &rc, sf) so the trailer fires on EVERY exit branch including preflight-fail, rebuild-fail, validator-fail
  • Three new display helpers: displayVer, displayOrigin, passedStr (pure)

Tests

6 new in cmd/nftban-installer/update_apply_logging_test.go:

Test Assertion
T-L1 from→to line emitted with correct versions (source install)
T-L2 "already up-to-date" marker when current == target
T-L3 per-phase duration_ms= + result=pass at every PhaseEnd
T-L4 trailer contains all 8 documented key=value pairs
T-L5 trailer fires via defer on every exit branch (4-way table)
T-L6 AuditRecordedCommands + AuditWrittenFiles still pass — logging did not broaden call path

Non-goals honoured

  • ❌ No new exec.Run calls in runUpdateApply
  • ❌ No new ApplyWhitelist entries
  • ❌ No change to exit-code contract or state transitions
  • ❌ No idempotency short-circuit — rebuild still runs on current == target
  • ❌ No validation/recovery/authority change

Gate sequence after this merges

  • Parity checkpoint (G3-U-REBUILD-PARITY proof)
  • PR-21 only opens after parity is proven

Contract: internal/installer/update/logging_contract.md

🤖 Generated with Claude Code

itcmsgr and others added 2 commits April 19, 2026 20:42
Pinned sentence:

  "PR-20 is observability only: it may refine logging detail, phase
   markers, and idempotency visibility, but may not introduce any new
   state-mutation, recovery, authority, or validation behavior."

Scope (G3-U15/U16/U17):
  - G3-U15 from→to version line at apply start + end-of-run trailer
    (machine-parseable key=value: mode/from/to/phases_passed/
    phases_failed/duration_ms/final_state)
  - G3-U16 "already up-to-date" marker when current == target —
    observation only, NO idempotency short-circuit (rebuild remains
    single authority for no-op)
  - G3-U17 per-phase duration at PhaseEnd

Explicit non-goals:
  - No new exec.Run calls (any host-state log uses existing PR-18
    whitelisted probes)
  - No mutation/recovery/authority/validation behavior
  - No idempotency short-circuit
  - No change to exit-code contract, state-transition rules, or
    ApplyWhitelist

Post-merge gate reminder: after PR-20 merges, parity checkpoint
(G3-U-REBUILD-PARITY) must pass before PR-21 opens. PR-20 does not
ship that proof — it only makes the proof readable.

Contract seed lands BEFORE any code, same pattern as PR-18 / PR-19.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR-20 adds pure observability. No behaviour changes, no new exec.Run
calls, no new whitelist entries, no new state transitions, no new
recovery/authority paths.

runMeta observability struct in update_apply.go:
  - Captures run-wide: mode / from / to / start time / phases_passed /
    phases_failed
  - Captures per-phase: phase name + start time for duration
  - Emits trailer via deferred emitTrailer on every exit branch so a
    preflight-fail, rebuild-fail, validator-fail, or happy-path run all
    produce a machine-parseable summary line
  - Uses named return (rc int) so the trailer captures the actual
    returned exit code

G3-U15 runtime state preserved:
  - After preflight+origin detection, emit "update apply: <from> →
    <to> (origin=<origin>)" BEFORE any mutation invocation. DetectVersions
    reads only whitelisted sources (VERSION file + rpm/dpkg); no new
    contract surface.

G3-U16 idempotency visibility:
  - When current == target != "", emit "already up-to-date (... ==
    <ver>) — rebuild will no-op" marker
  - Apply still invokes rebuild — rebuild remains the single authority
    for "is this a no-op" (v1.96 atomic switch produces identical
    kernel state for identical input)
  - No short-circuit, no control-flow change

G3-U17 log/evidence adequacy:
  - beginPhase/endPhase wrappers track wall-clock duration per phase
  - Each PhaseEnd emits "  phase <Name> duration_ms=<N> result=pass|fail"
  - End-of-run trailer aggregates all phase results + final state

Tests (6 new, in cmd/nftban-installer/update_apply_logging_test.go):
  - T-L1 from→to line emitted after preflight detection (source install
    with distinct current/target)
  - T-L2 "already up-to-date" marker when current == target
  - T-L3 per-phase duration_ms + result=pass on happy path
  - T-L4 trailer contains mode/from/to/phases_passed/phases_failed/
    duration_ms/exit/final_state
  - T-L5 trailer fires via defer on every exit branch (happy,
    preflight-fail, rebuild-fail, validator-fail)
  - T-L6 AuditRecordedCommands + AuditWrittenFiles pass unchanged —
    PR-20 additions did not broaden the call path

Tests capture log output by constructing a Logger with logPath in
t.TempDir(), running the code, closing the logger, reading the file.

Non-goals honoured:
  - No new exec.Run calls in runUpdateApply
  - No new ApplyWhitelist entries
  - No change to exit-code contract or state transitions
  - No idempotency short-circuit — rebuild still runs on current==target
  - No validation/recovery/authority change

Contract: internal/installer/update/logging_contract.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@itcmsgr itcmsgr marked this pull request as ready for review April 19, 2026 17:49
@itcmsgr itcmsgr merged commit 44dd36f into main Apr 19, 2026
54 checks passed
@itcmsgr itcmsgr deleted the feat/v1.99-pr20-update-logging branch April 19, 2026 18:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant