Skip to content

Add explicit source context and retained evidence - #91

Open
jmc-wander wants to merge 2 commits into
mainfrom
codex/source-context-integration
Open

jmc-wander wants to merge 2 commits into
mainfrom
codex/source-context-integration

Conversation

@jmc-wander

@jmc-wander jmc-wander commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Add explicit, source-grounded context preparation and retained evidence spans. kin context-query and MCP query_context preserve the ordinary source result while optionally adding bounded handoff evidence. Exact source versions, project/client scope, current applicability, byte budgets and privacy refusal remain visible; no hook, scheduler or model calls are enabled automatically.

Add source snapshot/retain/inspect/project interfaces and explicit evidence-aware resume. Historical owner inspection remains distinct from current-source admission. Locator project/profile mismatches are rejected before recording evidence.

Validation

  • Current-main baseline: 3,907 passed, 3 skipped, 9 subtests.
  • Candidate source-context, privacy, scope, CLI/MCP/stdio and metadata tests: 216 passed.
  • Two new locator-binding regressions were observed failing before the repair and passing afterward.
  • Full candidate regression: 4,116 passed, 3 skipped, 9 subtests. The final CLI repair additionally passed 254 focused query/session/CLI/metadata tests.
  • Final-head CI passed on 48f557f3fed08a715a20ca6c28f1dc1123f8b858: https://github.com/wandercom/kindex/actions/runs/39520078285
  • Final independent review has no surviving findings. Human review by Jeremy McEntire remains required for the source/privacy and durable-record surfaces.
  • Independent source review identified the locator defect and missing durable query tests; both are addressed. Three other suspected issues were refuted or narrowed against source. A later CLI SQLite-error finding was repaired and covered by four observed-red/then-green regressions.

Risk and scope

Source/privacy boundaries and durable retained records receive strict validation, synthetic fixtures and independent review. Existing baseline commands retain their behavior. Local host labels are not authentication; this does not add a multi-user authorization service. Optional handoff is not a recall-improvement claim or proof of delivery/use.

Validator, implementation and test authors used the same model family with separate procedural roles. No live knowledge stores, private history, paid model calls or human beta activation were used.

Not run

Live-store or real-user beta exercises are intentionally excluded. The explicit supported interfaces were exercised with invented fixtures, including an actual MCP stdio process.

Rollback

Revert the additive feature commit. Preserve any retained session records; they are bounded selected evidence, not a full source backup. No database migration or release tag is included.

@jmc-wander jmc-wander added enhancement New feature or request work-in-progress Fixes or validation in progress labels Oct 21, 2026
@jmc-wander jmc-wander removed the work-in-progress Fixes or validation in progress label Oct 21, 2026
@jmc-wander
jmc-wander marked this pull request as ready for review October 21, 2026 00:21

@adaptcom adaptcom 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.

Confidence Score: 2/5

Summary

Score reconciled from 3/5 because the review includes a blocking finding.

Adds read-only query-context preparation and session evidence retain/inspect/project across library, CLI and MCP with strict validation. Additive, well tested (4101 passed locally); one handoff status contract defect and several CLI/MCP consistency gaps remain.

Important Files Changed

File Overview
src/kindex/query_context.py New baseline+handoff preparation; handoff status/content mismatch, strict scope filters
src/kindex/evidence.py New retain/inspect/project register on session extra via atomic_extra_update
src/kindex/cli.py New context-query and evidence commands; project resolution differs between them
src/kindex/mcp_server.py Strict query_context tool plus four evidence tools; generic error collapse
src/kindex/sessions.py format_resume_context wrapper adds optional evidence; native path unchanged
README.md Mentions query_context and new docs
docs/query-context.md Contract doc; states unknown IDs yield no contents (code differs)
docs/session-evidence.md Evidence CLI/MCP usage and format doc
docs/.well-known/mcp/server-card.json Five new tool names
docs/index.html Tool count 71 to 76 (auto-synced)
tests/test_release_metadata.py Counts _readonly_query_tool decorator; expects 76 tools
tests/query_context/init.py Package marker
tests/query_context/conftest.py Hermetic fixtures, network denied
tests/query_context/constants.py Fixed evaluation instant
tests/query_context/test_adapter_surface.py Adapter surface checks
tests/query_context/test_additive_contract.py Main contract suite; unknown-ID status left ambiguous
tests/query_context/test_cli_storage_errors.py SQLite failures become sanitized JSON exit 2
tests/query_context/test_exact_egress.py Redaction refusal preserves exact bytes
tests/query_context/test_falsy_scope.py Falsy/malformed scope tags fail closed
tests/query_context/test_public_boundary.py MCP schema, host client binding, CLI parity
tests/query_context/test_source_locator.py Foreign project/profile locators rejected
tests/query_context/test_stdio_boundary.py Real MCP stdio round trip and refusal
tests/session_evidence/conftest.py Evidence fixtures
tests/session_evidence/compact/conftest.py Projection fixtures
tests/session_evidence/compact/test_compact_projection.py Compact projection format and budgets
tests/session_evidence/test_evidence_adapters.py CLI/MCP evidence adapter parity
tests/session_evidence/test_evidence_contract.py Retention, replay, corruption, concurrency contract
tests/session_evidence/test_evidence_owner_only_amendment.py Owner-only amendment
tests/session_evidence/test_evidence_partial_register_amendment.py Partial register amendment
tests/session_evidence/test_evidence_scope_amendment.py Scope amendment

