Skip to content

feat(v1.100 PR-P2-1): prior-authority record hardening — 5-state classification + required RecordedAt/InstallerVersion/ActiveAtInstall#484

Merged
itcmsgr merged 1 commit intomainfrom
fix/v1.100-pr-p2-1-prior-authority-hardening
Apr 20, 2026
Merged

feat(v1.100 PR-P2-1): prior-authority record hardening — 5-state classification + required RecordedAt/InstallerVersion/ActiveAtInstall#484
itcmsgr merged 1 commit intomainfrom
fix/v1.100-pr-p2-1-prior-authority-hardening

Conversation

@itcmsgr
Copy link
Copy Markdown
Owner

@itcmsgr itcmsgr commented Apr 19, 2026

Pre-PR-23 blocker #1 of 6. Strengthens prior-authority records so PR-24 restore-enforcement can rely on them without inheriting weak semantics.

Migration note (required per authorization)

Older prior-authority records may now classify as Incomplete by design. This is intentional safety tightening, not a functional regression.

Records produced by the install-side writer landing with PR-23 will carry all required fields. Until then, existing fixtures / local test records without recorded_at / installer_version / explicit active_at_install will be reclassified as Incomplete — this is the explicit backward-safety path.

Scope-locked (per repair contract)

  • ✅ schema additions (recorded_at, installer_version, *bool active_at_install)
  • ✅ 5-state classification (NoRecord / Malformed / Incomplete / UsableActive / UsableInactive)
  • ✅ backward-safe degradation of old weak records to Incomplete
  • ✅ render/JSON symmetry — PriorActiveAtInstall field + render line
  • ✅ tests + contract/doc updates

Not in scope:

  • ❌ no restore execution
  • ❌ no lifecycle mutation
  • ❌ no signature/checksum framework
  • ❌ no unrelated refactors

What "usable" now means

