feat(code-intel): no completeness claim without data (PLN-2027 PR 1) - #15
Merged
Merged
Conversation
PLN-2027 unit PR 1 (PRD-739 FR1, FR3, FR6). - freshness.require_ready: throttled readiness gate for the store-backed tools. Mid-rebuild raises IndexRebuilding; an absent or empty index raises CodeIntelUnavailable. Called by analyze_changes, code_query, check_coverage and open_file_graph. - completeness.objective_for_data; CodeIntelStore.call_graph_gap. - code_changes, code_query intel selects and file-graph kinds report data_status/reason and a partial objective when their data is missing. - relations: data_status "unavailable" stamps partial; "empty" stays exhaustive. - tests/gateway/test_completeness_contract.py pins the contract per op. Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: lemoncrow <302591943+lemoncrow-agent[bot]@users.noreply.github.com>
…a literal (PLN-2027 PR 1) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…027 PR 1) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Sep 15, 2026
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.
Campaign chunk C1 of 7 (PRD-739 → PLN-2027), built one chunk at a time against
main.C1 — No completeness claim without data; no silent empty results mid-rebuild (FR1, FR3, FR6)
Lands PLN-2027 PR 1.
analyze_changes,code_query,check_coverage,open_file_graph) now callrequire_ready: mid-rebuild they raiseIndexRebuilding; on an absent or empty index they raiseCodeIntelUnavailable. Gating lives in the infra functions, solc codebehaves like MCP.code_changes,code_queryintel selects, file-graph kinds andrelationsreportdata_statusand stampobjective: "partial"when call-graph or import data is missing, instead ofexhaustive.tests/gateway/test_completeness_contract.pycovers the missing-data case for every enumerative op.Acceptance ledger
PRD-739 user-story ACs mapped to PR 1 (separate audit): 5 met by this branch, 3 already met on
main, 1 owned by a later unit (PR 2:relationspre-limit total andtruncatedsurviving packing), 0 not met.Deviations from the plan
Did: analyze_changes calls require_ready after collect_changes (the git diff) and before the first store read (src/lemoncrow/infra/code_intel/change_impact.py:502). code_query calls it after argument validation (src/lemoncrow/infra/code_intel/query.py:564). check_coverage and open_file_graph gate first.
Why: tests/infra/code_intel/test_change_impact.py::test_non_git_directory_is_an_error_not_an_empty_report pins GitUnavailable for a non-git directory, and a malformed query should surface as QueryError whatever state the index is in. Both gates still run before any store read.
Did: Added CodeIntelStore.call_graph_gap() (src/lemoncrow/infra/code_intel/store.py:570). It applies the same rule with EXISTS probes and is shared by change_impact and code_query.
Why: snapshot() runs full COUNT(*) queries across both databases on every call. EXISTS answers the same question with index seeks on the code_query hot path.
Did: code_query intel selects also report data_status unavailable and objective partial when intel.sqlite exists but holds no call edge and no reference while symbols exist. This is the change_impact rule (src/lemoncrow/infra/code_intel/query.py:591).
Why: On first connect the engine creates intel.sqlite with empty tables (engine _init_secondary_schemas), so an absent file is rarely the live missing-data state. Without the extension, code_query callers over an unbuilt call graph still returned zero rows marked exhaustive (FR3). Fail-safe side effect: a repository with genuinely no calls reads partial.
Did: The callers and callees cases use the engine's own traverse_call_graph with a neighbour lookup that returns None. usages is pinned at _maybe_attach_code_rendered with a payload that carries data_status unavailable (tests/gateway/test_completeness_contract.py:237). The engine is unchanged.
Why: engine.find_references has no unavailable state: with no reference rows it falls back to a zoekt/text search, so there is no engine missing-data case for usages to build. Adding one would mean editing the upstream-owned engine.py.
Did: Replaced two tests that pinned the opposite behaviour: tests/infra/code_intel/test_query.py test_empty_index_degrades_to_empty became test_absent_index_raises_unavailable, and tests/infra/code_intel/test_file_graph.py test_an_empty_index_analyses_cleanly became test_an_empty_index_raises_rather_than_analysing_nothing. Gave two fixtures real data with their assertions unchanged: test_coverage.py test_git_ignored_files_are_excluded_not_missing now indexes one file, and test_completeness.py test_change_impact_stamps_an_objective gains a call edge.
Why: Those fixtures were an empty index or a call graph with no edges, which now raise or read partial by design. Counterfactuals M16 and M12 show the modified tests still guard their original behaviour.
Affects: PLN-2027 PR 4
Did: Ran ruff over the make-lint paths: 10 findings, none in files this change touched, so the plan's baseline of 9 is stale. Ran mypy --strict on the 8 changed src files. Ran
pytest -m 'not slow'once in this worktree and re-ran each of its 17 failures on origin/main in a scratch worktree, which was then removed.Why: The campaign brief scopes verification: it excludes make typecheck over all of src and full main-vs-branch suite diffs, and caps any single run at 10 minutes. The capped run finished in 9:00.
Review
/code-review:deep --base origin/main(run cr-12127, 5 reviewers + 4 verifiers, all collected): NEEDS_ATTENTION — verified 4, justified 0, rejected 0, pending 0, coverage gaps 0. No BLOCKING and no correctness or security findings; 1 HIGH (design), 3 MEDIUM (code quality).Fixed
6d17c2af)18773adf)Declined
Re-review after fixes (
--since-last-review, incremental diff 04d22e7..18773ad, 46 LOC, fast path with one reviewer): APPROVED — verified 0, justified 0, rejected 0, pending 0, coverage gaps 0. Confirms the HIGH atmcp_server.py:9006is fixed.Verification
uv run pytest -q tests/infra/code_intel— pass: 395 passed (386 on base + 9 new). The first run failed 1, test_coverage.py::test_git_ignored_files_are_excluded_not_missing: its fixture was an empty index, which now raises by decision 2. Fixed by indexing one real file with the assertions unchanged.uv run pytest -q tests/gateway/test_completeness_contract.py tests/gateway/test_code_engine_cache_invalidation.py tests/gateway/test_mcp_tool_handlers.py tests/— pass: 226 passed, 2 deselectedgit grep -l --untracked -E 'require_ready|reset_readiness_probes|call_graph_gap|objective_for_data|DATA_AVAILABLE|DATA_UNAVAILABLE|analyze_changes|ChangeImpactR— pass: 19 files. conftest.py is not a test module. Excluded per the brief: tests/benchmarks/test_edit_ab_real.py and tests/benchmarks/test_shell_ab_real.py (benchmarks), and tests/gateway/test_mcp_jsonrpc_e2e.py (slow, known base failure).uv run pytest -q tests/gateway/test_cap_tools_list_gate.py tests/gateway/test_cli.py tests/gateway/test_completeness_contract.py tests/gateway/test_context_mcp_— pass: 497 passed, 5 deselecteduv run ruff check src/lemoncrow/infra/code_intel/completeness.py src/lemoncrow/infra/code_intel/store.py src/lemoncrow/infra/code_intel/freshness.py src/lemoncr— pass: All checks passed!uv run mypy --strict src/lemoncrow/infra/code_intel/completeness.py src/lemoncrow/infra/code_intel/store.py src/lemoncrow/infra/code_intel/freshness.py src/lemo— pass: Success: no issues found in 8 source files (also clean without --strict)uv run python /private/tmp/claude-501/-Users-kris-wong-Dev-lemoncrow/9d1d9d21-4fff-4e14-89f2-09de7d76a208/scratchpad/counterfactuals.py— pass: Counterfactual proof: 13 single-line mutations, with failure messages parsed per test from junit XML. 73/73 guarding test instances failed under their mutation with the guarded message. Every mutated file was restored byte-identical, and the git diff hash was unchanged.uv run pytest -q tests/gateway/test_completeness_contract.py tests/infra/code_intel/test_completeness.py— pass: 73 passed after the pre-commit hook black-reformatted both files, run against the committed bytes of 04d22e7uv run pytest -q tests/test_mypyc_compile_safety.py— pass: 3 passeduv run ruff check src benchmarks tests scripts integrations— pass: make lint equivalent: 10 findings, 0 in files this change touched (all 10 are in untouched files); the plan's stated baseline of 9 is stalegit diff --check— pass: cleanuv run pytest -q -m "not slow"— pass: Run in this worktree under the 10-minute cap: 17 failed, 6795 passed, 16 skipped, 155 deselected in 540.79s. All 17 failures reproduce on origin/main (next row), so none is attributable to this change. They are 2 cross_vendor_routing, test_code_context_index_workers, 2 docs migration, claude_paralleorigin/main (00e1b390) scratch worktree .claude/worktrees/prd-739-20260915-cb (same path length as the branch worktree, removed afterwards): uv sync --frozen; u— pass: 17 failed on origin/main, the identical set; lemoncrow imported from the scratch worktreeuv run pytest -q tests/gateway/test_completeness_contract.py tests/infra/code_intel/test_file_graph.py tests/infra/code_intel/test_completeness.py tests/infra/c— passuv run ruff check src/lemoncrow/gateway/adapters/mcp_server.py src/lemoncrow/infra/code_intel/file_graph.py src/lemoncrow/infra/code_intel/store.py— passuv run mypy --strict src/lemoncrow/gateway/adapters/mcp_server.py src/lemoncrow/infra/code_intel/file_graph.py src/lemoncrow/infra/code_intel/store.py— passuv run pytest -q tests/test_mypyc_compile_safety.py— passuv run pytest -q tests/infra/code_intel/test_intel_store.py— passgit diff --check— passStill owed by later chunks
relationspre-limit total (related_total) andtruncatedthat survives budget packing (FR2).test_tools_list_hides_internal_workflow_toolsfailure onmain.check_coveragenow raises on an absent or empty index.lc code export/import./code-review:deepwith LemonCrow reinstalled).Artifacts: PRD-739 · PLN-2027