Findings

  • No CHANGELOG [Unreleased] entry for two new CLI commands, five MCP tools and two docs; every recent feature/fix PR in this repo adds one.

Reviewed 41d0bd3...48f557f · ↻ Re-run review · View in Adapt

text = candidate
else:
_add_omission(omissions, "budget", evidence_id=record["evidence_id"])
status = "unavailable" if missing else ("partial" if omissions else "prepared")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

With one unknown and one valid evidence_id, status is 'unavailable' while text/included carry the valid entry (reproduced); docs promise no contents; use 'partial' or drop entries.

Comment thread src/kindex/mcp_server.py
arguments = {key: getattr(request, key) for key in QueryContextRequest.model_fields}
return _json(prepare_query_context(store, **arguments))
except (ValueError, sqlite3.Error):
raise ValueError("Query context is unavailable") from None

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Every ValueError (missing session, client conflict, privacy refusal) collapses to 'Query context is unavailable', unlike CLI safe_error and the evidence tools; return safe_error for parity.

Comment thread src/kindex/cli.py
store = None
try:
config = _config(args)
project = normalize_project_path(str(config._project_path or os.getcwd()))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

context-query uses config._project_path (git root) while tag/evidence use cwd; from a subdirectory handoff silently returns session_scope_denied (reproduced); os.getcwd() fallback is dead.

Comment thread src/kindex/cli.py
from .sessions import normalize_project_path
store = None
# Match native CLI session start/resume ownership, including subdirectories.
project = os.getcwd()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

cmd_evidence ignores --project-path added by _common, so the flag changes store selection but not ownership scope ('Session project scope mismatch' reproduced); honor or reject it.

if node is None:
return None, "missing_source", False
extra = node.get("extra")
if (not isinstance(extra, dict) or not isinstance(extra.get("project_path"), str)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Baseline admits only sources with extra.project_path, which no ordinary add/ingest path writes; real graphs yield mostly project_scope_denied. Confirm intended or stamp scope on add.

def _client_binding(node, descriptor):
# Store's legacy tags alias uses ``domains or []``. Validate the original
# decoded field first so malformed falsy JSON cannot become unrestricted.
tags = node.get("domains")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Schema default domains '' (and NULL) is treated as malformed and denied as client_scope_denied, whereas _row_to_dict and node_scope_clients treat it as unscoped; accept falsy as [].

# Scope filtering precedes identity-bearing diagnostics, even for pins
# outside the current native query candidates or caller ID filter.
if not scope_allowed:
_add_omission(omissions, "client_scope_denied" if reason == "client_scope_denied" else reason,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Ternary is a no-op (both branches yield reason); either delete it or map to the intended generic reason.


whole = entry(0, len(body), node["content"])
whole_key = (node["id"], snapshot["source_sha256"], snapshot["metadata_sha256"], 0, len(body))
if whole_key in seen:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

whole_key can never be in seen (node ids are unique per result), and _add_omission's start/end parameters are never passed; remove dead code.

Comment thread src/kindex/cli.py
# resume, hooks and their defaults retain their existing behavior.
s = sub.add_parser("context-query", help="Prepare scoped query context with optional additive handoff")
s.add_argument("query", help="Literal query (quote multiword queries)")
s.add_argument("--source-bytes", type=int, required=True, help="Independent serialized source UTF-8 ceiling (0..16384)")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

CLI requires --source-bytes while MCP defaults retrieval_max_bytes to 8192; REVIEW.md asks for consistent defaults across surfaces.

Comment thread src/kindex/mcp_server.py

# The Field constraints are reflected in this tool's advertised schema as well
# as the shared strict semantic request model. This does not alter old tools.
from pydantic import Field as _QueryField

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Module-level pydantic import placed mid-file; move to the top-level imports.

This branch has not been deployed

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

Labels

enhancement New feature or request

Development

Successfully merging this pull request may close these issues.

2 participants