Skip to content

feat(v1.100 Amendment 3): code-B — dispatcher wires ExternalIndicator + Amendment-3 OrphanEvidence gathering#527

Merged
itcmsgr merged 2 commits intomainfrom
amendment-3-code-B-dispatcher-wiring
Apr 29, 2026
Merged

feat(v1.100 Amendment 3): code-B — dispatcher wires ExternalIndicator + Amendment-3 OrphanEvidence gathering#527
itcmsgr merged 2 commits intomainfrom
amendment-3-code-B-dispatcher-wiring

Conversation

@itcmsgr
Copy link
Copy Markdown
Owner

@itcmsgr itcmsgr commented Apr 29, 2026

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/AmbiguityConflictExternal split cannot be reached on a real host because the dispatcher does not:

  1. Plumb auth.External into DecisionInput.ExternalIndicator (required by §62 entry condition external == "csf")
  2. Trigger gatherOrphanEvidence on the Amendment 3 quintuple shape (AuthorityAmbiguous + AmbiguityConflictExternal + external=="csf" + NoRecord + DA + both flags)
  3. Reframe E.2 per Amendment 3 §64.1 so the dispatcher's entry condition is reflected in the evidence row

cmd/-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)

File Change Lines
cmd/nftban-installer/restore_decide.go DecisionInput sets ExternalIndicator + extended OrphanEvidence-gathering trigger +50/−13
cmd/nftban-installer/restore_decide_evidence.go E.2 reframed per §64.1; E.13 tightened to "no AmbiguityOrphanNFTBan" wording +47/−3
cmd/nftban-installer/restore_decide_amendment3_test.go 3 new tests, 9 sub-cases (NEW) +200/0

NOT changed

  • 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
  • §32 11-step ordering — unchanged
  • Amendment 2 §54 path — preserved (regression-checked via existing tests + E.13 wording match)

E.13 wording fix (carried in this PR)

The original gatherOrphanEvidence evaluated E.13 = (auth.State != AuthorityAmbiguous). Per §54.1 and §64.1, the wording is "no AmbiguityOrphanNFTBan". The two were equivalent under Amendment 2's AuthorityNFTBan entry condition (Ambiguity is None, so OrphanNFTBan check is trivially true), but they diverge under Amendment 3's AuthorityAmbiguous + ConflictExternal entry. 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/...ok
  • go test ./internal/installer/restore/...ok
  • go test ./...64 packages ok, 0 FAIL

3 new test functions, 9 sub-cases including:

  • E.2 = true on Amendment 2 entry (AuthorityNFTBan)
  • E.2 = true on Amendment 3 entry (AuthorityAmbiguous + ConflictExternal + external=csf)
  • E.2 = false on empty external, ufw external, multi-external (csf,ufw), OrphanNFTBan ambiguity, AuthorityExternal
  • AllTrueAmendment3 = true on Amendment 3 happy path
  • AllTrue (Amendment 2) = false on Amendment 3 happy path (E.12 cannot be true)
  • FailedRowIDAmendment3 returns "" on happy and "AMD3-E.7" on E.7 mutation

Test plan

  • Auditor on-PR audit: scope verification (cmd/-side only), no engine.go change, no contract change, all 9 sub-cases pass
  • Operator merges
  • Fresh Tier 1 binary built on lab4 from post-merge HEAD (supersedes 153f7abe…)
  • Distribute to dns2; byte-pin source==host
  • Operator runs systemctl reset-failed lfd.service (cosmetic, idempotent if dns2 didn't reboot)
  • Fresh dns2-gate-b H2/H3 populated
  • External reachability monitor activated → reachability-pre.txt captured (HARD-GATE-WITH-NO-EXCEPTIONS)
  • Pre-execution Gate B retry audit returns GO
  • Single Gate B retry invocation on dns2: --mode=restore --panel-auto-takeover --accept-orphan-nftban
  • Post-B audit returns GO
  • PR-26 final mergeable

🤖 Generated with Claude Code

… + 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>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 29, 2026

Dependency Review

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

Scanned Files

None

…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 itcmsgr merged commit 7c9b409 into main Apr 29, 2026
63 checks passed
@itcmsgr itcmsgr deleted the amendment-3-code-B-dispatcher-wiring branch April 29, 2026 13:53
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