KB-4B89

FIX7 Recheck-9 Produce Fail-Closed + Negative Tests — R9-B2 (2026-06-10)

4 min read Revision 1
tool-kiem-thufix7recheck-9packet-v2r9-b1-b52026-06-10

FIX7 Recheck-9 — Production --produce Fail-Closed + Negative Tests (R9-B2)

  • Date: 2026-06-10 · Authority: provisional-non-authority, evidence-only. Codex consulted: NO · Production mutation: NO (SSOT patch P-EXT-2 = authorized KB doc write in this lane).
  • Subject: FIX7-CANON-V1 canonicalizer SSOT fence (blueprint path, KB rev3) == evidence/fix7_canon_v1_ssot_extended.py (one canonical identity, sha256 d9caa9fe…f3e5).

1. The V1 defect (Codex evidence, reproduced)

--produce with docs/05-rollback-blueprint.md removed printed EXTRACT_ERROR=LOCAL_FILE_MISSING yet reported membership_frozen_ok: True and exited 0 (reproduced: REPRO_B2_MISSING05_PRODUCE_EXIT=0). Root causes: membership hashed the frozen ID constants (not the actual corpus); per-member errors never gated digests; --produce unconditionally sys.exit(0).

2. The P-EXT-2 repair (in the SSOT fence itself)

  1. validate_corpus_listing(actual_names) — pure fail-closed listing check vs the frozen membership: detects missing, extra, duplicate (exact or case-variant) members.
  2. extract() rejects empty active content (ACTIVE_CONTENT_EMPTY).
  3. membership recomputed over PRESENT+VALID members — a missing/invalid member can never reproduce the frozen pin f2bda8…fe251.
  4. gate_and_suppress() — single enforcement point: ANY listing problem / extract error / incomplete extraction ⇒ corpus_ok=false, membership_frozen_ok=false, and EVERY candidate digest (membership, corpus, registry, boundary, guard set, canonicalizer candidate, N7, N8) replaced by SUPPRESSED_CORPUS_NOT_OK.
  5. CLI: exit 0 ONLY iff corpus_ok AND membership_frozen_ok; exit 4 otherwise, printing ALL CANDIDATE DIGESTS SUPPRESSED (fail-closed: corpus not OK) plus the problem lists.
  6. Statuses documented in the SSOT prose: LOCAL_FILE_MISSING, DOCS_DIR_MISSING, GUARD_SET_SOURCE_MISSING, SUPPRESSED_CORPUS_NOT_OK, ACTIVE_CONTENT_EMPTY.
  7. Selftest 36 → 45 checks: +9 R9-B2 fixtures (exact/missing/extra/duplicate listings; gate truth table; suppression wiring over a synthetic problem corpus; empty-content rejection).

3. Executable negative tests (every one EXECUTED; results recomputed inside every --verify)

test construction expected observed
missing member (Codex's probe) docs copy minus 05-rollback-blueprint.md exit 4; all digests suppressed; membership_frozen_ok: False exit 4 ✓ suppression ✓ (suite T2a; verify negative_tests.produce_missing_member)
extra member docs copy + 99-extra-doc.md exit 4; suppression exit 4 ✓
invalid member doc 03 replaced by unbalanced-fence content exit 4; suppression (error FENCE_UNBALANCED) exit 4 ✓
docs dir absent nonexistent path exit 4; DOCS_DIR_MISSING exit 4 ✓
duplicate member listing with case-variant 05-Rollback-Blueprint.md (pure check — a case-insensitive FS cannot host the file pair) listing not ok, 1 duplicate ✓ (selftest fixture + verify negative_tests.listing_duplicate_detected)
wrong membership 9-doc digest ≠ frozen pin
doc tamper doc 03 + appended bytes hash diverges (caught by manifest/HASH_MANIFEST)
absent .py (recheck-8 class) open nonexistent file FileNotFoundError fail-closed

Nominal control: full valid corpus → exit 0, corpus_ok: True, membership_frozen_ok: True, and every digest value byte-identical to V1 (encoder untouched: N1×10 unchanged, N3 bb9ca0…, N4 9b111c…, N5 1144b7…, N6 rehearsal d777e8…, membership f2bda8…fe251).

4. Verdict

R9-B2 CLOSED. Detection now IS enforcement: any corpus problem suppresses all candidate digests, marks membership/corpus not OK, and exits nonzero — proven by executable negative tests that re-run inside every manifest verification and inside the adversarial suite.

Back to Knowledge Hub knowledge/dev/laws/tool-kiem-thu/reports/fix7-recheck9-produce-failclosed-negative-tests-2026-06-10.md