Skip to content

ledger-signals: blockedEvalStreak ignores its own "majority of window" doc comment, false-positives on a minority-blocked tail + witness,verify scan #153

Open

Description

@ruvnet

Nightly Dream Cycle, 2026-10-06. DEEP=ledger-signals, SCAN=witness,verify. Full report: docs/dream-cycle/2026-10-06-ledger-signals-report.md.

Rotation

Slot DAYINT % 5 == 1 → DEEP=ledger-signals, SCAN=witness,verify. No bonus deep dive (DAYINT % 25 == 6, DAYINT % 75 == 56).

Ledger Check

docs/dream-cycle/LEDGER.md read (52 rows). ledger signals on main: zeroMergeStreak=true, ledgerStale=true (daysSinceLastRow=6, last row 2026-09-30), duplicateDirections=["harden anchored replay json evidence reject"] (a historical cross-repo "portfolio" finding, not applicable to this self-hosting-only config), blockedEvalStreak=false, lowScoreStreak=false. Last 7 nights' PRs spot-checked live via list_pull_requests/search_pull_requests: #145 (2026-10-01, ledger-signals, open/draft) already fixes a different ledger bug (splitRow pipe-escape round-trip) — confirmed no overlap with tonight's candidate via its full diff. #150 (2026-10-03, security-adversarial, open/draft) already covers this session's own independently-rediscovered shallow-clone BASELINE_COMMIT governance failure — not re-filed. #152, #148, #141, #139, #137 also open/draft on other surfaces, no overlap.

Deep Dive

packages/ledger/src/index.ts's learningSignals() computes blockedEvalStreak. The code comment directly above it reads: "Blocked-eval streak: majority of the window blocked, and last 3 all blocked." The implementation only ever checked the second half: recent.length >= 3 && lastThreeEvals.length === 3 && lastThreeEvals.every(e => e === 'blocked') — no majority computation existed anywhere in the function. Live repro: 11 yes + 3 blocked (trailing) rows in a 14-row window → blockedEvalStreak: true, even though blocked rows are a clear minority (3/14 ≈ 21%). This field is read by this exact nightly pipeline's own STEP 1.1 ("a long LLM_EVAL=blocked streak → prefer no-model-call candidates") and by the TUI dashboard — a false positive is a wrong steering signal fed back into this same self-hosting loop, not merely cosmetic.

Comparable "sustained failure" signal designs (Resilience4j CircuitBreaker, Netflix Hystrix) pair a consecutive/count condition with a window-share (rate) condition for exactly this reason — a brief run of failures in an otherwise-healthy window should not read as a sustained outage. This repo's own comment already specified that two-part design; the code just never implemented the second part.

Hypothesis

Given a 14-row window where exactly the last 3 rows have evaluated: 'blocked' and the rest do not (blocked rows a strict minority), the pre-fix implementation returns blockedEvalStreak: true (false positive); the fix adds a blockedCount * 2 > recent.length strict-majority conjunct so the same input returns false, while an all-blocked window and a genuine-majority window continue to return true.

Evaluation Receipt

Real evaluator: npm test (vitest run && npm run test:governance).

Baseline (main@23a5770) Candidate
vitest 769 772 (+3 new, 0 regressions)
governance 150/150 150/150 unchanged
lint / typecheck / build clean clean

Live pre/post-fix repro against the built packages/ledger/dist/index.js: minority-tail (3/14) case flips true→false; genuine-majority (8/14) case stays true; real committed LEDGER.md stays false→false (bug is latent on main — never yet triggered there — fix forecloses the next time OPENROUTER_API_KEY is briefly absent for 3 consecutive nights).

Darwin Results

DARWIN=not-applicable — single boolean-expression fix, no evolvable population (same judgment as PRs #116/#127/#132/#137/#145 on this surface). Entrypoint probed live: npx --yes @metaharness/darwin --version responds (reachable, not blocked).

Evidence

OBSERVATION (comment vs. implementation mismatch) → MEASUREMENT (live pre-fix minority-tail repro: false positive) → OBSERVATION (Resilience4j/Hystrix prior art for the two-part design) → MEASUREMENT (769→772 vitest, 0 regressions; governance unchanged) → MEASUREMENT (live post-fix repro: minority false, majority true, real ledger unchanged) → INFERENCE (independent critic, separate context) → DECISION (ACCEPT).

Reward-Hack Check

Independent adversarial-critic subagent (separate context, own build/git stash cycle): CLEAR. Diff confined to packages/ledger/src/index.ts (+11/−1) and a pure-addition test file (+27/−0, no existing test weakened/removed, confirmed by raw diff). Independent before/after run matched 769→772 exactly. Critic's own 6 hand-written edge cases (50/50 split, 8/14 majority, majority-at-head-not-tail, below-minimum window, degenerate n=3, non-default window option) all matched expected behavior, no counter-example found. Repo-wide grep for blockedEvalStreak found only one other reference (packages/cli/src/tui.ts:206, a display-only warning string) — nothing depends on the old broken semantics.

Security Review

No credential, network, or trust-boundary surface touched. Pure local arithmetic/boolean logic over already-parsed rows. No LLM calls in this path.

Scan Findings (witness, verify)

Competitors

Resilience4j CircuitBreaker (A — official docs) and Netflix Hystrix (A — official docs) both pair a consecutive/count condition with a window-share threshold specifically to avoid this false-positive class; Kubernetes liveness probes (A) are a legitimate consecutive-only counterexample design, included to show that choice is deliberate elsewhere — just not what this repo's own comment promises. Sakana AI Scientist / OpenHands / SWE-agent / DSPy-GEPA / AutoGPT lineage (C, recalled) expose no analogous cross-run streak telemetry.

Gist

GIST=LOCAL — no gh CLI (GH_TOKEN invalid) and no gist-creation MCP tool this session; GitHub MCP access itself works fine (issue/PR tools succeed), so FALLBACK=false. Full report committed at docs/dream-cycle/2026-10-06-ledger-signals-report.md.

Witness

Computed after this issue is filed; published in the PR and the committed report.

Recommendation

evaluated: accepted — candidate fixes a reproducible false-positive in a signal this repo's own nightly loop reads for its next-night planning. Human review required before merge; no self-merge, no automerge-safe label.


🤖 Generated with Claude Code

https://claude.ai/code/session_01SqtjzJmtG789iUrkQU8bfz

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions