Skip to content

fix(insights): prefer typed PR/session evidence over regex correlation - #3425

Merged
Sinity merged 2 commits into
masterfrom
fix/insights/session-commit-typed-evidence
Jul 31, 2026
Merged

fix(insights): prefer typed PR/session evidence over regex correlation#3425
Sinity merged 2 commits into
masterfrom
fix/insights/session-commit-typed-evidence

Conversation

@Sinity

@Sinity Sinity commented Jul 31, 2026

Copy link
Copy Markdown
Owner

Summary

analyze correlation (the read --view correlation CLI path and
Polylogue.session_correlation_payload) regex-scans message text for
GitHub PR/issue refs and scores git commits by time-window/file-overlap
heuristics, while the archive already persists the same facts as typed
evidence with zero readers. This PR makes the correlation path consume
the typed evidence first and treats the heuristics as a fallback, per
polylogue-l9su.

Problem

Bead polylogue-l9su (P1, filed after an independent audit) found:

  • session_refs (kind=pull_request, from the Claude Code pr-link
    sidecar record) has 18,932 rows across 167 sessions — never read by
    build_correlation_result.
  • claude_bridge_session session_events (12,154 rows) carry a
    bridge_session_id (cse_<token>) — never read either.
  • This repo's own commits carry a Claude-Session: https://claude.ai/code/session_<token> trailer by convention (116
    commits in this repo's history). Verified live: the <token> is the
    same id as claude_bridge_session's bridge_session_id minus its
    cse_ prefix (cse_0182HDxDpJpsbn2qcKWK6Fsf in session_events
    matches this repo's own session_0182HDxDpJpsbn2qcKWK6Fsf trailer
    byte-for-byte). Nothing parsed it.
  • session_commits.detection_type has a live CHECK-constraint value
    origin_reported with zero rows using it.

Solution

