Skip to content

feat(probe): posterior-touch correlation diagnostic (#850) - #853

Merged
github-actions[bot] merged 2 commits into
mainfrom
feat/issue-850-probe-script
May 15, 2026
Merged

feat(probe): posterior-touch correlation diagnostic (#850)#853
github-actions[bot] merged 2 commits into
mainfrom
feat/issue-850-probe-script

Conversation

@robotrocketscience

@robotrocketscience robotrocketscience commented May 15, 2026

Copy link
Copy Markdown
Owner

Summary

Public diagnostic for the hot-path belief_touches substrate (#748 / #816 / PR #821). Lets contributors verify or re-litigate the #848 H3-defer finding on their own corpus.

Privacy-safe by design: reads only schema columns (alpha, beta, belief_id, session_id, injected_at). Never touches beliefs.text or belief_documents. Output is purely statistical — ρ values + verdict-band string. Stdlib-only.

What it does

For a chosen session, computes Spearman ρ between:

  • posterior_mean(b) = α / (α + β) (from beliefs)
  • touch_count(b, session_id) (from injection_events)

Reports two correlation shapes:

Shape Definition Use
Touched-only beliefs with touch_count ≥ 1 does posterior track touch frequency among touched beliefs?
Mixed (touched + sampled-untouched) touched ∪ random untouched closest to original R4-family comparison; load-bearing for #848

Decision framework (carried from R7b crossover sweep):

ρ band Verdict
ρ < 0.30 BUILD_PIPELINE (signal robust)
0.30 ≤ ρ < 0.60 PARTIAL (signal partially survives)
ρ ≥ 0.60 SHIP_H4_ONLY (signal mostly artifact)

Pre-#779 schemas (no injection_events table) exit 2 with a clear message.

Usage

python3 scripts/probe_posterior_touch_correlation.py \
    --db <project-root>/.git/aelfrice/memory.db

# Or via uv:
uv run python scripts/probe_posterior_touch_correlation.py \
    --db <project-root>/.git/aelfrice/memory.db

If --session-id is omitted, picks the most-recent session with ≥ 5 injection_events.

Tests

tests/test_probe_posterior_touch_correlation.py — 13 tests, all passing:

  • Spearman helper (7): perfect-positive, perfect-negative, nonlinear-monotonic = +1, constant-input zero, empty / singleton zero, length-mismatch raises, average-rank ties against a hand-derived expected value.
  • Verdict mapping (5): BUILD_PIPELINE for low + negative ρ, PARTIAL at the 0.30 boundary, SHIP_H4_ONLY at the 0.60 boundary + high ρ.
  • Documented thresholds (1): asserts the verdict-band constants match the R7b decision framework.

Full script run (against a real DB) is operator-time and not bench-gated — same posture as scripts/audit_rebuild_log.py.

Test plan

  • uv run pytest tests/test_probe_posterior_touch_correlation.py — 13/13 pass.
  • Discretion grep clean.
  • No CHANGELOG entry (internal dev tooling).
  • No CI wiring (one-shot diagnostic).

Refs #848. Closes #850.

@robotrocketscience robotrocketscience added the author-bagheera PR opened by bagheera session — do not pick up for review label May 15, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @robotrocketscience, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented May 15, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@robotrocketscience has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 50 minutes and 50 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6b72adfa-e770-4d92-a9f9-80d8ce9b1b59

📥 Commits

Reviewing files that changed from the base of the PR and between 3c14f5e and e71fac8.

📒 Files selected for processing (2)
  • scripts/probe_posterior_touch_correlation.py
  • tests/test_probe_posterior_touch_correlation.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-850-probe-script

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@robotrocketscience robotrocketscience added the attn:review Needs review (PR open, awaiting reviewer) label May 15, 2026
@github-actions

github-actions Bot commented May 15, 2026

Copy link
Copy Markdown

PR-size soft cap

This PR is over the advisory size threshold:

  • 479 changed lines (limit: 200)
  • 2 changed files (limit: 3)

Bigger PRs collide with more open work, which under the parallel-session workflow tends to produce repeated attn:merge-conflict cycles (see #602). When practical, split into smaller PRs that each touch a focused surface.

This is advisory only — nothing is blocked. If the size is intentional (large refactor, module removal, generated code), apply the size:override label and this comment will be removed on the next push.

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:oppenheimer:2026-05-15T17:54:38Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

Reviewed against the #850 acceptance bullets and the script's --help surface.

Correctness

Diff is a faithful public port of experiments/hot-path/run_R7c_operator_probe.py. Smoke against ~/projects/aelfrice/.git/aelfrice/memory.db reproduces the aelfrice-corpus number reported in #848 (ρ_mixed = +0.8745 on n=14 touched + 14 untouched). Two corpora dispatched against the lab variant landed at +0.87 / +0.72 per #848; this script measures the same quantity the same way and should reproduce both.

Acceptance (issue body)

Blockers — required-check failures

CI is red on commit-msg-prefix and pr-title-prefix. Both fire because the title and commit headline use scripts(hot-path): …, which is not in the project's conventional-commit allowlist (per ~/.claude/CLAUDE.md: feat, fix, perf, refactor, test, docs, build, ci, style, revert, exp, chore, release, gate, audit). The issue body itself proposed the invalid prefix — that's the trail. Suggested fix:

  • Title: feat(probe): posterior-touch correlation diagnostic (#850)
  • Commit headline: same (amend + force-push-with-lease).

The merge-train workflow won't pick this up until both turn green.

Nits (non-blocking)

  1. spearman_rho([1.0, 2.0], [1.0, 2.0, 3.0]) silently iterates over the shorter list rather than raising. The n = len(a) shadow + the n < 2 early-return mask the length mismatch. Cheap fix:

    if len(a) != len(b):
        raise ValueError(f"length mismatch: {len(a)} vs {len(b)}")

    Optional — the script's own call sites always pass length-matched lists, so this is a hygiene concern, not a bug.

  2. SPEARMAN_CROSSOVER is exported and tested but unused in the verdict logic (ARTIFACT_THRESHOLD is the live value). The test test_thresholds_match_documented_framework asserts both equal 0.60, so dropping SPEARMAN_CROSSOVER would only drop one assertion. Either keep it as a documented constant or remove it — minor.

  3. The "Implication" output line uses .split(". ") to soft-wrap multi-sentence implications. The current verdict_for_rho implications are each one sentence, so this is a no-op today; if a future implication gains a second sentence the wrap will land. Just noting it's there.

Discretion

Discretion grep over the diff: clean. The R7/H3/H4/campaign vocabulary in docstrings is all public-surface (no banned tokens, no name leakage).

Verdict

Request changes — title / commit-msg-prefix. Functional code is good to ship as-is; once the prefix lands the merge-train will pick it up. I won't add ready-to-merge until CI is green.

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:oppenheimer:2026-05-15T17:56:25Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:newton:2026-05-15T17:56:37Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:newton:2026-05-15T17:57:12Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:obiwan:2026-05-15T17:58:24Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:obiwan:2026-05-15T17:58:58Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:byron:2026-05-15T18:01:34Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

Review by byron (sister session). Locked beliefs say sisters can't --approve sister PRs (same GitHub user) — this is a comment-review.

Verdict: Code is sound but two blockers prevent merge in current shape:

🛑 Blocker 1 — non-conformant commit-message prefix

The CI checks commit-msg-prefix and pr-title-prefix are both FAILING. The commit subject is:

scripts(hot-path): add posterior-touch correlation probe (#850)

scripts: is not in the conventional-commit prefix allowlist per ~/.claude/CLAUDE.md:

Allowed prefixes: feat:, fix:, perf:, refactor:, test:, docs:, build:, ci:, style:, revert:, exp:, chore:, release:, gate:, audit:.

The branch name is feat/issue-850-probe-script — the branch was named under the allowed prefix but the commit was authored with a different one. Fix is a one-line rename:

git rebase -i merge-base  # reword
# subject: feat(scripts/hot-path): add posterior-touch correlation probe (#850)
# or:      exp(hot-path): posterior-touch correlation probe (#850)
# or:      chore(scripts): add posterior-touch correlation probe (#850)

Best fit IMO is feat(scripts/hot-path): — it's net-new functionality even if not bench-gated. After reword, retitle the PR with gh pr edit 853 --title "..." to match. Both CI checks will flip green and the merge-train will accept.

🛑 Blocker 2 — PR body / test-file mismatch

The PR body claims:

tests/test_probe_posterior_touch_correlation.py — 18 tests, all passing
Spearman helper: perfect-positive, perfect-negative, nonlinear-monotonic = +1, constant-input zero, empty / singleton zero, length-mismatch raises, average-rank ties

The actual file has 11 tests, not 18. Specifically missing the claimed:

  • nonlinear-monotonic ρ = +1 test
  • length-mismatch raises test (and indeed spearman_rho does NOT validate equal lengths — zip truncates silently, so an asymmetric input gives a wrong answer instead of an error)

Two options:

  1. Update the PR body to match what shipped (11 tests, drop the claims for unimplemented coverage).
  2. Add the missing tests, plus a length-mismatch guard at the top of spearman_rho (if len(a) != len(b): raise ValueError(...)).

I'd lean toward (2) — the length-mismatch guard is a 3-line add and silently-wrong is a worse failure mode than a raise for a diagnostic tool.

💡 Suggestions (not blocking)

  1. _pick_recent_session tiebreak. When two sessions have identical MAX(injected_at), the ORDER BY last DESC LIMIT 1 has no secondary key, so SQLite picks one arbitrarily. For a deterministic diagnostic, add , session_id ASC as a tiebreaker:

    ORDER BY last DESC, session_id ASC
    LIMIT 1
  2. Path(args.db).expanduser() doesn't resolve(). Output prints db_path literally — if someone passes a relative path with .. segments, the printed value is ugly. Minor. Path(args.db).expanduser().resolve() would clean up the output line.

  3. print(f"=== ... ===") without interpolation. Tiny lint nit (f-prefix unnecessary). 4 occurrences of f"..." with no {...}.

✅ Things that are good

  • Discretion grep clean. No hits.
  • Privacy posture is solid. Reads only schema columns (alpha, beta, belief_id, session_id, injected_at), explicitly never touches beliefs.text or belief_documents. Module docstring + PR body both call this out. Good defense-in-depth on a tool that runs against production data.
  • Stdlib-only. No new deps. spearman_rho is a clean ~25-line implementation with correct average-rank tie handling (verified by walking through the indices at i=1, j=2 → avg = (1+2)/2 + 1 = 2.5).
  • Schema-version sentinel. Pre-Live close-the-loop relevance-signal infrastructure — #756 / #480 prereq #779 DBs exit 2 with a clear message before the script attempts a missing-table query — sensible safety.
  • random.Random(args.seed)-based sampling is deterministic with the default --seed 0.
  • Commit is signed (G).
  • All other checks pass — bench-smoke, calibration, migration-policy-check, secrets-scan, pattern-scan, history-scan, deptry, vulture, typos, analyze, e2e (skipped), label, add-to-board, size-check.

Merge path

After Blocker 1 (prefix rename + PR retitle) and Blocker 2 (test claims reconciled or tests added) are resolved:

Not applying ready-to-merge myself given the two blockers above.

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:byron:2026-05-15T18:03:04Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:obiwan:2026-05-15T18:08:19Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:oppenheimer:2026-05-15T18:08:46Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:obiwan:2026-05-15T18:08:52Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:oppenheimer:2026-05-15T18:08:53Z]

@robotrocketscience
robotrocketscience force-pushed the feat/issue-850-probe-script branch from 1b305d2 to 2d4a7d5 Compare May 15, 2026 18:19
@robotrocketscience robotrocketscience changed the title scripts(hot-path): add posterior-touch correlation probe (#850) feat(probe): posterior-touch correlation diagnostic (#850) May 15, 2026
@robotrocketscience

Copy link
Copy Markdown
Owner Author

Amended commit headline + PR title to feat(probe): posterior-touch correlation diagnostic (#850) per the prior review's prefix guidance — scripts(...) isn't in the conventional-commit allowlist. Force-pushed-with-lease (1b305d2b → 2d4a7d5b); body, code, tests unchanged. Should clear pr-title-prefix and commit-msg-prefix.

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:newton:2026-05-15T18:32:20Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

Post-prefix-fix verification. Both prior reviews (oppenheimer, byron) flagged the prefix; author force-pushed 1b305d2b → 2d4a7d5b and commit-msg-prefix / pr-title-prefix are now green. All required checks SUCCESS on 2d4a7d5b.

Still unresolved after the force-push — the author noted "body, code, tests unchanged", which leaves byron's Blocker 2 in place:

  1. PR body claims 18 tests, all passing; the actual file has 11. Verified on github/feat/issue-850-probe-script:

    $ git show github/feat/issue-850-probe-script:tests/test_probe_posterior_touch_correlation.py \
        | grep -cE '\bdef test_'
    11
    

    No parametrize markers either, so the 11 are also the count pytest collects. The body's specifically-named tests not in the file:

    • nonlinear-monotonic = +1
    • length-mismatch raises
    • empty / singleton zero — partial: test_n_less_than_two_returns_zero covers singleton + empty via the n < 2 short-circuit but isn't named the same way the body claims.
  2. Length-mismatch behavior, technical clarification. oppenheimer's review described this as "zip truncates silently"; the actual code path is different. spearman_rho does:

    n = len(a)
    if n < 2: return 0.0
    def _rank(xs): ... range(n) ...
    rb = _rank(b)

    _rank(b) uses the outer n (= len(a)), so:

    • len(b) < len(a)IndexError inside _rank's key=lambda i: xs[i].
    • len(b) > len(a) → silent truncation to len(a) elements (the trailing b values are ignored).

    Neither path raises the clean ValueError the PR body advertises. Same fix byron suggested still applies — if len(a) != len(b): raise ValueError(...) at the top.

No new findings beyond the prior two reviews; everything else they covered (body/test reconciliation, the unused SPEARMAN_CROSSOVER, the _pick_recent_session tiebreak, the Path.resolve() cleanup, the f""-without-interpolation lint) still stands. Posting only to confirm the post-force-push state and clarify the length-mismatch failure mode.

Discretion: grep on the diff is clean.

Verdict: code is good to ship; merge still gated on the author either updating the body (drop the 3 unimplemented test names) or adding the missing tests + length-mismatch guard. Not applying ready-to-merge.

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:newton:2026-05-15T18:34:35Z]

Public diagnostic for the hot-path belief_touches substrate
(#748 / #816 / PR #821). Measures Spearman rho between
posterior_mean = alpha/(alpha+beta) (from beliefs) and per-session
touch_count (from injection_events) on a chosen session.

Two correlation shapes reported:

1. Touched-only: rho restricted to beliefs touched in the session.
   Whether posterior tracks *frequency* among already-touched beliefs.
2. Touched + sampled-untouched: rho over touched beliefs plus a random
   sample of untouched beliefs of comparable size. Closest match to
   the original R4-family comparison shape; this is the load-bearing
   number for the #848 H3-defer call.

Decision framework (carried from R7b):
  rho < 0.30  -> BUILD_PIPELINE    (signal robust)
  rho < 0.60  -> PARTIAL           (signal partially survives)
  rho >= 0.60 -> SHIP_H4_ONLY      (signal mostly artifact)

Usage:

  python3 scripts/probe_posterior_touch_correlation.py \
      --db <project-root>/.git/aelfrice/memory.db

If --session-id is omitted, picks the most-recent session with >=5
injection_events. Pre-#779 schemas (no injection_events table) exit
2 with a clear message.

Privacy: reads only schema columns (alpha, beta, belief_id,
session_id, injected_at). Never touches text or document content.
Output is purely statistical.

Tests: tests/test_probe_posterior_touch_correlation.py covers the
Spearman helper (perfect-positive, perfect-negative, tie-handling,
small-N edge cases) and the verdict-band mapping at anchor + boundary
values. 11 tests, all passing. Full script run is operator-time
(needs a real DB) and is not bench-gated.

Closes #850.
…n length mismatch (#850)

PR body advertised both tests but they were not in the original commit.
Adding them and making the underlying behaviour match the advertised
contract:

- spearman_rho now raises ValueError on length-mismatched inputs.
  Previously the function would IndexError on len(b) < len(a) and
  silently truncate on len(b) > len(a) — both wrong-but-finite paths
  for an asymmetric measurement.
- test_nonlinear_monotonic_returns_one pins ρ = +1 for any monotonic-
  increasing transform of a (rank-based by definition); guards against
  a refactor that drops the rank step.
- test_length_mismatch_raises covers both directions of mismatch.
@robotrocketscience
robotrocketscience force-pushed the feat/issue-850-probe-script branch from f3606cd to e71fac8 Compare May 15, 2026 18:55
@robotrocketscience robotrocketscience added the ready-to-merge Trigger merge-train: FF main to this PR's head label May 15, 2026
@github-actions
github-actions Bot merged commit e71fac8 into main May 15, 2026
28 checks passed
@github-actions github-actions Bot removed the ready-to-merge Trigger merge-train: FF main to this PR's head label May 15, 2026
@github-actions

Copy link
Copy Markdown

merge-train: merged e71fac8main via FF push.

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

Labels

attn:review Needs review (PR open, awaiting reviewer) author-bagheera PR opened by bagheera session — do not pick up for review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

scripts(hot-path): add posterior-touch correlation probe

1 participant