Conversation
…n-CSF split Implements the §63 lattice extension + §64 evidence predicate from the Amendment 3 doc seed (#522). Decision-layer only. Lattice changes (engine.go): - 3 new rule constants: G1/AmbiguityConflictExternal/default → REFUSE (preserves pre-Amendment-3 hard-stop) G1/AmbiguityConflictExternal/OrphanProceed → PROCEED PanelNative/csf G1/AmbiguityConflictExternal/EvidenceMismatch → REFUSE (predicate any-false) - decideAmbiguityConflictExternal() function mirrors decideAuthorityNFTBan() in shape: Group 2 precedence preserved → quintuple-check (csf + DA + NoRecord + --panel-auto-takeover + --accept-orphan-nftban) → §64 predicate delegation. - Decide() now calls decideAmbiguityConflictExternal(in) for the AuthorityAmbiguous + AmbiguityConflictExternal branch (replaces the inline REFUSE). Evidence predicate (types.go): - New ExternalIndicator string field on DecisionInput (carries the classifier's external-authority string; required for §62 entry condition). - New AllTrueAmendment3() helper on OrphanEvidence: identical to AllTrue() EXCEPT row E.12 (NoConflictExternal) is omitted — the §62 entry IS AmbiguityConflictExternal so requiring "no conflict external" is incompatible by construction. - New FailedRowIDAmendment3() helper returns AMD3-E.{N} stable IDs so structured logs and Code-D evidence-records distinguish which predicate fired. Test matrix (engine_amendment3_test.go — new file): - 15-row §67 matrix (AMD3-1 through AMD3-15) including the 3 auditor- required defensive-guard rows: AMD3-13 empty external, AMD3-14 rule- label assertion ("amendment-3 orphan-intent" reason substring), AMD3-15 multi-external "csf,ufw". - TestAmd3_RuleConstants pins canonical rule strings. - TestAmd3_AllTrueAmendment3_OmitsE12 verifies E.12 is excluded. - TestAmd3_FailedRowIDAmendment3_SkipsE12 verifies row-walk skips E.12. - TestAmd3_NilEvidence_AMD3E0 verifies nil-receiver sentinel. Regression updates (engine_test.go, engine_amendment2_test.go): - 2 pre-existing fixtures asserting the old "G1/AmbiguityConflictExternal" rule string updated to RuleG1AmbConflictExtDefault. Behavior unchanged (still REFUSE); only the rule sub-classifier name shifts. Same pattern as Amendment 2's AuthorityNFTBan/default rename. - declaredRules() in engine_test.go updated to include the 3 new sub-rule constants (RuleG1AmbiguityConflictExt umbrella retained for grep parity). - 2 sentinel fixtures added to allFixtures pinning the new OrphanProceed and EvidenceMismatch rules for coverage assertion (full §67 matrix lives in engine_amendment3_test.go). NOT touched (in scope per operator): - internal/installer/uninstall/* (classifier — semantic unchanged) - internal/installer/restore/execute.go (mutation surface 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) - §32 11-step ordering (unchanged) - Amendment 2's §54 predicate (untouched; AllTrue() preserved for the AuthorityNFTBan path; AllTrueAmendment3() is a new sibling) Test results (lab4): - go test ./internal/installer/restore/... → ok - go test ./cmd/nftban-installer/... → ok - go test ./... → 64 packages ok, 0 FAIL No host action. No binary rebuild. No nftban-installer invocation. dns2 stays in canonical post-Gate-A state. Closes part of PR-26 final closure path: Gate B retry on dns2 unblocks once this PR merges + fresh Tier 1 binary is built + reachability monitor activates + pre-execution Gate B retry 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 |
11 tasks
itcmsgr
added a commit
that referenced
this pull request
Apr 29, 2026
…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>
itcmsgr
added a commit
that referenced
this pull request
Apr 29, 2026
… + Amendment-3 OrphanEvidence gathering (#527) * feat(v1.100 Amendment 3): code-B — dispatcher wires ExternalIndicator + 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> * refactor(v1.100 Amendment 3): code-B — split into separate gatherOrphanEvidenceAmendment3 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> --------- 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
Implements the Amendment 3 contract (PR #522, merged 2026-04-29) §63 lattice extension + §64 evidence predicate. Decision-layer only — no execute path touched, no classifier touched, no mutation surface added.
The Amendment 3 lattice splits the existing
G1/AmbiguityConflictExternalrow (locked-REFUSE) into:G1/AmbiguityConflictExternal/defaultG1/AmbiguityConflictExternal/orphan-intent-candidate-csfG1/AmbiguityConflictExternal/OrphanProceedKind=PanelNative,FirewallType=csf(when §64 all-true)G1/AmbiguityConflictExternal/EvidenceMismatchEntry condition (the candidate quintuple):
external=="csf"+Prior=NoRecord+Panel=DirectAdmin+--panel-auto-takeover+--accept-orphan-nftban.Mirrors Amendment 2's
G1/AuthorityNFTBansplit exactly. Same evidence-gating discipline. Same operator-intent override pattern.Files changed (5)
internal/installer/restore/engine.godecideAmbiguityConflictExternal()+Decide()dispatchinternal/installer/restore/types.goExternalIndicatorfield +AllTrueAmendment3()+FailedRowIDAmendment3()internal/installer/restore/engine_amendment3_test.gointernal/installer/restore/engine_test.godeclaredRules()extension + 1 fixture rule-name shiftinternal/installer/restore/engine_amendment2_test.goNOT changed (per operator scope)
internal/installer/uninstall/*— classifier unchangedinternal/installer/restore/execute.go— mutation surface unchangedinternal/installer/state/*— state machine unchangedcmd/nftban-installer/main.go— history gate unchangedcmd/nftban-installer/flags.go— flag surface unchanged.github/workflows/*— CI unchangedAllTrue()preservedTest results (lab4 build host)
go test ./internal/installer/restore/...→ okgo test ./cmd/nftban-installer/...→ okgo test ./...→ 64 packages ok, 0 FAILAll 15 §67 rows pass:
--accept-orphan-nftban--panel-auto-takeoverTest plan
153f7abe…)--mode=restore --panel-auto-takeover --accept-orphan-nftban)🤖 Generated with Claude Code