diffcontext 0.5.3__tar.gz → 0.5.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {diffcontext-0.5.3 → diffcontext-0.5.4}/PKG-INFO +1 -1
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/__init__.py +1 -1
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/cli/__init__.py +5 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/mcp_server.py +4 -2
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext.egg-info/PKG-INFO +1 -1
- {diffcontext-0.5.3 → diffcontext-0.5.4}/tests/test_core.py +59 -34
- {diffcontext-0.5.3 → diffcontext-0.5.4}/LICENSE +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/README.md +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/_warn_once.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/cache.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/context/__init__.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/context/compiler.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/context/selector.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/diff/__init__.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/diff/git_diff.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/diff/state_manager.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/graph_builder.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/history.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/impact/__init__.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/impact/blast_radius.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/impact/scoring.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/impact/traversal.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/impact/visualizer.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/languages/__init__.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/languages/typescript.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/lexical.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/models.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/parser.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/pipeline.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/py.typed +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/rerank/__init__.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/rerank/features.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/rerank/model.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/resolver.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/scanner.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/symbols.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/verify/__init__.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/verify/cases.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/verify/history.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext/verify/sufficiency.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext.egg-info/SOURCES.txt +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext.egg-info/dependency_links.txt +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext.egg-info/entry_points.txt +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext.egg-info/requires.txt +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/diffcontext.egg-info/top_level.txt +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/pyproject.toml +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/setup.cfg +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/tests/test_cache.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/tests/test_compiler_meta.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/tests/test_cutoff.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/tests/test_determinism.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/tests/test_downstream_eval.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/tests/test_git_diff.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/tests/test_harness_api.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/tests/test_history.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/tests/test_incremental.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/tests/test_index_scoping.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/tests/test_lexical.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/tests/test_mcp_server.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/tests/test_rerank.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/tests/test_semantic_ablation.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/tests/test_semantic_audit.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/tests/test_semantic_gap.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/tests/test_semantic_metrics.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/tests/test_semantic_mine.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/tests/test_service_clone.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/tests/test_src_layout.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/tests/test_stats.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/tests/test_token_budget.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/tests/test_typescript_adapter.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/tests/test_verify.py +0 -0
- {diffcontext-0.5.3 → diffcontext-0.5.4}/tests/test_version.py +0 -0
|
@@ -32,7 +32,7 @@ Usage as a library:
|
|
|
32
32
|
print(ctx.reduction_pct) # how much code was filtered out
|
|
33
33
|
"""
|
|
34
34
|
|
|
35
|
-
__version__ = "0.5.
|
|
35
|
+
__version__ = "0.5.4"
|
|
36
36
|
|
|
37
37
|
# Public, semver-covered API. Everything not listed here (graph_builder,
|
|
38
38
|
# resolver, symbols, scanner, parser internals) is importable but carries no
|
|
@@ -392,6 +392,11 @@ def _cmd_compile(args):
|
|
|
392
392
|
from ..history import CoChangeIndex
|
|
393
393
|
history = CoChangeIndex(args.repo)
|
|
394
394
|
|
|
395
|
+
# Untuned default; not derived from any sweep. The query_weight controls
|
|
396
|
+
# how much the problem-description signal moves candidates relative to
|
|
397
|
+
# the graph + BM25 + same-file blend. 0.3 is a guess that "matters but
|
|
398
|
+
# doesn't dominate" — it has NOT been benchmarked or optimized. Do not
|
|
399
|
+
# cite this as a measured value; tune it with evidence when data exists.
|
|
395
400
|
query_weight = 0.3 if args.query_text else 0.0
|
|
396
401
|
impact = analyze_impact(
|
|
397
402
|
idx, changed, max_depth=args.depth, hybrid=not args.graph_only,
|
|
@@ -131,8 +131,10 @@ def main():
|
|
|
131
131
|
if not changed and not task_description:
|
|
132
132
|
return "No changed symbols found. Pass changed_symbols, git_ref, or task_description."
|
|
133
133
|
|
|
134
|
-
#
|
|
135
|
-
#
|
|
134
|
+
# Untuned default; not derived from any sweep. The query_weight controls
|
|
135
|
+
# how much the problem-description signal moves candidates relative to
|
|
136
|
+
# the graph + BM25 + same-file blend. 0.3 is a guess that "matters but
|
|
137
|
+
# doesn't dominate" — it has NOT been benchmarked or optimized.
|
|
136
138
|
query_weight = 0.3 if task_description else 0.0
|
|
137
139
|
impact = analyze_impact(
|
|
138
140
|
idx, changed, query_text=task_description, query_weight=query_weight,
|
|
@@ -308,55 +308,80 @@ class TestPipeline:
|
|
|
308
308
|
class TestQueryText:
|
|
309
309
|
"""The query_text signal: BM25 scores the bug report / issue text
|
|
310
310
|
against every symbol's source code, adding a relevance-to-the-problem
|
|
311
|
-
signal the graph alone can't provide.
|
|
311
|
+
signal the graph alone can't provide.
|
|
312
312
|
|
|
313
|
-
|
|
313
|
+
Two requirements the user asked for:
|
|
314
|
+
1. query_text=None reproduces current ranking byte-identically
|
|
315
|
+
(regression guard — no behavior change when the feature is off).
|
|
316
|
+
2. A non-None value measurably reorders results (the signal actually
|
|
317
|
+
changes the ranking, not just the scores).
|
|
318
|
+
"""
|
|
319
|
+
|
|
320
|
+
def test_none_reproduces_baseline_byte_identically(self):
|
|
321
|
+
"""The regression guard: query_text=None must not change ANY score,
|
|
322
|
+
not even by a floating-point epsilon. This is what keeps the feature
|
|
323
|
+
truly opt-in — existing users see zero behavior change."""
|
|
314
324
|
_require_fixture(MEDIUM, "medium_repo")
|
|
315
325
|
idx = index_repository(MEDIUM)
|
|
316
|
-
|
|
317
|
-
assert sym_id in idx.graph
|
|
318
|
-
|
|
319
|
-
# Without query_text: baseline scores
|
|
320
|
-
impact_base = analyze_impact(idx, [sym_id])
|
|
321
|
-
base_sids = set(impact_base.scores)
|
|
326
|
+
seed = "./service.py:onboard_user"
|
|
322
327
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
impact_q = analyze_impact(
|
|
327
|
-
idx, [sym_id], query_text="create user order onboard", query_weight=0.3,
|
|
328
|
+
baseline = analyze_impact(idx, [seed])
|
|
329
|
+
with_query_kwarg = analyze_impact(
|
|
330
|
+
idx, [seed], query_text=None, query_weight=0.0,
|
|
328
331
|
)
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
"query_text must not remove baseline candidates, only add them"
|
|
332
|
+
# Every score must be identical — not just the set of candidates,
|
|
333
|
+
# but the actual float values. query_text=None is a true no-op.
|
|
334
|
+
assert baseline.scores == with_query_kwarg.scores, (
|
|
335
|
+
"query_text=None must reproduce baseline scores exactly; "
|
|
336
|
+
"the feature must be a true no-op when disabled."
|
|
335
337
|
)
|
|
336
338
|
|
|
337
|
-
def
|
|
339
|
+
def test_query_text_reorders_results(self):
|
|
340
|
+
"""A non-None query_text must measurably change the ranking — not
|
|
341
|
+
just add candidates, but move at least one existing symbol to a
|
|
342
|
+
different rank position. This proves the signal is doing work."""
|
|
338
343
|
_require_fixture(MEDIUM, "medium_repo")
|
|
339
344
|
idx = index_repository(MEDIUM)
|
|
340
|
-
|
|
345
|
+
seed = "./service.py:onboard_user"
|
|
341
346
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
347
|
+
baseline = analyze_impact(idx, [seed])
|
|
348
|
+
base_ranked = sorted(baseline.scores.items(), key=lambda x: -x[1])
|
|
349
|
+
base_order = [sid for sid, _ in base_ranked]
|
|
350
|
+
|
|
351
|
+
# "email validation check" should boost is_valid_email (whose code
|
|
352
|
+
# contains "email" and "valid") above User.__init__ (which doesn't).
|
|
353
|
+
impacted = analyze_impact(
|
|
354
|
+
idx, [seed], query_text="email validation check", query_weight=0.3,
|
|
355
|
+
)
|
|
356
|
+
q_ranked = sorted(impacted.scores.items(), key=lambda x: -x[1])
|
|
357
|
+
q_order = [sid for sid, _ in q_ranked]
|
|
358
|
+
|
|
359
|
+
assert base_order != q_order, (
|
|
360
|
+
"query_text must reorder at least one symbol; if the ranking "
|
|
361
|
+
"is identical the signal has no effect."
|
|
362
|
+
)
|
|
363
|
+
# Specifically: is_valid_email should rank higher with the query
|
|
364
|
+
# "email validation check" than without it.
|
|
365
|
+
base_email_rank = base_order.index("./validators.py:is_valid_email")
|
|
366
|
+
q_email_rank = q_order.index("./validators.py:is_valid_email")
|
|
367
|
+
assert q_email_rank < base_email_rank, (
|
|
368
|
+
f"is_valid_email should rank HIGHER with query_text='email "
|
|
369
|
+
f"validation check' (baseline rank {base_email_rank} -> query "
|
|
370
|
+
f"rank {q_email_rank}), not lower or unchanged."
|
|
348
371
|
)
|
|
349
372
|
|
|
350
|
-
def
|
|
373
|
+
def test_query_weight_zero_is_noop(self):
|
|
374
|
+
"""query_weight=0.0 must be a no-op even with non-None query_text."""
|
|
351
375
|
_require_fixture(MEDIUM, "medium_repo")
|
|
352
376
|
idx = index_repository(MEDIUM)
|
|
353
|
-
|
|
377
|
+
seed = "./service.py:onboard_user"
|
|
354
378
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
379
|
+
baseline = analyze_impact(idx, [seed])
|
|
380
|
+
with_weight_zero = analyze_impact(
|
|
381
|
+
idx, [seed], query_text="email validation", query_weight=0.0,
|
|
382
|
+
)
|
|
383
|
+
assert baseline.scores == with_weight_zero.scores, (
|
|
384
|
+
"query_weight=0.0 must be a no-op even with non-None query_text."
|
|
360
385
|
)
|
|
361
386
|
|
|
362
387
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|