polylogue/insights/session_commit.py:

  • detect_session_commits gains bridge_session_ids. When a commit's
    Claude-Session: trailer token matches one of the session's own
    bridge-session ids, the edge is detection_method="origin_reported",
    confidence=1.0, and supersedes file_overlap/time_window/explicit_ref
    for that commit sha. When a trailer names a different session than
    the one being scored, the heuristic edge is kept (not silently dropped)
    but flagged with disagreement_note — "derive rather than compare, fail
    loud rather than guess quietly."
  • build_correlation_result gains typed_pr_refs/typed_issue_refs
    (GitHubRef rows built from session_refs). When supplied, they are
    authoritative for that ref kind; the regex scan of message text still
    runs (it's the only source for file_paths) but is used only as a
    fallback for sessions with no typed evidence, or to detect a
    disagreement against typed evidence — never to override it silently.
  • New CorrelationDisagreement / SessionCorrelationResult.disagreements
    and GitHubRef.source (typed_session_ref vs heuristic_regex) make
    which mechanism resolved each linkage visible in the JSON/plaintext
    payload, satisfying AC3/AC4 (record which mechanism won; surface
    disagreements instead of silently preferring one).
  • New pure helpers typed_refs_from_session_refs /
    bridge_session_ids_from_events convert SessionRefRecord/
    SessionEvent rows into the typed inputs above (duck-typed, so the
    module stays DB-free and unit-testable).

Wiring (point 5 — read surface): read --view correlation and
session_correlation_payload already existed as the CLI/API surface for
this data (polylogue/cli/read_views/correlation.py,
polylogue/insights/correlation_view.py); both were updated to fetch
session_refs via repository.get_session_refs and bridge ids from the
session's own session_events, and pass them through. No new surface was
needed — the blocker was purely that the existing surface ignored typed
evidence it already had access to.

Incidental bug fix required for correctness: while adding the
trailer scan I found _parse_git_log_blocks had a latent bug — splitting
git log --format=%H%n%ai%n%s%n--- output on the literal \n---\n token
left every commit's changed-file set empty, because --name-only
appends the file list after the format text, landing it in the next
split segment past where the parser had already stopped consuming header
lines. This meant file_overlap detection never actually returned a
file-overlap edge against a real repository (only ever exercised in tests
against nonexistent paths, which never hit this code). Fixed by switching
to %x1e/%x1f ASCII field/record separators that cannot collide with
commit text.

Explicitly out of scope: session_commits (the durable batch-ingest
table in storage/sqlite/archive_tiers/write.py, excluded from this
lane's surface) is a separate mechanism populated from
session.git_commit_hash at ingest time — a narrower fact (repo HEAD at
session-capture time, not "commits this session produced") that the bead's
self-correction note already distinguishes from the on-demand correlation
path this PR fixes. It is unaffected and unchanged by this PR.

Verification

  • devtools test tests/unit/insights/test_session_commit.py tests/unit/cli/test_correlate_view.py → 45 passed. New fixtures build a
    real git repo in tmp_path (subprocess git init/commit) and cover:
    trailer match takes priority over file_overlap (origin_reported,
    confidence 1.0); a foreign trailer is flagged as a disagreement but the
    heuristic edge is kept; no bridge_session_ids falls back to plain
    heuristics unflagged; typed PR refs are preferred over regex and tagged
    source=typed_session_ref; a disagreement is recorded when regex finds
    PR numbers not in typed evidence; the typed_refs_from_session_refs /
    bridge_session_ids_from_events converters.
  • devtools verify --quick → exit 0 (ruff format/check, mypy --strict,
    render all --check, layering/closure-matrix/manifest/doc-command
    gates) — run twice, once pre-commit and once on git push via the hook.
  • mypy polylogue/insights/session_commit.py polylogue/insights/correlation_view.py polylogue/api/archive.py → no
    issues found.
  • Live read-only measurement against /realm/db/polylogue/index.db
    (sqlite3 ...?mode=ro, no writes, no daemon):
    • 167 sessions carry typed pull_request session_refs (1,690 typed
      PR-number rows total).
    • Running the old regex-only extract_github_refs() over each of
      those sessions' own block text finds 1,934 PR-number mentions: 102
      sessions match typed evidence exactly, 65 sessions would have
      surfaced extra/different PR numbers from the regex scan alone
      — this
      is exactly the silent-disagreement class the fix now makes visible
      instead of guessing.
    • Trailer side: of 9 distinct Claude-Session trailer tokens found in
      this repo's own git history, 8 resolve to a real archived session
      via claude_bridge_session (one token maps to 2 sessions → 9 sessions
      total). Once a caller threads that session's bridge_session_ids
      through, those commits get detection_method="origin_reported"
      instead of file_overlap/time_window/nothing.
    • session_commits unaffected: still 2,990 rows, all
      detection_type=explicit_ref (out of this lane's declared surface,
      see above).

Follow-ups

  • session_commits' origin_reported CHECK slot is still unused at the
    batch-ingest layer; wiring the same trailer-parse logic into
    archive_tiers/write.py (explicitly excluded from this lane) is a
    separate follow-up if the operator wants the durable table to carry
    this fact too, rather than only the on-demand correlation payload.
  • typed_issue_refs has the same priority wiring as PR refs, but no
    producer currently writes session_refs rows with kind=issue (only
    pull_request is populated today) — so the issue-ref path is exercised
    by unit tests but has no live typed data yet.

Ref polylogue-l9su

Problem: session_commit.py's on-demand `analyze correlation` path
(build_correlation_result / detect_session_commits, reachable via `read
--view correlation` and the API's session_correlation_payload) regex-scans
message text for GitHub PR/issue refs and scores commits by time-window
and file-overlap, while the archive already persists the same facts as
typed evidence: session_refs (kind=pull_request, 18,932 rows / 167
sessions) and claude_bridge_session session_events (12,154 rows), plus
this repo's own `Claude-Session:` commit trailers (verified: the trailer
token is the same base62 id as claude_bridge_session's bridge_session_id
minus its `cse_` prefix). Neither typed source had a reader; the
session_commits schema's `origin_reported` detection_type CHECK value had
zero rows. Ref polylogue-l9su.

Solution:
- session_commit.py: `detect_session_commits` gains `bridge_session_ids`;
  when a commit's Claude-Session trailer matches one of the session's own
  bridge-session ids, the edge is `detection_method="origin_reported"`,
  confidence=1.0, superseding file_overlap/time_window/explicit_ref for
  that commit. A trailer naming a *different* session is recorded on the
  edge as `disagreement_note` rather than silently accepted or dropped.
- `build_correlation_result` gains `typed_pr_refs`/`typed_issue_refs`
  (from session_refs); when supplied they are authoritative for that ref
  kind and the regex scan (still run, since it is the only file-path
  source) becomes a disagreement check, not the primary result. New
  `CorrelationDisagreement`/`SessionCorrelationResult.disagreements` and
  `GitHubRef.source` (`typed_session_ref` vs `heuristic_regex`) make which
  mechanism resolved each linkage visible on the JSON/plaintext payload.
- Wired both callers (`insights/correlation_view.py`,
  `api/archive.py::session_correlation_payload`) to fetch session_refs via
  `repository.get_session_refs` and bridge ids from the session's own
  `session_events`, via new `typed_refs_from_session_refs` /
  `bridge_session_ids_from_events` helpers.
- Fixed a latent bug in `_parse_git_log_blocks` found while adding the
  trailer scan: splitting `git log` output on a literal `\n---\n` token
  left every commit's changed-file set empty (the file list for commit N
  landed in the *next* split segment), so file_overlap detection never
  actually worked against a real repo. Switched to `%x1e`/`%x1f`
  field/record separators that cannot collide with commit text.

Verification:
- `devtools test tests/unit/insights/test_session_commit.py
  tests/unit/cli/test_correlate_view.py` -- 45 passed (new fixtures cover
  trailer-priority-over-heuristics, foreign-trailer disagreement,
  typed-refs-preferred, and the helper converters, using a real git repo
  built in tmp_path).
- `devtools verify --quick` -- exit 0 (ruff format/check, mypy --strict,
  render all --check, layering/closure/manifest/doc-command gates).
- `mypy polylogue/insights/session_commit.py
  polylogue/insights/correlation_view.py polylogue/api/archive.py` --
  no issues.
- Live read-only measurement against /realm/db/polylogue/index.db
  (sqlite3 mode=ro, no writes): 167 sessions carry typed pull_request
  session_refs (1,690 typed PR-number rows). Running the OLD regex-only
  extract_github_refs() over each of those sessions' own block text finds
  1,934 PR-number mentions: 102 sessions match typed evidence exactly, 65
  sessions would have surfaced extra/different PR numbers from the regex
  scan alone -- the disagreement class this change now makes visible
  instead of silently guessing. Trailer-side: of 9 distinct Claude-Session
  trailer tokens in this repo's own git history, 8 resolve to a real
  archived session via claude_bridge_session (one token maps to 2
  sessions, 9 total) -- those commits will get detection_method
  "origin_reported" once bridge_session_ids is threaded through for that
  session, instead of file_overlap/time_window/nothing.
- session_commits (the batch-ingest-time table, storage/sqlite/
  archive_tiers/write.py) is out of this change's declared surface and
  unaffected: still 2,990 rows, all detection_type=explicit_ref, populated
  from session.git_commit_hash at ingest time. That table is a different,
  narrower fact (HEAD at session-capture time) from the on-demand
  correlation path this PR fixes; see polylogue-l9su's self-correction
  note.

Ref polylogue-l9su

Co-Authored-By: Claude <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@Sinity, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 44 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b2520dd0-7ace-4ca0-b783-e07dee04ee4b

📥 Commits

Reviewing files that changed from the base of the PR and between 13565a3 and fa37be1.

📒 Files selected for processing (7)
  • .beads/issues.jsonl
  • docs/plans/test-clock-allowlist.yaml
  • polylogue/api/archive.py
  • polylogue/insights/correlation_view.py
  • polylogue/insights/session_commit.py
  • tests/unit/cli/test_correlate_view.py
  • tests/unit/insights/test_session_commit.py

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.

Ref polylogue-l9su.

Co-Authored-By: Claude <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 36c0caddc6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +780 to +785
number = getattr(ref, "number", None) or 0
url = getattr(ref, "url", None)
built = GitHubRef(
owner=owner_name,
repo=repo_name,
number=int(number),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve typed pull-request IDs instead of emitting PR #0

When a valid session_refs row lacks ref_number, this coerces it to zero. This occurs for Codex Cloud because chatgpt_codex_sidecar._pull_request_ref() stores external_pull_request_id (for example, "1234") in url while leaving repo and number unset. The correlation surface consequently emits a typed PR numbered 0, builds an inconsistent object ref from the raw ID, and—because typed refs are authoritative—suppresses any correctly parsed regex result. Parse the numeric/URL identifier when possible or skip rows that cannot be represented as a GitHubRef.

Useful? React with 👍 / 👎.

Comment on lines +737 to +739
typed_numbers = {ref.number for ref in resolved}
heuristic_numbers = {ref.number for ref in heuristic_refs}
extra = heuristic_numbers - typed_numbers

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Compare complete repository-qualified reference identities

For repository-qualified URLs, PR/issue identity is (owner, repo, number), not just the number. If typed evidence names acme/product#42 while message text names other/repo#42, these sets compare equal and no disagreement is surfaced, despite the two signals identifying different PRs. This silently violates the new disagreement behavior precisely when repositories reuse the same issue number; compare canonical repository-qualified identities, retaining explicit handling for genuinely bare references.

Useful? React with 👍 / 👎.

)
continue

foreign_trailer = bool(trailer_tokens) and not (trailer_tokens & own_trailer_tokens)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid declaring foreign trailers without an own bridge ID

When the current session has no claude_bridge_session evidence, own_trailer_tokens is empty, so every heuristically matched commit carrying any Claude-Session trailer is labeled as naming a different session. The inspected Claude/Codex correlation paths legitimately pass an empty list for sessions without such events, meaning the result reports a disagreement despite having no typed identity to compare. Gate the foreign-trailer classification on having at least one own trailer token.

Useful? React with 👍 / 👎.

@Sinity

Sinity commented Jul 31, 2026

Copy link
Copy Markdown
Owner Author

Triaged the 3 CodeRabbit P2 findings (PR #0 coercion at L785, cross-repo number collision at L739, foreign-trailer false-disagreement at L500) as real but non-blocking: they are edge cases of the new typed-evidence path (Codex Cloud refs lacking ref_number; repo-qualified identity comparison; sessions with no bridge evidence), not regressions in existing behavior, and the 45 new/updated tests cover the primary trailer-match/typed-ref/fallback paths. Filed polylogue-2vor with per-item acceptance criteria and merging. Ref polylogue-2vor.

@Sinity
Sinity merged commit 5525446 into master Jul 31, 2026
3 checks passed
@Sinity
Sinity deleted the fix/insights/session-commit-typed-evidence branch July 31, 2026 05:57
Sinity added a commit that referenced this pull request Jul 31, 2026
…3431)

## Summary

Closes the two named, still-open gaps from tonight's polylogue-pbuh and
polylogue-cijx.4 investigations: (1) verifies and finishes the read-side
wiring for typed session→PR evidence (pbuh AC4), and (2) wires the
`root:`
session-structure filter end-to-end across the query DSL, CLI, and
Python
API (cijx.4 AC4 / its follow-up polylogue-oqib).

## Problem

**Gap 1 (pbuh AC4).** polylogue-pbuh's parser-side fix (index v46, PR
#3390)
persists Claude Code's `pr-link` sidecar record as typed `session_refs`
evidence, but nothing on the CLI/insights/MCP surface read it — the four
dependent beads (212.2/xyel/kph/fs1.4) were blocked on an inference
mechanism (`session_commits`, 0 readers, 2,989 rows of a narrower fact)
instead of the typed evidence the provider already supplies.

**Gap 2 (cijx.4 AC4 / oqib).** `sessions.parent_session_id` and
`Session.is_root` are correct, and a plan-level `root: bool | None`
field
plus `.is_root()` builder already existed, but `root` had no
`spec_attr`,
no DSL grammar case, and no CLI flag — completely unreachable from any
query surface, so a default `find` mixes 66.1% root sessions with 33.9%
subagent/branch children unlabeled.

## Solution

**Gap 1**: A sibling lane's PR #3425
(fix/insights/session-commit-typed-evidence)
had landed the actual read-side fix — `build_correlation_result` now
consumes `session_refs`/`claude_bridge_session` typed evidence as
authoritative, falling back to regex/time-window heuristics only where
no
typed evidence exists, and surfacing disagreements. It was open but
unmerged when this pass started; triaged its 3 non-blocking CodeRabbit
P2
findings (filed as follow-up polylogue-2vor) and merged it (5525446).

Verifying the now-merged surface against the live archive
(`find id:<session> then read --view correlation`) surfaced a **second,
independent, pre-existing bug**: `_enrich_with_github_api`
(`polylogue/insights/correlation_view.py`) referenced
`SessionCorrelationResult`
at runtime while only importing it under `TYPE_CHECKING` — every call
with
the default `github_api=True` and any issue/PR ref present raised
`NameError`. This predates PR #3425 (present since `ac84f734f`); the
existing test suite only exercised `github_api=False`, so it was never
caught. Fixed by importing the class at runtime alongside the existing
`GitHubRef` import, plus a regression test.

**Gap 2**:
- `SessionQuerySpec.root: bool | None`, wired through
`build_query_spec_from_params`/`query_spec_to_plan` (new `optional_bool`
  tri-state parser in `archive/query/spec.py`).
- DSL: `root:true`/`root:false` field clause
(`archive/query/expression.py`);
`-root:` negation is rejected pointing at `root:false` instead (the
value
  already carries polarity).
- CLI: `--root/--no-root` flag, added last in `cli()`'s signature per
this
  repo's "new Click params go last" convention.
- `EXPRESSION_FIELD_REGISTRY["root"]` + regenerated
`docs/cli-reference.md`,
  `docs/search.md`.

Two deeper bugs found while making this reachable (neither was "just
unreachable" — both were silent no-ops even where a plan/spec did carry
a
value):

1. The CLI's actual browse/search path (`cli/archive_query.py`'s
`_ArchiveFilterKwargs` →
`ArchiveStore.list_summaries`/`search_summaries`/
   `count_sessions`/`count_search_sessions`/`search_session_ids`/
   `semantic_summaries`/`stats`/`stats_by`) is a SQL-level filter path
   entirely separate from the `SessionQueryPlan`/`apply_common_filters`
