Repository navigation
Add explicit source context and retained evidence - #91
jmc-wander wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
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") |
There was a problem hiding this comment.
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.
| 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 |
There was a problem hiding this comment.
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.
| store = None | ||
| try: | ||
| config = _config(args) | ||
| project = normalize_project_path(str(config._project_path or os.getcwd())) |
There was a problem hiding this comment.
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.
| from .sessions import normalize_project_path | ||
| store = None | ||
| # Match native CLI session start/resume ownership, including subdirectories. | ||
| project = os.getcwd() |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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") |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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.
| # 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)") |
There was a problem hiding this comment.
CLI requires --source-bytes while MCP defaults retrieval_max_bytes to 8192; REVIEW.md asks for consistent defaults across surfaces.
|
|
||
| # 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 |
There was a problem hiding this comment.
Module-level pydantic import placed mid-file; move to the top-level imports.
Summary
Add explicit, source-grounded context preparation and retained evidence spans.
kin context-queryand MCPquery_contextpreserve 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
48f557f3fed08a715a20ca6c28f1dc1123f8b858: https://github.com/wandercom/kindex/actions/runs/39520078285Risk 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.