Conversation
… + Amendment-3 OrphanEvidence gathering Closes the dispatcher-side wiring gap surfaced by the post-merge audit of Amendment-3-code-A (PR #523). Without this PR, the engine's G1/AmbiguityConflictExternal split (added in PR #523) cannot be reached because the dispatcher does not (a) plumb auth.External into DecisionInput.ExternalIndicator, and (b) trigger gatherOrphanEvidence on the Amendment 3 quintuple shape. cmd/-side wiring only. Decision-layer engine.go untouched. Contract.md untouched. Mutation surfaces unchanged. State machine, exit codes, classifier, and CI all unchanged. Changes: cmd/nftban-installer/restore_decide.go (+50/-13) - DecisionInput initializer now sets ExternalIndicator: auth.External so the engine's §62 entry condition (external == "csf") can be evaluated. - OrphanEvidence-gathering condition extended to recognize EITHER candidate quintuple: * Amendment 2 (§54.3): AuthorityNFTBan + NoRecord + DA + flags * Amendment 3 (§62): AuthorityAmbiguous + ConflictExternal + external=="csf" + NoRecord + DA + flags - Diagnostic log line now reports both predicates (amd2_all_true, amd2_failed_row, amd3_all_true, amd3_failed_row) for observability; the engine consumes whichever is appropriate per the entry path. cmd/nftban-installer/restore_decide_evidence.go (+47/-3) - E.2 reframed per Amendment 3 §64.1: bool is true under EITHER Amendment 2's AuthorityNFTBan entry OR Amendment 3's AuthorityAmbiguous + AmbiguityConflictExternal + external=="csf" entry. Both candidate quintuples produce E.2=true; all other classifier states produce E.2=false (defensive — empty external, non-csf external, multi-external, OrphanNFTBan ambiguity, and AuthorityExternal all fail E.2). - E.13 evaluation tightened to match §54.1 / §64.1 wording exactly ("no AmbiguityOrphanNFTBan" — not the looser "no AuthorityAmbiguous" that conflated the two Amendment 3 ambiguity sub-kinds). Amendment 2 behavior preserved (its entry implies Ambiguity==None which is != OrphanNFTBan). cmd/nftban-installer/restore_decide_amendment3_test.go (NEW, +200) - TestAmd3Dispatcher_E2Reframed_AllTrueAmendment3_True confirms the Amendment 3 entry condition produces E.2=true and AllTrueAmendment3 passes (and Amendment 2's AllTrue() correctly fails because E.12 cannot be true under the §62 entry by construction). - TestAmd3Dispatcher_E2_FalseWhenMisclassified pins 7 classifier shapes: amd2-path-true, amd3-path-true, empty-external-false, ufw-external-false, multi-external-false, OrphanNFTBan-false, AuthorityExternal-false. - TestAmd3Dispatcher_FailedRow_Amendment3 verifies FailedRowIDAmendment3() returns "" on happy path and AMD3-E.7 when E.7 is mutated false. NOT touched: - internal/installer/restore/engine.go (lattice unchanged) - internal/installer/restore/types.go (struct unchanged) - internal/installer/restore/contract.md (no amendment) - internal/installer/uninstall/* (classifier unchanged) - internal/installer/state/* (state machine unchanged) - cmd/nftban-installer/main.go (history gate unchanged) - cmd/nftban-installer/flags.go (flag surface unchanged) - .github/workflows/* (CI unchanged) Test results (lab4): - go test ./cmd/nftban-installer/... → ok - go test ./internal/installer/restore/... → ok - go test ./... → 64 packages ok, 0 FAIL No host action. No binary rebuild. dns2 stays in canonical post-Gate-A state. Unblocks Gate B retry once a fresh Tier 1 binary is built from post-merge HEAD, distributed to dns2, signoff captured, reachability monitor activated, and pre-execution Gate B audit returns GO. 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 |
…anEvidenceAmendment3 helper per auditor recommendation Per auditor pre-draft review: implement Sub-gap 2a as a separate gatherOrphanEvidenceAmendment3() helper rather than a mode-conditional inside the existing gatherOrphanEvidence(). Mirrors the AllTrue() vs AllTrueAmendment3() split that PR #523 introduced in types.go and the decideAuthorityNFTBan() vs decideAmbiguityConflictExternal() split in engine.go. "Two helpers, shared rows, different entry-condition filling" — symmetric with the engine pattern. Refactor changes (no behavior delta vs the prior code-B commit on this branch): cmd/nftban-installer/restore_decide_evidence.go - populateSharedOrphanEvidenceRows(): private helper populates all rows EXCEPT E.2 (the entry-condition row). - gatherOrphanEvidence(): Amendment 2 — calls shared helper + sets E.2 = (Authority == AuthorityNFTBan). Restored to Amendment-2-byte-clean E.2 evaluation. - gatherOrphanEvidenceAmendment3(): Amendment 3 — calls shared helper + sets E.2 = (AuthorityAmbiguous + ConflictExternal + external == "csf"). New sibling helper. - E.13 retained at the contract-wording-exact form (Ambiguity != AmbiguityOrphanNFTBan); shared by both helpers via the populate function. cmd/nftban-installer/restore_decide.go - Dispatcher's evidence-gathering now switches on the candidate type: amd2Candidate calls gatherOrphanEvidence (unchanged from pre-Amendment-3 main); amd3Candidate calls gatherOrphanEvidenceAmendment3. - Diagnostic log line is now amendment-specific (no dual-predicate noise) — clearer audit trail per amendment. cmd/nftban-installer/restore_decide_amendment3_test.go - Renamed and reorganized test cases to test the two helpers independently: * TestGatherOrphanEvidenceAmendment3_* tests the Amendment 3 helper specifically (rejects Amendment 2 entry, accepts §62 entry, AMD3-E.2 attribution on external=ufw, omits E.12). * TestGatherOrphanEvidence_Amendment2Unchanged regression-tests the Amendment 2 helper: still strict on AuthorityNFTBan, rejects Amendment 3 entry. Why two helpers (auditor reasoning): - Audit-chain clarity: one helper per amendment, mirroring the engine.go and types.go split structure. - Each helper's CI grep gates and tests can reason about it independently. - No runtime classifier-state coupling between the dispatcher's inspection and the evidence-gathering. - Symmetric with AllTrue()/AllTrueAmendment3() in types.go. Test results (lab4): - go test ./cmd/nftban-installer/... → ok - go test ./internal/installer/restore/... → ok - go test ./... → 64 packages ok, 0 FAIL No host action. No engine.go change. No contract.md change. No new mutation surface. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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
Closes the dispatcher-side wiring gap that the auditor flagged in their post-merge ruling on Amendment-3-code-A (PR #523). Without this PR, the engine's
G1/AmbiguityConflictExternalsplit cannot be reached on a real host because the dispatcher does not:auth.ExternalintoDecisionInput.ExternalIndicator(required by §62 entry conditionexternal == "csf")gatherOrphanEvidenceon the Amendment 3 quintuple shape (AuthorityAmbiguous + AmbiguityConflictExternal + external=="csf" + NoRecord + DA + both flags)E.2per Amendment 3 §64.1 so the dispatcher's entry condition is reflected in the evidence rowcmd/-side wiring only. Engine.go untouched. Contract.md untouched. No mutation surfaces. No new state terminals. No new exit codes. No CI change.
Files changed (3)
cmd/nftban-installer/restore_decide.gocmd/nftban-installer/restore_decide_evidence.gocmd/nftban-installer/restore_decide_amendment3_test.goNOT changed
internal/installer/restore/engine.go— lattice unchangedinternal/installer/restore/types.go— struct unchangedinternal/installer/restore/contract.md— no amendmentinternal/installer/uninstall/*— classifier unchangedinternal/installer/state/*— state machine unchangedcmd/nftban-installer/main.go— history gate unchangedcmd/nftban-installer/flags.go— flag surface unchanged.github/workflows/*— CI unchangedE.13 wording fix (carried in this PR)
The original
gatherOrphanEvidenceevaluatedE.13 = (auth.State != AuthorityAmbiguous). Per §54.1 and §64.1, the wording is "no AmbiguityOrphanNFTBan". The two were equivalent under Amendment 2'sAuthorityNFTBanentry condition (Ambiguity is None, so OrphanNFTBan check is trivially true), but they diverge under Amendment 3'sAuthorityAmbiguous + ConflictExternalentry. Amendment 3 requires E.13=true; the strict implementation returned false. Fixed to match contract wording exactly:E.13 = (auth.Ambiguity != AmbiguityOrphanNFTBan). Amendment 2 path preserved.Test results (lab4)
go test ./cmd/nftban-installer/...→ okgo test ./internal/installer/restore/...→ okgo test ./...→ 64 packages ok, 0 FAIL3 new test functions, 9 sub-cases including:
Test plan
153f7abe…)systemctl reset-failed lfd.service(cosmetic, idempotent if dns2 didn't reboot)--mode=restore --panel-auto-takeover --accept-orphan-nftban🤖 Generated with Claude Code