Skip to content

feat(v1.99 PR-19): update validation — truth enforcement (G3-U11/U12/U13)#476

Merged
itcmsgr merged 4 commits intomainfrom
feat/v1.99-pr19-update-validation
Apr 19, 2026
Merged

feat(v1.99 PR-19): update validation — truth enforcement (G3-U11/U12/U13)#476
itcmsgr merged 4 commits intomainfrom
feat/v1.99-pr19-update-validation

Conversation

@itcmsgr
Copy link
Copy Markdown
Owner

@itcmsgr itcmsgr commented Apr 19, 2026

Summary

Truth-enforcement layer on top of the PR-18 orchestrator. Closes the remaining G3 sub-gates on the validation/metadata surface.

Pinned sentence:

PR-19 is truth-enforcement only: it may refine validation outcome mapping, update metadata correctness, and history coherence, but may not introduce any new state-mutation, recovery, or authority-taking behavior.

Scope (G3-U11 / U12 / U13)

G3-U11 — Exit-code truth

PR-18 fixed the validator-fail state↔exit split via stateForValidatorExit(rc). During PR-19 survey I found the same class on the preflight-fail branch: transitions to state.StateFailedAbort (maps to exit 3) but returns hard-coded state.ExitDegraded (= 1). PR-19 fixes this and locks every state-transitioning branch with a regression test asserting sf.State.ExitCode() == <returned rc>.

