feat(v1.99 PR-20): update logging observability (G3-U15/U16/U17)#477
Merged
feat(v1.99 PR-20): update logging observability (G3-U15/U16/U17)#477
Conversation
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>
Contributor
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Pure observability layer on top of PR-19. No behaviour changes, no new
exec.Runcalls, no new whitelist entries, no new state transitions.Pinned sentence:
Scope
G3-U15 — Runtime state preserved in evidence
from → toversion line emitted after preflight/origin detection, BEFORE any mutation invocationmode,from,to,phases_passed,phases_failed,duration_ms,exit,final_stateG3-U16 — Idempotency visibility
current == target, log"already up-to-date — rebuild will no-op"G3-U17 — Log/evidence adequacy
beginPhase/endPhasewrappers track wall-clock per phasePhaseEndemitsphase <Name> duration_ms=<N> result=pass|failImplementation
One runtime file touched:
cmd/nftban-installer/update_apply.gorunMetastruct withbeginPhase/endPhase/emitTrailerrunUpdateApplynow 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-faildisplayVer,displayOrigin,passedStr(pure)Tests
6 new in
cmd/nftban-installer/update_apply_logging_test.go:duration_ms=+result=passat every PhaseEndAuditRecordedCommands+AuditWrittenFilesstill pass — logging did not broaden call pathNon-goals honoured
exec.Runcalls inrunUpdateApplyApplyWhitelistentriescurrent == targetGate sequence after this merges
Contract:
internal/installer/update/logging_contract.md🤖 Generated with Claude Code