#000037 Phase 1 continued: §14 row 4 + §13 step 11 + weight-tunable gates

Closes four gaps in the initial Phase 1 landing (commit f625cac):

1. §14 row 4 — Hermes-saturation guard. `hermes_utilization` was on
   the input contract but never consumed. Now: `utilization >=
   budget` → DEFERRED with HERMES_SATURATED note + advisory event
   carrying (utilization, budget) for Phase 2 audit. Three new
   tests (saturation-equal, saturation-overflow, headroom-exists).

2. §13 step 11 — falsification-fixture proposal emission.
   New `FalsificationFixtureProposal` dataclass; controller now
   emits one per branch whose `witness_divergence >=
   falsification_divergence_threshold` (weight-tunable, default
   0.5). Emission fires BEFORE the all-vetoed cascade so vetoed-
   AND-diverged branches still surface as 5F-fixture candidates
   per #000025. Four new tests (high-divergence emits, low-
   divergence stays silent, threshold is weight-tunable, vetoed-
   diverged emits anyway).

3. Entropy + memory gating moved from module-level constants
   (`H_LOW=0.3`, `H_HIGH=0.7`, `KAPPA_MEMORY=0.5`) to weight
   fields (`h_low`, `h_high`, `kappa_memory`). Constants stay as
   back-compat exports; defaults match exactly so byte-identical
   behavior when neither override fires. Three new tests
   (h_low/h_high tunable, kappa_memory tunable, back-compat
   match).

4. Veto-class cascade hardening. Added explicit tests for the
   `replay_window_unbounded` → ESCALATE path and the
   `soft_hash_signal` → QUARANTINE path (§6 + §14 documented but
   previously untested). Plus tests for the §6 fail-loud
   priority ordering: ESCALATE > QUARANTINE > REJECT when mixed.

Dry-run regenerated against ~/.arborist/shards corpus:
  Target A at τ_qa=1d surfaces **576 FalsificationFixture
  proposals** from witness_divergence >= 0.5 (24% of swept
  candidates). These rows are now an actionable funnel for 5F
  fixture mining under #000025 §3.

Module: arborist/substrate/prometheus.py (+117 LOC, 899 total)
Tests:  tests/test_prometheus.py (+196 LOC, 22 → 36 tests)
Dryrun: bench/scripts/prometheus_sigma_sweep_dryrun.py +
        bench/results/prometheus-sigma-sweep-dryrun-2026-05-10.md
        track the new falsification_proposals_total counter.

Full suite: 2326 passed, 37 skipped (+14 net from the new tests).
This commit is contained in:
russell@unturf.com 2026-05-10 16:56:18 -04:00
parent 61424370bd
commit f9f5ae459b
No known key found for this signature in database
4 changed files with 330 additions and 15 deletions

View file

@ -306,6 +306,7 @@ def sweep_target_a(shards_dir: Path, tau_qa_seconds: int, now: int, chunk_size:
veto_kinds: Counter[str] = Counter()
memory_proposals = 0
selfmodel_proposals = 0
falsification_proposals = 0
advisory_event_count = 0
candidates_total = 0
chunks_total = 0
@ -355,6 +356,7 @@ def sweep_target_a(shards_dir: Path, tau_qa_seconds: int, now: int, chunk_size:
label_counts[decision.label] += 1
memory_proposals += len(decision.memory_proposals)
selfmodel_proposals += len(decision.selfmodel_proposals)
falsification_proposals += len(decision.falsification_proposals)
advisory_event_count += len(decision.advisory_events)
for reasons in decision.veto_reasons.values():
for r in reasons:
@ -378,6 +380,7 @@ def sweep_target_a(shards_dir: Path, tau_qa_seconds: int, now: int, chunk_size:
label_counts[decision.label] += 1
memory_proposals += len(decision.memory_proposals)
selfmodel_proposals += len(decision.selfmodel_proposals)
falsification_proposals += len(decision.falsification_proposals)
advisory_event_count += len(decision.advisory_events)
for reasons in decision.veto_reasons.values():
for r in reasons:
@ -394,6 +397,7 @@ def sweep_target_a(shards_dir: Path, tau_qa_seconds: int, now: int, chunk_size:
"veto_kinds": dict(veto_kinds),
"memory_proposals_total": memory_proposals,
"selfmodel_proposals_total": selfmodel_proposals,
"falsification_proposals_total": falsification_proposals,
"advisory_event_count_total": advisory_event_count,
"runtime_total_ms": round(runtime_total_ms, 2),
"chunk_size": chunk_size,
@ -573,6 +577,9 @@ def render_markdown(a_results: dict, b_results: dict, opts: dict) -> str:
f"{a_results['memory_proposals_total']}")
lines.append(f"- SelfModel proposals: "
f"{a_results['selfmodel_proposals_total']}")
lines.append(f"- FalsificationFixture proposals "
f"(§13 Step 11 — high-divergence → 5F-fixture funnel): "
f"**{a_results.get('falsification_proposals_total', 0)}**")
lines.append(f"- Advisory event entries "
f"(would write to `controller_events` under Phase 2): "
f"{a_results['advisory_event_count_total']}")