post-filter machinery `root`'s field descriptor
(`requires_post_filter=True`)
was designed against. None of those eight `ArchiveStore` methods
accepted
a `root` kwarg. Fixed by pushing `root` into `_session_filter_clause` as
   a direct SQL predicate (`sessions.parent_session_id IS [NOT] NULL` —
trivially SQL-pushable, unlike `continuation`/`sidechain` which derive
   from `branch_type`) and threading it through all eight methods.
2. Even the `SessionQueryPlan` post-filter path (Python API's
   `list_summaries_archive`/`list_archive`) was independently broken:
   `ArchiveSessionSummary` never carried `parent_id` (the SELECT never
projected `sessions.parent_session_id`, `_summary_from_row` never read
it), so `is_root` was `True` for every summary row regardless of actual
   parent — a `root:true` filter would have silently returned everything
   even once reachable. Fixed by adding `parent_id` to
   `ArchiveSessionSummary`, projecting the column in both `read_summary`
   and `list_summaries`, and threading it through `_summary_to_domain`.

**Default-behavior decision**: did **not** flip any surface's default.
`find`/Python API `list()`/MCP query/daemon HTTP all continue to return
every session unless `root:`/`--root`/`.is_root()` is given explicitly.
Flipping even the narrower "CLI `find` verb only" option still has real
blast radius (every existing test/saved-query/demo-script assuming
today's "everything" default needs re-auditing), and reachability is the
load-bearing wedge — `polylogue find repo:polylogue root:true` now
produces the named, non-fanout view AC4's proof text asks for. The
default question is left open as a deliberate, separately-reviewable
follow-up (narrowed onto polylogue-oqib).

## AC disposition

**polylogue-pbuh** (typed session→PR evidence): AC4 **satisfied**. Typed
session→PR linkage is reachable from `read --view correlation`
(CLI/API), verified live against the real archive. AC6 (before/after
UUID-title/PR-link census) is untouched — out of this gap's declared
scope, still open.

**polylogue-cijx.1** (repo-identity bead whose notes tracked the
session-commits/pr-link consumer question): the specific blocking
concern
its notes raised for 212.2/xyel/kph/fs1.4 ("the producer does not work"
/
"0 readers") is resolved. Its own titled AC (repo_id fragmentation) is
unrelated and was already addressed by cijx.4.

**212.2 / xyel / kph / fs1.4**: unblocked, not closed — each still needs
its own concrete deliverable (demo build, CI hook, CLI/report regen)
beyond "the data is now readable." Noted individually on each bead.

**polylogue-cijx.4** AC4: reachability **satisfied**. Default-behavior
question **deferred**, narrowed onto polylogue-oqib (priority lowered —
remaining scope is a design decision, not plumbing).

**polylogue-oqib**: reachability + both deeper bugs **satisfied and
fixed**. Default-behavior flip **not attempted**, left open and
explicitly narrowed to that one remaining decision.

## Verification

- `devtools test tests/unit/cli/test_correlate_view.py` — 4 passed
  (including new NameError regression test).
- `devtools test tests/unit/cli/test_query_expression.py
tests/unit/core/test_query_fields.py
tests/unit/cli/test_archive_query.py
  tests/unit/archive/test_archive_execution_filters.py
  tests/unit/cli/test_query_exec_laws.py tests/unit/archive/
  tests/unit/storage/test_archive_tiers_archive.py
  tests/unit/archive/query/test_discovery.py` — all green (new
`test_root_filter_partitions_top_level_and_subagent_sessions` covers all
eight `ArchiveStore` methods plus the `parent_id`/`is_root` wiring bug).
- `mypy --strict` on every touched module — no issues.
- `devtools render all --check` — sync OK.
- `devtools verify --quick` — exit 0 (also ran automatically via the
  pre-push hook).
- Manual live verification (read-only, `/realm/db/polylogue/index.db`):
  `find id:<session> then read --view correlation --format json` returns
typed `pr_refs` (`source=typed_session_ref`) plus a `disagreements`
list;
`find repo:polylogue --root` → total 1906; `find repo:polylogue
--no-root`
  → total 3206; 1906+3206=5112, the unfiltered total.

## Not done / follow-ups

- polylogue-2vor: 3 CodeRabbit P2 findings on PR #3425's typed-evidence
  path (PR #0 coercion, cross-repo number collision, foreign-trailer
  false-disagreement).
- polylogue-oqib: the default-behavior decision for `root:` (which
  surfaces, if any, should default to top-level-only).
- `root` is not wired into `query_unit_session_filters` (the `with
<units>`
  projection's separate session-filter adapter), and daemon HTTP's
  `_build_query_spec_params` named-param allowlist has no dedicated
  `?root=` query param (the existing `?query=root:true` DSL path already
covers it). `continuation`/`sidechain`/`has_branches` remain exactly as
  unreachable as before this PR.

Ref polylogue-pbuh, polylogue-cijx.1, polylogue-cijx.4, polylogue-oqib.

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sinity added a commit that referenced this pull request Jul 31, 2026
## Summary

- Extracts the hand-duplicated
`_MockServer`/`_MockHeaders`/`_make_handler` trio from three daemon test
files into a shared `tests/infra/daemon_http_harness.py`
(polylogue-myhg).
- Closes three CodeRabbit findings from PR #3425's review that were
triaged and filed as polylogue-2vor rather than blocking that merge (all
three are in `polylogue/insights/session_commit.py`, with regression
tests).

## Problem

**polylogue-myhg**: CodeRabbit flagged (PR #2559) that
`_MockServer`/`_MockHeaders`/`_make_handler` were duplicated
near-identically across `test_daemon_http_security.py`,
`test_daemon_events_endpoint.py`, and `test_provider_usage_endpoint.py`.
Drift already happened once: the `host=` parameter was added to two of
the three copies during a fast-follow, not all three. A fresh grep
tonight confirmed the duplication was still live.

**polylogue-2vor**: three P2 findings in `session_commit.py` were left
unaddressed at PR #3425's merge time:
1. `typed_refs_from_session_refs()` coerced a `session_refs` row with a
valid `url`/`repo` but no `ref_number` to "PR #0" (observed for Codex
Cloud's `chatgpt_codex_sidecar._pull_request_ref()`, which stores
`external_pull_request_id` in `url` and leaves `repo`/`number` unset).
Since typed refs are authoritative over the regex fallback, this could
suppress a correctly-parsed regex result with a bogus PR #0.
2. Disagreement detection compared PR/issue identity by bare number
only, so `acme/product#42` and `other/repo#42` compared equal — a real
cross-repo disagreement was never surfaced.
3. Foreign-trailer classification flagged a disagreement whenever a
commit carried *any* Claude-Session trailer, even when the current
session has no `bridge_session_ids` of its own — i.e. even with no typed
identity to compare against.

## Solution

**Mock harness extraction** (`tests/infra/daemon_http_harness.py`):
- `MockDaemonServer`, `MockHeaders`, `make_daemon_handler`,
`capture_json_response`, `capture_responses` — these mock only the HTTP
transport boundary (the listening socket/`ThreadingHTTPServer` a real
`DaemonAPIHandler` normally sits on, and the parsed header block a
socket read would produce). `do_GET`/`do_POST` dispatch, `_check_auth`,
cross-origin checks, route handlers, and JSON/SSE serialization all
still run as the real production `DaemonAPIHandler` built via `__new__`
— no boundary that was previously mocked is hollowed out.
- The three named test files now import the shared harness instead of
defining their own copies. `test_daemon_http_security.py` keeps a thin
file-local `_make_handler` wrapper because that file's whole point is
exercising the auth-required path, so its default server needs a
configured token (`"secret"`) unlike the harness's own open-by-default
`MockDaemonServer`.
- While touching `test_daemon_events_endpoint.py`, three ad-hoc
`type()`-built `_Srv` server stand-ins were also converted to
`MockDaemonServer` — same duplication pattern CodeRabbit flagged in the
original trio.
- Fixed two cross-file importers (`test_web_auth.py`,
`test_route_contracts.py`) that imported
`_MockServer`/`_capture_responses` *from* `test_daemon_http_security.py`
rather than defining their own copies — a case a narrow per-file
`devtools test` run doesn't catch but whole-repo `mypy --strict` does.

**session_commit.py fixes** (regression test per finding, see
`tests/unit/insights/test_session_commit.py`):
1. `typed_refs_from_session_refs()` now tries to recover a real number
by parsing a genuine `github.com` PR/issue URL out of `url` when
`number` is absent; if that also fails, the row is skipped rather than
defaulting to 0.
2. New `_refs_match()` compares the full `(owner, repo, number)`
identity whenever both refs are repo-qualified, falling back to
number-only equality when either side lacks repo identity (e.g. a bare
`#42` regex mention).
3. `foreign_trailer` now additionally requires `own_trailer_tokens` to
be non-empty.

## Verification

- `ruff check` + `mypy` (strict, whole repo: 2397 source files) clean.
- `devtools test tests/unit/daemon/test_daemon_http_security.py
tests/unit/daemon/test_daemon_events_endpoint.py
tests/unit/daemon/test_provider_usage_endpoint.py
tests/unit/daemon/test_web_auth.py
tests/unit/daemon/test_route_contracts.py` → 709 passed.
- `devtools test tests/unit/insights/test_session_commit.py` → 46
passed; also ran `tests/unit/cli/test_correlate_view.py
tests/unit/storage/test_archive_tiers_write.py` (other consumers of
`session_commit.py`) → 80 passed.
- `devtools verify --quick` → clean end to end.

Ref polylogue-myhg
Ref polylogue-2vor

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sinity added a commit that referenced this pull request Jul 31, 2026
Both closed after PR #3434 merged: shared DaemonAPIHandler mock
scaffolding extraction (tests/infra/daemon_http_harness.py) and the
three session_commit.py typed-evidence gaps from PR #3425's
CodeRabbit review.

Co-Authored-By: Claude <noreply@anthropic.com>
Sinity added a commit that referenced this pull request Jul 31, 2026
…#3438)

## Summary

Two independent "computed and discarded" defects, both verified live
tonight per the operator's audit theme:

- **polylogue-uh9l**: Claude Workflow artifact coverage was computed on
every ingest pass by `assembly_claude_code.py:discover_sidecars` and
consumed by nothing. Deleted the dead branch and wired the
genuinely-running materializer's gap count into `polylogue doctor`'s
readiness surface instead.
- **polylogue-xyel**: re-verified against current master (not assumed
from the bead's original framing) that `session_refs` now has a live
production consumer (PR #3425/#3431, already merged) — then built the
bead's own remaining, un-satisfied AC: a real D1 "receipts" demo packet.

## Problem

**uh9l**: `polylogue-z9gh.6`'s closure claimed "readiness and repair
commands no longer report healthy solely because subagents/workflows is
classified as a known sidecar." False as written — no readiness/repair
command consulted either of the two coverage computations that existed
for Claude Workflow artifacts. One
(`ClaudeOrchestrationCoverage`/`inventory_claude_orchestration_artifacts`)
was fully dead code; the other (`claude_workflow_materializer`'s gap
tuple) ran every convergence pass but was only ever logged.

**xyel**: the bead's title/framing ("SESSION_REFS HAS NO CONSUMER")
predates this repo's own same-night investigation trail
(`polylogue-cijx.1`), which found and fixed the actual gap (a NameError
that crashed `read --view correlation`'s default GitHub-enrichment path
on every real ref). The bead's own literal AC — build and register a
real PF-D1 receipts demo, not the packet-contract stub `212.7` shipped —
remained unaddressed.

## Solution

**uh9l** (3 commits):
1. `refactor(sources)`: delete the dead
`orchestration_artifacts`/`orchestration_coverage`/`orchestration_parse_gaps`
computation from `discover_sidecars` and its supporting
`ClaudeOrchestrationCoverage`/`inventory_claude_orchestration_artifacts`
(kept
`parse_claude_orchestration_artifact`/`ClaudeOrchestrationArtifact`/`ClaudeOrchestrationFact`,
still used by the materializer).
2. `feat(readiness)`: `daemon/convergence_stages.py`'s `claude_workflow`
stage now persists each materialization summary into `ops.db`'s existing
generic `daemon_stage_events` table (no schema change).
`readiness/__init__.py` registers a new
`claude_workflow_materialization` `ReadinessCheck` that `polylogue
doctor` already surfaces via `get_readiness()`.

**xyel** (1 commit + a follow-up type fix):
3. `feat(demos)`: `.agent/demos/d1-receipts/` — resolves a real merged
PR (`#3282`) to its authoring/dispatch session
structurally via `session_refs`, then checks 4 individually falsifiable
PR-body claims against that session's own recorded blocks. 3 supported,
1 explicitly scored `not_supported` (a real, structurally-confirmed gap:
a 7-file `devtools test` invocation named in the PR body only ever
appears as prose, never as an executed command in this session). Also
surfaces a genuine finding: the resolved session is a merge-conductor (0
`Edit`/`Write` tool_use blocks), not the file-editing session.
4. `fix(tests)`: type-narrow a `dict[str, object]` read in the new
integration test that `devtools verify --quick`'s `dmypy`-backed mypy
step caught (the earlier plain `mypy polylogue` spot-check doesn't cover
`tests/`).

Beads updated in a final commit: closed `polylogue-uh9l` and
`polylogue-xyel` with full AC disposition in their close reasons; filed
`polylogue-nt5f` for the one honestly-named remainder (D1's public
seed-corpus variant isn't built — `session_refs` pull_request rows are a
provider-native capability the deterministic demo seed fixture doesn't
currently populate).

## Verification

- `devtools test tests/unit/sources/test_assembly_claude_code_history.py
tests/unit/sources/test_parsers_claude_code_artifacts.py
tests/unit/storage/test_archive_readiness.py
tests/unit/daemon/test_convergence_stages.py
tests/unit/cli/test_convergence_surface_contract.py
tests/unit/cli/test_check.py
tests/integration/test_claude_workflow_admission.py
tests/unit/devtools/test_demo_packet.py
tests/unit/demo/test_tour_packet_contract.py` → 191 passed
- `devtools lab policy demo-packet-registry` → "demo packet registry:
all 4 entries conform"
- `devtools lab policy bead-graph` → exit 0 (dup_labels=0, inversions=0,
malformed_wave=0)
- `devtools verify --quick` → 20/20 steps green (pre-push hook also ran
this clean)
- `devtools render all --check` → OK, no drift

Ref polylogue-uh9l, polylogue-xyel

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added Claude Workflow readiness reporting that identifies missing
materialization records, unresolved gaps, and successful convergence.
* Added a verification demo documenting pull-request claims, supporting
evidence, counterexamples, limitations, and reproduction steps.
* **Bug Fixes**
* Improved archive readiness handling for missing, malformed, or
unavailable status data.
* Updated audit records with fixes, deferred work, and newly identified
reliability issues.
* **Refactor**
* Simplified Claude Code history processing by removing obsolete
orchestration artifact inventory and coverage reporting.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sinity added a commit that referenced this pull request Jul 31, 2026
…faces (#3442)

## Summary

Three beads about polylogue evidence that is already captured and
persisted, but unreachable from any CLI/MCP/API surface. This PR wires
the genuine remainders: `file_edits`, `session_agent_policies`, and
`sessions.display_name` (the Claude Code "slug" wire field).

## Problem

- **polylogue-nua7**: `file_edits` (76,272 live rows),
`session_agent_policies` (402,879 rows), and `session_refs` (19,024
rows) each had a complete, tested read chain that terminated at
`repository/archive/sessions.py` with nothing above it. `session_refs`
was already wired by prior PRs #3425/#3431 (`read --view correlation`);
`file_edits` and `session_agent_policies` remained unreachable from any
CLI/MCP/API surface. Four helpers (`get_file_edit`,
`sync_get_file_edits_for_session`, `sync_get_session_refs`,
`sync_session_agent_policies_batch`) had zero references anywhere
outside their own `def`/`__all__` entry.
- **polylogue-cgfy**: the Claude Code `slug` wire field (1,500 sampled
occurrences) is captured into `ParsedSession.display_name` and persisted
into `sessions.display_name`, but neither `Session` nor `SessionSummary`
carried a `display_name` field at all — the value was dropped on every
read path. This is the fix for subagent rows rendering as
`<uuid-prefix>:agent-<suffix>` instead of a human name.
- **polylogue-pbuh**: AC6 asked for a live before/after census of
UUID-titled sessions and PR-link counts. Measured read-only against
`/realm/db/polylogue/index.db`.

## Solution

1. **`polylogue/api/archive.py`**: added `Polylogue.get_file_edits()` /
`get_agent_policies()`, mirroring the existing `get_session_events()`
reader pattern (returns `None` for an unknown session, an empty list for
a session with no matching rows).
2. **MCP** (`polylogue/mcp/server_cutover.py`): wired both into the
existing `get(ref, projection=...)` dispatcher as two new projection
values (`"file-edits"`, `"agent-policies"`) alongside the existing
`"events"` projection. No new tool or tool contract needed — same
six-tool `get` operation.
3. **CLI**: added `read --view file-edits` / `read --view
agent-policies`, following the existing `events`/`hooks` view pattern —
new handler module `polylogue/cli/read_views/file_edits.py`, registered
in `read_view_handlers.py`, `read_view_registry.py`, and the profile
metadata in `archive/viewport/profiles.py`. Also required adding entries
to `polylogue/surfaces/projection_spec.py`'s
`EvidenceFamily`/`READ_VIEW_PROJECTION_FAMILIES` maps — a separate
registry from the CLI handler registry that raises "unknown projection
view" if a view is missing there.
4. **Dead code**: deleted the four zero-reference helpers named in
nua7's audit, plus their now-dangling `sqlite3` imports where nothing
else used them.
5. **`display_name` reachability** (polylogue-cgfy), two independent
hydration paths:
- Async repository path (`storage/hydrators.py`): added `display_name`
to `Session`/`SessionSummary` (`archive/session/domain_models.py`) and
their runtime mixins' `display_title` property — now falls back
`user_title > title > display_name > id[:8]`.
- Sync `ArchiveStore` summary path that backs `find`/MCP `get(ref)`
default projection (`storage/sqlite/archive_tiers/archive.py`): added
`display_name` to `ArchiveSessionSummary`, selected it in
`read_summary`'s and `list_summaries`' SQL, and made it a title fallback
tier **above** the existing structural-label fallback (polylogue-cijx.4
decision 3) when no provider title exists — `display_name` is real
origin evidence (`title_source="origin"`), stronger than a derived
structural label. Wired through
`api/archive.py::_archive_summary_to_domain`.
6. **pbuh AC6 live census** (read-only against
`/realm/db/polylogue/index.db`, no code change — measurement only):
- 16,420 Claude Code sessions total; 14,717 carry
`title_source='unknown'` (raw-id/structural-label fallback before this
PR).
- 7,088 of those 16,420 sessions have a captured `display_name`; **6,585
of the 14,717 `title_source='unknown'` sessions (44.7%) now surface a
real slug-derived title** instead of a raw id or structural label — this
PR's concrete, measured improvement.
- `session_events` with `event_type='claude_pr_link'`: 19,140 rows
(unchanged by this PR — pr-link reader wiring was already resolved by
the earlier #3425/#3431 pass; see pbuh's own notes).
- `file_edits`: 76,272 rows; `session_agent_policies`: 402,879 rows;
`session_refs`: 19,024 rows (167 distinct sessions) — all now reachable
per point 1-3 above.

## AC disposition

**polylogue-nua7**: file_edits/session_agent_policies now have real CLI
(`read --view file-edits`/`agent-policies`) and MCP
(`get(projection=...)`) consumers; session_refs already had one (prior
PRs, verified unchanged). All four zero-reference helpers deleted.
Satisfied.

**polylogue-cgfy**: AC1 (per-key classification recorded in OriginSpec)
and AC4 (re-runnable committed enumeration) were already addressed by
prior passes per the bead's own notes — not re-verified in this pass,
out of this PR's declared surface. AC2's
`structuredPatch`/`originalFile`/`oldString` persistence was already
done (index v46); this PR adds the missing *read* side (file_edits
reachability, point 1-3 above) — the specific "cijx grading rises from
observed to checkpointed" wiring is a separate insights-model change
(`insights/session_commit.py`-adjacent, in this PR's avoid list) and
remains open, noted here as a genuine remainder. AC3 (slug reaches read
surfaces) is satisfied — see point 5 above, proven with a measured live
census (point 6) and end-to-end tests. AC5 (bytes/row counts per key
acquired) is partially covered by the live counts in point 6 but not a
full per-key report.

**polylogue-pbuh**: AC6 (before/after census) satisfied as a live
measurement (point 6) — this is the "after" state; no "before" baseline
exists since the parser fix landed in an earlier PR and no un-fixed
archive is available to compare against. AC1-AC5 were already resolved
in prior passes per the bead's own extensive notes; not re-verified
here, out of this PR's declared surface (parsers/claude,
assembly_claude_code.py, providers/claude_code*.py were avoided per the
task's own instructions).

## Verification

- `devtools test tests/unit/mcp/test_server_surfaces.py` — 11 passed,
including 3 new tests exercising `get(projection="file-edits")`,
`get(projection="agent-policies")`, and the default `get(ref)`
display_name fallback through the real MCP `tool_manager` entrypoint
against a real `ArchiveStore`-written session.
- `devtools test
tests/unit/cli/test_file_edits_and_agent_policies_views.py` — 2 passed,
full `CliRunner` invocation of `read --view file-edits`/`agent-policies`
against a real `ArchiveStore`-written session.
- `devtools test
tests/unit/storage/test_session_display_name_reaches_repository.py` — 2
passed, proving both `Session` and `SessionSummary` carry
`display_name`/`display_title` correctly through the real writer → async
repository chain, and that a real title still wins over the slug.
- `devtools test tests/unit/storage/test_unread_wire_batch_v46.py
tests/unit/storage/test_repository_agent_policies.py` — 13 passed (no
regression from dead-helper removal).
- `devtools test tests/unit/storage/test_title_source_queryable.py
tests/unit/storage/test_archive_tiers_write.py
tests/unit/storage/test_archive_tiers_archive.py
tests/unit/cli/test_query_exec_laws.py` — 243 passed (no regressions to
existing title/summary logic).
- `devtools test tests/unit/api/test_facade_contracts.py -k
"no_undiscovered or file_edits or agent_polic"` — 3 passed.
- Broader affected-area sweep: 28 test files that directly import the
touched domain modules
(`tests/unit/{api,archive,cli,core,insights,sources,storage,surfaces}/...`)
— 863 passed, 2 pre-existing unrelated failures
(`test_archive_tiers_api_raw_artifacts_read_source_tier`, 4 parametrized
cases of `test_filters_props.py::TestFilterDateParsing`) confirmed to
reproduce identically with this branch's changes reverted — stale
hardcoded date assertions / a clock-hygiene issue in
`archive/filter/filters.py`, untouched by this PR.
- `mypy --strict` on every touched file — no issues.
- `devtools verify --quick` — exit 0.
- `devtools render all --check` — all surfaces sync OK (regenerated
`docs/cli-reference.md`, `docs/plans/topology-target.yaml` for the new
`polylogue/cli/read_views/file_edits.py` module).

Not run: full `devtools verify --all` / whole-directory `tests/unit`
sweep (anti-pattern per repo convention — testmon wasn't seeded in this
worktree; relying on the targeted + affected-area sweeps above plus CI's
post-merge heavy `test` suite).

Ref polylogue-nua7, polylogue-cgfy, polylogue-pbuh
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