Record is usable only if: JSON parses, schema matches, firewall type known, recorded_at present + parseable, installer_version present, AND active_at_install is explicitly committed (pointer non-nil — not just Go's zero-value false). Usable does not imply "restore/re-enable now" — PR-24 defines its own authorization rules on top.

5-state classification

State Condition PR-24 trust level
NoRecord no artifact on disk cannot restore
RecordMalformed JSON does not parse cannot restore
RecordIncomplete required field missing/invalid cannot restore (see IncompleteReason for why)
RecordUsableActive all required + active_at_install=true restore = re-enable a firewall that was running
RecordUsableInactive all required + active_at_install=false restore = enable a firewall that was NOT running (warrants prompt)

IncompleteReason enum: Unreadable / SchemaMismatch / MissingFirewallType / UnknownFirewallType / MissingRecordedAt / MissingInstallerVersion / MissingActiveAtInstall. Machine-consumable so downstream tooling doesn't have to substring-match notes.

Plan integration

  • Plan.RestoreAuthorized now uses PriorState.IsUsable() — both Usable* variants authorize at this layer
  • new Plan.PriorActiveAtInstall *bool surfaces the tri-state (nil = unknown, &true/&false = explicit)
  • new Plan.PriorIncompleteReason surfaces typed reason in JSON
  • new render line Prior firewall at install : active|inactive — only emitted when record is usable (no defaulted values)
  • new warning when restore requested on UsableInactive record: "restoration would re-enable a firewall the operator was not running"

Tests (all in internal/installer/uninstall/uninstall_test.go)

Added:

  • TestProbe_RecordUsableActive (rename + tightened) + TestProbe_RecordUsableInactive
  • TestProbe_RecordMalformed_BadJSON (split from Incomplete)
  • TestProbe_RecordIncomplete_MissingRecordedAt / MissingInstallerVersion / MissingActiveAtInstall — each asserts both State AND typed IncompleteReason
  • TestProbe_OldStyleRecord_DegradesToIncomplete — explicit backward-safety regression guard
  • TestBuildPlan_Restore_UsableInactive_AuthorizedButWarned

All existing tests updated: fixtures carry the 5 required fields; JSON assertions use record_usable_active.

Doc update

New "PR-P2-1 hardening" section in internal/installer/uninstall/contract.md under the prior-authority record definition.

Acceptance checklist (per repair contract §5)

  • weak old records are not silently treated as fully usable
  • active_at_install=false is explicit and test-covered
  • recorded_at and installer_version are required for usable
  • no restore behavior added
  • no mutation behavior changes
  • docs updated
  • signature/checksum work NOT added (deferred)

Test plan

  • Build & Test green — all unit tests pass including 8 new probe tests
  • ci-uninstall-canonization matrix green — all 3 modes' existing tests still pass (render/JSON/plan structural checks)
  • No CI regression on install/update canonization gates (scope-locked; no touches)

🤖 Generated with Claude Code

…sification + required RecordedAt/InstallerVersion/ActiveAtInstall

Pre-PR-23 blocker #1. Strengthens prior-authority records so PR-24
restore-enforcement can rely on them without inheriting weak semantics.

**Scope-locked per repair contract:**
- schema additions (recorded_at, installer_version, *bool active_at_install)
- 5-state classification (NoRecord / Malformed / Incomplete /
  UsableActive / UsableInactive)
- backward-safe degradation of old weak records to Incomplete
- render/JSON symmetry — PriorActiveAtInstall field + render line
- tests + contract/doc updates only
- NO restore execution, NO lifecycle mutation, NO signature/checksum
  framework, NO unrelated refactors

## Schema additions (prior.go)

```go
type PriorRecord struct {
    SchemaVersion    string
    FirewallType     string
    RecordedAt       *time.Time  // NEW — required for usable
    InstallerVersion string      // NEW — required for usable
    ActiveAtInstall  *bool       // CHANGED from bool — tri-state:
                                 //   nil   = record writer did not commit
                                 //   &true = prior was active
                                 //   &false = prior was explicitly inactive
}
```

## 5-state classification

| State | Condition |
|---|---|
| NoRecord | no artifact on disk |
| RecordMalformed | JSON does not parse (was folded into Incomplete before) |
| RecordIncomplete | parses but required field missing/invalid |
| RecordUsableActive | all required + ActiveAtInstall=&true |
| RecordUsableInactive | all required + ActiveAtInstall=&false |

`PriorRecordState.IsUsable()` helper returns true for both Usable*
variants — RestoreAuthorized uses it.

`IncompleteReason` enum provides machine-consumable reason codes:
Unreadable, SchemaMismatch, MissingFirewallType, UnknownFirewallType,
MissingRecordedAt, MissingInstallerVersion, MissingActiveAtInstall.

## Backward-safety

Older records from before PR-P2-1 that lack recorded_at /
installer_version / explicit active_at_install are intentionally
reclassified as RecordIncomplete. Migration-style silent upgrade is
forbidden. Test `TestProbe_OldStyleRecord_DegradesToIncomplete`
falsifies this path directly.

> **Migration note:** Older prior-authority records may now classify
> as Incomplete by design. This is intentional safety tightening, not
> a functional regression. Records produced by the install-side writer
> that lands alongside PR-23 will carry all required fields.

## Plan integration (plan.go)

- `Plan.RestoreAuthorized` now uses `PriorState.IsUsable()` — both
  Usable variants authorize, active/inactive is separate dimension
- new `Plan.PriorIncompleteReason` surfaces typed reason in JSON
- new `Plan.PriorActiveAtInstall *bool` surfaces the tri-state
  (nil = unknown, &true/&false = explicit) — PR-24 uses this to
  decide whether restoration needs a second confirmation prompt
- new Render line `Prior firewall at install : active|inactive`
  only emitted when record is usable — no defaulted values
- new warning when restore requested on UsableInactive record

## Tests

Added (uninstall_test.go):

- `TestProbe_RecordUsableActive` (rename of TestProbe_RecordUsable)
- `TestProbe_RecordUsableInactive` — active_at_install=false path
- `TestProbe_RecordMalformed_BadJSON` — split from Incomplete
- `TestProbe_RecordIncomplete_MissingRecordedAt`
- `TestProbe_RecordIncomplete_MissingInstallerVersion`
- `TestProbe_RecordIncomplete_MissingActiveAtInstall`
- `TestProbe_OldStyleRecord_DegradesToIncomplete` — backward-safety
- `TestBuildPlan_Restore_UsableInactive_AuthorizedButWarned`
- All existing tests updated: fixtures carry all 5 required fields;
  JSON strings updated to `record_usable_active`

Every Incomplete* test also asserts the specific IncompleteReason
(not just the state) so regressions attribute cleanly.

## Doc update (contract.md)

Added "PR-P2-1 hardening" section under the prior-authority record
definition: describes the 5 states, what "usable" now means, explicit
backward-safety rule.

## Non-goals (strict scope lock)

- no restore execution
- no lifecycle mutation
- no signature/checksum framework
- no unrelated refactors

Refs: internal/installer/uninstall/contract.md §"Pre-PR-23 blockers"
Authorization: locked repair contract (pre-PR-23 Phase 2 item #1)

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 merged commit 3b83403 into main Apr 20, 2026
57 checks passed
@itcmsgr itcmsgr deleted the fix/v1.100-pr-p2-1-prior-authority-hardening branch April 20, 2026 06:01
itcmsgr added a commit that referenced this pull request Apr 20, 2026
PR-P2-1 (prior-authority record hardening) merged as 3b83403. The
blocker table in internal/installer/uninstall/contract.md now shows
item 1 struck through with explicit LANDED status + merge SHA. Items
2-6 remain pending; each is still its own bounded PR with
micro-contract + falsifiable proof test per the approved sequencing.

No code changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
itcmsgr added a commit that referenced this pull request Apr 20, 2026
…uckets

Per review feedback: the table previously mixed code-semantic changes
with CI/gate additions, and said "all six items" when #1 is already
landed. Restructured for clarity:

- Landed bucket — #1 (prior-authority hardening, PR #484)
- Behavioral / semantic blockers — #2 (external-firewall detection
  unification), #6 (payload integrity minimum checks)
- Assurance / gate blockers — #3 (kernel/service snapshot CI),
  #4 (exec-trace CI), #5 (auto-elevate shim removal gate)

Wording tightened: "PR-23 starts only after all remaining pre-PR-23
blockers merge" instead of "all six items below."

Also flagged that PR-24/25/26 ordering is preferred, not dogmatic —
restore enforcement may expose facts that affect artifact-removal
semantics.

No code changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
itcmsgr added a commit that referenced this pull request Apr 20, 2026
…uckets + mark blocker #1 LANDED (#485)

* docs(v1.100): mark pre-PR-23 blocker #1 LANDED (PR #484)

PR-P2-1 (prior-authority record hardening) merged as 3b83403. The
blocker table in internal/installer/uninstall/contract.md now shows
item 1 struck through with explicit LANDED status + merge SHA. Items
2-6 remain pending; each is still its own bounded PR with
micro-contract + falsifiable proof test per the approved sequencing.

No code changes.

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

* docs(v1.100): split pre-PR-23 blockers into behavioral vs assurance buckets

Per review feedback: the table previously mixed code-semantic changes
with CI/gate additions, and said "all six items" when #1 is already
landed. Restructured for clarity:

- Landed bucket — #1 (prior-authority hardening, PR #484)
- Behavioral / semantic blockers — #2 (external-firewall detection
  unification), #6 (payload integrity minimum checks)
- Assurance / gate blockers — #3 (kernel/service snapshot CI),
  #4 (exec-trace CI), #5 (auto-elevate shim removal gate)

Wording tightened: "PR-23 starts only after all remaining pre-PR-23
blockers merge" instead of "all six items below."

Also flagged that PR-24/25/26 ordering is preferred, not dogmatic —
restore enforcement may expose facts that affect artifact-removal
semantics.

No code changes.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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