G3-U12 — Update history integrity

  • writeHistory must emit install_fail (not success) whenever sf.State != StateCommitted
  • from/to metadata must reflect the operator-intended transition (the plan's versions), not just the installer binary's compile-time constant
  • installType must have a source case — currently defaults to rpm/deb only, silently mislabeling source installs as RPM

G3-U13 — Source/package coherence

Builds on PR-17's install_origin_coherent preflight. Extends enforcement to the history/metadata surface:

  • source installs record installType = "source"
  • origin-mismatch is reflected in a new optional coherence field
  • no mixed-mode success reporting

Explicit non-goals

  • ❌ No new mutation path
  • ❌ No new recovery / rollback behavior
  • ❌ No new authority-taking logic
  • ❌ No change to runUpdateApply's call graph
  • ❌ No new InstallState enum value
  • ❌ No reinterpretation of validator JSON body

Implementation plan (commit-by-commit)

  1. Contract seedvalidation_contract.md (landed as 0dce2907)
  2. G3-U11 exit-code truth — preflight-fail transition fix + regression tests per branch
  3. G3-U12 history integrity — status-from-state helper + source installType + from/to from plan
  4. G3-U13 coherence — origin-mismatch surfacing + success-blocking on mismatch
  5. CI gate additions — G3-U11/U12/U13 checks in ci-update-canonization.yml

Depends on

  • PR-18 merged (main 22f8fb41) — provides stateForValidatorExit pattern + runUpdateApply + contract audit harness

Status: Draft — un-drafts after CI green.

🤖 Generated with Claude Code

Pinned sentence (repeated in PR body + contract file):

  "PR-19 is truth-enforcement only: it may refine validation outcome
   mapping, update metadata correctness, and history coherence, but may
   not introduce any new state-mutation, recovery, or authority-taking
   behavior."

Contract surfaces the three G3 sub-gates PR-19 closes:

G3-U11 exit-code truth:
  - Extends the PR-18 stateForValidatorExit discipline to every state-
    transitioning branch in runUpdateApply
  - Flags an existing truth split on the preflight-fail branch:
    transitions to StateFailedAbort (exits 3) but returns hard-coded
    state.ExitDegraded (= 1). Same class the reviewer caught in PR-18.
  - Locks every transition with a regression test that asserts
    sf.State.ExitCode() == returned process exit

G3-U12 update history integrity:
  - writeHistory (main.go:335) currently writes install_fail on any non-
    committed state, but history fields like from/to are written
    regardless. For intermediate states this is misleading.
  - installType defaults to "rpm" and falls back to "deb"; source
    installs get labeled "rpm" incorrectly.
  - from/to should reflect operator-intended versions when a plan is
    available, not just the installer binary's compile-time version.

G3-U13 source/package coherence:
  - Source installs must record installType = "source" in history JSON
  - P-7 (install_origin_coherent) failures must block success status
  - New optional coherence field in history for origin_mismatch evidence

Non-goals pinned:
  - No new mutation/recovery/authority logic
  - No change to runUpdateApply call graph
  - No new InstallState enum value
  - No validator JSON reinterpretation
  - No change to ApplyWhitelist (unless legitimately read-only)

Post-merge watch from PR-18 carried forward:
  "keep the same discipline around validator severity and persisted
   lifecycle state"

This commit lands the contract BEFORE any code change, same pattern as
PR-18 step 0. Implementation commits land on this branch in order:
step 2 (exit-code truth) → step 3 (history integrity) → step 4 (CI gates).

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

github-actions Bot commented Apr 19, 2026

Dependency Review

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

Scanned Files

None

itcmsgr and others added 3 commits April 19, 2026 20:21
Same class of truth split PR-18 fixed for the validator-fail branch,
this time on the preflight-fail branch of runUpdateApply. The reviewer
flagged state↔exit discipline in the PR-18 post-merge watch item:
"keep the same discipline around validator severity and persisted
lifecycle state. Do not reintroduce any state↔exit split through a
new validation-focused path." PR-19 locks this in both directions.

Before:
  sf.Transition(state.StateFailedAbort, ...)  // ExitCode() = 3
  return state.ExitDegraded                    // = 1
  // persisted state disagreed with returned process exit

After:
  st := stateForPreflightFailure(pre)
  sf.Transition(st, ...)
  return st.ExitCode()
  // by construction, state.ExitCode() == returned rc

New helper stateForPreflightFailure(pre) in update_apply.go:
  - Today: all critical preflight failures → StateFailedNoFirewall
    (ExitCode = ExitFailed = 2). Honest: preflight exists to gate apply
    on "nftban is functionally present and authoritative."
  - Signature accepts full PreflightResult so a future PR can deepen
    the mapping without changing callers.
  - No mini policy engine inside: this stays a thin truth-enforcement
    layer per the PR-19 contract sentence.

Test T2 extended:
  - Asserts sf.State.ExitCode() == rc (state↔exit regression guard)
  - Asserts sf.State == StateFailedNoFirewall specifically

Non-goals honoured: no new mutation path, no new recovery, no new
enum value, no change to runUpdateApply's call graph, no validator
JSON reinterpretation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
G3-U12 (update history integrity):
  cmd/nftban-installer/main.go
    - Extract historyStatusForState(s) from writeHistory: pure helper
      that maps InstallState → history status string. Every non-
      StateCommitted state reports install_fail or verify_fail. No
      success coercion for intermediate/non-terminal states.
    - Extract historyInstallType(cfg): pure helper with priority order
      source > deb > rpm > default. Both helpers unit-testable.

G3-U13 (source/package coherence):
  cmd/nftban-installer/main.go
    - historyInstallType now has a source case. Previously source
      installs were silently mislabeled as "rpm" (default fallback),
      creating a coherence violation between declared install origin
      and recorded history. Fix: explicit cfg.source → "source".

New file cmd/nftban-installer/history_test.go:
  - TestHistoryStatusForState_Committed_IsSuccess
  - TestHistoryStatusForState_Degraded_IsVerifyFail
  - TestHistoryStatusForState_AllFailureStates_AreInstallFail (6
    failure states exhaustively)
  - TestHistoryStatusForState_IntermediateStates_AreInstallFail
    (5 non-terminal states — regression guard for G3-U12's
    "timeout/signal mid-apply must not report success" rule)
  - TestHistoryInstallType_{Source,DEB,RPM} — 3 base cases
  - TestHistoryInstallType_Priority_SourceOverridesDEB
  - TestHistoryInstallType_Priority_DEBOverridesRPM
  - TestHistoryInstallType_NoFlag_DefaultsToRPM

CI gate additions in .github/workflows/ci-update-canonization.yml:

  G3-U11 — structural check: no hard-coded exit literal returned
  after a StateFailed transition (regression guard for the original
  preflight-fail contradiction)

  G3-U12 — structural check: StatusSuccess literal must not appear
  outside the StateCommitted case arm (regression guard for future
  success coercion)

  G3-U13 — structural check: historyInstallType must contain a
  cfg.source case (regression guard for mislabeling fix)

All three CI steps run in addition to the unit tests — belt and
suspenders so a careless future change can't slip past just the unit
tests OR just the grep.

Non-goals honoured:
  - No mutation path change
  - No recovery/rollback change
  - No authority change
  - No change to runUpdateApply call graph
  - No new InstallState enum
  - No validator JSON reinterpretation

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…wn helper

The previous grep filtered out lines containing 'StateCommitted' OR
'historyStatusForState', but the actual line in historyStatusForState:

    case state.StateCommitted:
      return history.StatusSuccess   ← this line

only contains "history.StatusSuccess" — not either filtered token. So
the check flagged the correct implementation as a violation.

Fix: tighter grep that verifies the line IMMEDIATELY preceding every
"history.StatusSuccess" reference is "case state.StateCommitted:".
This catches any future refactor that moves the literal to a different
case arm or fabricates a new return site.

Behaviour:
  - 0 matches total → FAIL (literal must exist somewhere)
  - ≥1 matches not preceded by StateCommitted case → FAIL with
    line number + offending previous line for diagnosis
  - all matches gated → PASS

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@itcmsgr itcmsgr marked this pull request as ready for review April 19, 2026 17:32
@itcmsgr itcmsgr merged commit 9f88250 into main Apr 19, 2026
54 checks passed
@itcmsgr itcmsgr deleted the feat/v1.99-pr19-update-validation branch April 19, 2026 17:39
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