Repository navigation
feat(evidence): discover missing Evidence links without transferring records - #8633
Merged
Merged
Conversation
…records An agent could only learn that a retained record's source was absent by reading complete records or by having a downstream analysis refuse. The summary said nothing about links, and complete-record retention was easy to mistake for source closure. - Add detail: links to get_evidence_bundle and CLI inspect-evidence-bundle: paged link rows with retained or missing status and no record payloads, for selected records against the whole session or file. - Add retained-closure traversal, the walk composed workflows require, reporting complete or incomplete. Direct reads make no closure claim. - Count direct and missing direct links in the summary. Fixes #8454 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
The Evidence schema does not deduplicate evidence_links, so the summary counted a repeated link twice while link rows and counts reported it once. Both now read one helper for a record's distinct direct links. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
[Medium impact] No blocking issues were found; the PR appears safe to merge. SummaryAdds
Diagram%%{init: {'theme': 'neutral'}}%%
flowchart TD
MCP["MCP session snapshot"] --> Owner["inspectEvidenceBundle"]
CLI["CLI authenticated bundle file"] --> Owner
Owner --> Select["Select starting records"]
Select --> Direct["Read direct links"]
Select --> Closure["Follow retained links when requested"]
Direct --> Membership["Check against every retained record"]
Closure --> Membership
Membership --> Counts["Count all inspected links"]
Counts --> Page["Filter and page link rows"]
Page --> Result["Return IDs and membership without record payloads"]
Reviews (3) · Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." · Reviewed by Greptile |
The test never called tools/list, so neither the shared validator nor the MCP client checked its replies against what each profile advertises. A schema that rejected the links result could have passed. Fetch the advertised definition and validate every request and reply against it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #8454 (proposal: #8454 (comment)).
Problem and expected behavior
From the issue: a bundle holding a dossier without its linked source imported cleanly, and the summary showed one
complete-record. Onlybuild_call_pathrevealed the gap, by refusing.detail: "records"reportsmissing_evidence_linksonly after transferring the complete records it pages over.Expected: an agent asks which links are absent before attempting an analysis, without confusing record retention, link membership and analysis coverage, and without filters making an unselected source look absent.
Change and scope
inspectEvidenceLinksinsrc/domain/evidenceRecordSummary.ts: the existing filter owner selects the starting records. Membership is checked against every record in the session or file.traversal: "direct"(default) reads the selected records' own links and setsretained_closuretonull.traversal: "retained-closure"also follows retained links, the same walkresolveInvestigationEvidenceClosureperforms, and reportscompleteorincomplete. An empty selection also reportsnull.{evidence_id, linked_evidence_id, status}in canonical order. They default tolink_status: "missing"and page withoffsetandlimit. Thelinkscounts cover every inspected link regardless of that selection.direct_linksandmissing_direct_links, the distinct direct links of the matching records.inspectEvidenceBundle. A delivery-specific field outside its delivery is refused rather than ignored, and now names its detail. An oversized link page names a smaller selection.Follow the contribution scope. This reads existing Evidence within existing boundaries, so no maintainer scope decision is required. The API shape was posted on the issue before implementation; there has been no maintainer response yet.
Contract and boundary impact
evidenceRecordSummary.tsowns filters, the summary and now links. Downstream refusal stays withresolveInvestigationEvidenceClosure; both readevidence_linksas recorded.detailvalue and two links-only inputs; a new member in theget_evidence_bundleoutput union; two new required summary fields.missingmeans absent from this session or file, not nonexistent.docs/public/product-catalog.json), package, or installation impact: catalog only.Expected effects (check known effects; use documentation-only on its own).
Unchecked items do not establish absence; leave uncertainties visible.
Evidence and regression coverage
Before and after, executed. A real Kuna 1.988 session (stdio MCP, compact profile, macOS arm64, Homebrew's unexecuted
tmux, SHA-2568ab1bd9e…a86d, unchanged) rananalyze_functionandtrace_call_path, then exported a real bundle of 4 records. I removed the linkedanalyze_functionrecord from the file and imported it into a fresh target-free server. Bytes are the SDK-serialized reply; tokens aretokenxestimates, not billing. This compares whole-session discovery; a caller who already knows the exact record can filterrecordsto fewer bytes.detail: "summary"detail: "records", whole sessiondetail: "links"traversal: "retained-closure"on that recordincomplete, 1 of 1 links missingcomplete, 0 missing, same Evidence IDThe reported ID was the removed source, and CLI
inspect-evidence-bundleon the partial file returned the same rows as MCP.Scale, source-level. A synthetic linked chain, median of 7 in-process runs, measured as a function call rather than over MCP:
Schema cost, one catalog per session: compact 580,675 to 581,073 B (+398 B, about +106 estimated tokens); full 3,958,851 to 3,961,803 B (+2,952 B, about +870 estimated tokens).
tests/boundary/mcp/evidenceLinkInspectionMcp.test.ts(public MCP, full and compact, with every request and reply validated against the definition each profile advertises; removing the links member from the advertised union makes it fail): unselected sources stayretained, no payload in rows, direct versus closure, a record with no links, a repeated link counted once in every delivery, an empty selection, stable paging without skips or duplicates, offset past the end,build_call_pathrefusing the reported ID and proceeding after import with the original record unchanged, misplaced fields, and overflow naming a smaller page.tests/boundary/cli/analysisViewCli.test.tscovers a partial portable bundle through the shared owner. Four existing tests (three acceptance, one boundary) narrow the wider output union throughevidenceBundleSchema, and one verifier fixture gains the new counters.{"kind":"evidence-link-inspection","traversal":"direct","link_status":"missing","total_retained_records":3,"matching_records":3,"examined_records":3,"links":{"total":1,"retained":0,"missing":1},"retained_closure":null,"matching_links":1,"next_offset":null,"rows":[{"evidence_id":"ev_20c14b56…","linked_evidence_id":"ev_25f85fc5…","status":"missing"}]}build_call_pathagreement uses the repository's captured dossier example. The scale runs are one host and a synthetic chain. No autonomous-agent study.Applicable boundary regressions: identity preservation, page boundaries, omission versus absence. No lifecycle stage changes.
For evidence-bearing changes:
Validation performed
npm run check:fast— passednpm run format:check— passednpm run knip— passednpm run docs:check— passednpm run metadata:check— passednpm run test:focused -- tests/boundary/mcp/evidenceLinkInspectionMcp.test.ts tests/boundary/mcp/evidenceRecordSelectionMcp.test.ts tests/boundary/cli/analysisViewCli.test.ts tests/boundary/mcp/analysisViewMcp.test.ts tests/boundary/mcp/largeToolResult.test.ts tests/boundary/mcp/fridaMcp.test.ts tests/boundary/mcp/workflowSnapshotReplayMcp.test.ts tests/boundary/process/mcpVerifierResults.test.ts— 8 files, 111 tests passednpm run test:focused -- tests/boundary/mcp/evidenceLinkInspectionMcp.test.ts tests/boundary/mcp/evidenceRecordSelectionMcp.test.ts— 2 files, 13 tests passed. The real workflow and scale runs predate that small counting change; their records had no repeated links.mainat93b8b82db, which includes feat(native): deliver batch decompile as a retained summary #8631's changes to shared delivery code:npm run check:fast,format:check,knip,docs:checkandmetadata:checkpassed, andnpm run test:focused -- tests/boundary/mcp/evidenceLinkInspectionMcp.test.ts tests/boundary/mcp/evidenceRecordSelectionMcp.test.ts tests/boundary/mcp/batchDecompileViewsMcp.test.ts tests/boundary/mcp/largeToolResult.test.ts tests/boundary/cli/analysisViewCli.test.ts tests/boundary/process/mcpVerifierResults.test.tspassed (6 files, 80 tests).mainat2ac6d3efe,npx tsc --noEmitandnpm run test:focused -- tests/boundary/mcp/evidenceLinkInspectionMcp.test.ts tests/boundary/mcp/evidenceRecordSelectionMcp.test.tspassed.mainataba56df68,npx tsc --noEmitandnpm run test:focused -- tests/boundary/mcp/evidenceLinkInspectionMcp.test.ts tests/boundary/mcp/evidenceRecordSelectionMcp.test.ts tests/boundary/cli/analysisViewCli.test.tspassed.mainat0e6943c9d,npx tsc --noEmitand the same 2 focused files passed (13 tests). Every required CI check had passed on6dc651e88before this merge.mainatf95bdbce7,npx tsc --noEmitandnpm run test:focused -- tests/boundary/mcp/evidenceLinkInspectionMcp.test.ts tests/boundary/mcp/evidenceRecordSelectionMcp.test.ts— 2 files, 13 tests passed. Every required CI check had passed on9a62a45c3before this merge.npm run test:focused -- tests/acceptance/applications/bundleResourceRootCliMcp.test.ts tests/acceptance/tools/inspectorFileUrlIdentity.test.ts tests/acceptance/tools/reconstructionScopedUnknowns.test.ts— 3 files, 5 tests passedCompatibility, safety, and release
Author readiness
Human owner: @kaoru0822-kitauji
Before merge
type(scope): outcome.🤖 Generated with Claude Code