@ictechgy/context-guard 0.5.1 → 0.7.0

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.
@@ -52,6 +52,8 @@ ADAPTIVE_K_SCHEMA_VERSION = "contextguard.pack-adaptive-k.v1"
52
52
  ADAPTIVE_K_APPLICATION_SCHEMA_VERSION = "contextguard.pack-adaptive-k-application.v1"
53
53
  SYMBOL_MEMORY_SCHEMA_VERSION = "contextguard.pack-symbol-memory.v1"
54
54
  GRAPH_APPLICATION_SCHEMA_VERSION = "contextguard.pack-graph-application.v1"
55
+ SELF_FINANCING_SELECTION_SCHEMA_VERSION = "contextguard.pack-self-financing-selection.v1"
56
+ SELECTION_PLAN_SCHEMA_VERSION = "contextguard.pack-selection-plan.v1"
55
57
  CONTENT_ADDRESS_SCHEMA_VERSION = "contextguard.pack-content-address.v1"
56
58
  ROLLING_DELTA_SCHEMA_VERSION = "contextguard.pack-rolling-delta.v1"
57
59
  SKETCH_DUPLICATE_SHINGLE_WIDTH = 5
@@ -4720,6 +4722,270 @@ def line_identity_from_dict(value: object) -> str:
4720
4722
  return f"{value.get('start')}:{value.get('end')}"
4721
4723
 
4722
4724
 
4725
+ def frozen_source_content_sha256(
4726
+ path: str,
4727
+ lines: object,
4728
+ source_cache: _SourceSnapshotCache,
4729
+ ) -> str | None:
4730
+ rel, _reason = lexical_rel(path)
4731
+ if rel is None:
4732
+ return None
4733
+ snapshot = source_cache.entries.get(
4734
+ (rel.as_posix(), line_identity_from_dict(lines), "source_code")
4735
+ )
4736
+ if snapshot is None:
4737
+ return None
4738
+ return sha256_text("".join(snapshot.selected_lines))
4739
+
4740
+
4741
+ def frozen_source_identity(
4742
+ path: str,
4743
+ lines: object,
4744
+ identities: dict[str, tuple[int, int, int, int, int, int, int, int]],
4745
+ source_cache: _SourceSnapshotCache,
4746
+ ) -> str:
4747
+ identity = identities.get(path)
4748
+ content_sha256 = frozen_source_content_sha256(path, lines, source_cache)
4749
+ material = json.dumps(
4750
+ {
4751
+ "content_sha256": content_sha256,
4752
+ "lines": line_identity_from_dict(lines),
4753
+ "path": path,
4754
+ "stat": identity,
4755
+ },
4756
+ ensure_ascii=False,
4757
+ sort_keys=True,
4758
+ separators=(",", ":"),
4759
+ )
4760
+ return f"sha256:{sha256_text(material)}"
4761
+
4762
+
4763
+ def exact_source_fallback(
4764
+ root_arg: str,
4765
+ path: str,
4766
+ lines: object,
4767
+ *,
4768
+ expected_content_sha256: str | None,
4769
+ unavailable_reason: str | None = None,
4770
+ ) -> dict[str, Any]:
4771
+ if unavailable_reason is not None:
4772
+ return {"kind": "unavailable", "reason": unavailable_reason}
4773
+ rel, _reason = lexical_rel(path)
4774
+ safe_root = safe_root_arg_for_retrieval(root_arg)
4775
+ if (
4776
+ rel is None
4777
+ or safe_root is None
4778
+ or repo_map_path_has_sensitive_evidence(path)
4779
+ or not isinstance(lines, dict)
4780
+ or not isinstance(lines.get("start"), int)
4781
+ or isinstance(lines.get("start"), bool)
4782
+ or not isinstance(lines.get("end"), int)
4783
+ or isinstance(lines.get("end"), bool)
4784
+ or lines["start"] < 1
4785
+ or lines["end"] < lines["start"]
4786
+ or expected_content_sha256 is None
4787
+ ):
4788
+ return {"kind": "unavailable", "reason": "exact_snapshot_unavailable"}
4789
+ line_identity = line_identity_from_dict(lines)
4790
+ args = [
4791
+ "context-guard-pack", "slice", "--root", safe_root,
4792
+ "--path", rel.as_posix(), "--lines", line_identity, "--json",
4793
+ ]
4794
+ return {
4795
+ "kind": "exact_source_slice",
4796
+ "command": " ".join(shlex.quote(part) for part in args),
4797
+ "expected_content_sha256": expected_content_sha256,
4798
+ "path": rel.as_posix(),
4799
+ "lines": copy.deepcopy(lines),
4800
+ }
4801
+
4802
+
4803
+ def self_financing_candidate_receipt(
4804
+ *, phase: str, source: dict[str, Any], reason: str, status: str,
4805
+ secret_decision: str, identities: dict[str, tuple[int, int, int, int, int, int, int, int]],
4806
+ root_arg: str, source_cache: _SourceSnapshotCache, byte_delta: int = 0,
4807
+ removed_sources: list[dict[str, Any]] | None = None,
4808
+ ) -> dict[str, Any]:
4809
+ path = str(source.get("path", ""))
4810
+ content_sha256 = frozen_source_content_sha256(
4811
+ path, source.get("lines"), source_cache
4812
+ )
4813
+ return {
4814
+ "phase": phase,
4815
+ "status": status,
4816
+ "path": path,
4817
+ "lines": copy.deepcopy(source.get("lines")),
4818
+ "reason": reason,
4819
+ "hop_count": 1 if phase == "graph" else 0,
4820
+ "frozen_identity": frozen_source_identity(
4821
+ path, source.get("lines"), identities, source_cache
4822
+ ),
4823
+ "byte_delta": byte_delta,
4824
+ "secret_risk": {"decision": secret_decision, "signal": "bounded_local_pattern_scan"},
4825
+ "exact_fallback": exact_source_fallback(
4826
+ root_arg,
4827
+ path,
4828
+ source.get("lines"),
4829
+ expected_content_sha256=content_sha256,
4830
+ unavailable_reason="secret_risk" if secret_decision == "reject" else None,
4831
+ ),
4832
+ "removed_sources": copy.deepcopy(removed_sources or []),
4833
+ }
4834
+
4835
+
4836
+ def selection_plan_source(
4837
+ item: dict[str, Any], identities: dict[str, tuple[int, int, int, int, int, int, int, int]],
4838
+ source_cache: _SourceSnapshotCache, root_arg: str,
4839
+ ) -> dict[str, Any]:
4840
+ path = str(item.get("path", ""))
4841
+ lines = copy.deepcopy(item.get("requested_lines", item.get("lines")))
4842
+ content_sha256 = frozen_source_content_sha256(path, lines, source_cache)
4843
+ fallback = exact_source_fallback(
4844
+ root_arg, path, lines, expected_content_sha256=content_sha256
4845
+ )
4846
+ if fallback.get("kind") != "exact_source_slice":
4847
+ raise PackError("selection plan missing exact recovery")
4848
+ return {
4849
+ "path": path,
4850
+ "lines": lines,
4851
+ "identity": frozen_source_identity(path, lines, identities, source_cache),
4852
+ "content_sha256": content_sha256,
4853
+ "exact_fallback": fallback,
4854
+ }
4855
+
4856
+
4857
+ def build_selection_plan(
4858
+ args: argparse.Namespace, ordinary_build: dict[str, Any], selected_build: dict[str, Any],
4859
+ receipt: dict[str, Any], repo_map: dict[str, Any], suggest_payload: dict[str, Any],
4860
+ identities: dict[str, tuple[int, int, int, int, int, int, int, int]],
4861
+ source_cache: _SourceSnapshotCache, *, root_arg: str,
4862
+ ) -> dict[str, Any]:
4863
+ if getattr(args, "selection_plan", False) and (args.manifest_out or args.pack_out):
4864
+ raise PackError("selection planning is read-only; output paths are unsupported")
4865
+ if not args.json:
4866
+ raise PackError("selection planning requires --json")
4867
+ if getattr(args, "selection_plan", False) and getattr(args, "apply_selection_plan", None):
4868
+ raise PackError("selection plan and apply are mutually exclusive")
4869
+ if args.delta_from_pack_id:
4870
+ raise PackError("selection plan does not cross private receipt boundaries")
4871
+ explicit_paths = split_suggest_files(args.files) + list(args.output or []) + list(args.test_output or [])
4872
+ if any(repo_map_path_has_sensitive_evidence(path) or re.search(r"(?i)(?:^|[-_/.])(scorer|private)(?:[-_/.]|$)", path) for path in explicit_paths):
4873
+ raise PackError("selection plan refuses scorer/private data")
4874
+ if any(
4875
+ isinstance(item, dict) and str(item.get("path", "")).startswith("redacted-path#")
4876
+ for item in repo_map.get("token_tree", [])
4877
+ ):
4878
+ raise PackError("selection plan refuses scorer/private data")
4879
+ caps = repo_map.get("caps", {}) if isinstance(repo_map.get("caps"), dict) else {}
4880
+ summary = repo_map.get("summary", {}) if isinstance(repo_map.get("summary"), dict) else {}
4881
+ graph = repo_map.get("graph", {}) if isinstance(repo_map.get("graph"), dict) else {}
4882
+ if SECRET_CONTENT_RE.search(str(args.query)):
4883
+ raise PackError("selection plan refuses secret-risk input")
4884
+ if (
4885
+ any(bool(caps.get(key)) for key in ("files_capped", "candidate_files_capped", "scan_files_capped"))
4886
+ or int(summary.get("bytes_per_file_capped_count", 0) or 0) != 0
4887
+ or bool(repo_map.get("omitted_files"))
4888
+ or int(graph.get("edges_omitted_by_cap", 0) or 0) != 0
4889
+ or bool(ordinary_build.get("input", {}).get("capped"))
4890
+ or bool(selected_build.get("input", {}).get("capped"))
4891
+ or any(
4892
+ isinstance(item, dict) and item.get("reason") == "query_scan_truncated"
4893
+ for item in suggest_payload.get("omitted_sources", [])
4894
+ )
4895
+ ):
4896
+ raise PackError("selection plan requires a complete scan")
4897
+ secret_scan = repo_map.get("secret_scan", {}) if isinstance(repo_map.get("secret_scan"), dict) else {}
4898
+ if (
4899
+ int(ordinary_build.get("redaction", {}).get("redacted_lines", 0) or 0) != 0
4900
+ or int(selected_build.get("redaction", {}).get("redacted_lines", 0) or 0) != 0
4901
+ or bool(secret_scan.get("files_with_risks"))
4902
+ or int(secret_scan.get("files_omitted_by_cap", 0) or 0) != 0
4903
+ ):
4904
+ raise PackError("selection plan refuses secret-risk input")
4905
+
4906
+ ordinary = [
4907
+ selection_plan_source(item, identities, source_cache, root_arg)
4908
+ for item in ordinary_build.get("included_sources", []) if isinstance(item, dict)
4909
+ ]
4910
+ decisions = [copy.deepcopy(item) for item in receipt.get("decisions", []) if isinstance(item, dict)]
4911
+ for decision in decisions:
4912
+ fallback = decision.get("exact_fallback", {})
4913
+ if fallback.get("kind") != "exact_source_slice":
4914
+ raise PackError("selection plan missing exact recovery")
4915
+ recovered_removed = []
4916
+ for removed in decision.get("removed_sources", []):
4917
+ if not isinstance(removed, dict):
4918
+ raise PackError("selection plan missing exact recovery")
4919
+ recovered = copy.deepcopy(removed)
4920
+ if recovered.get("exact_fallback", {}).get("kind") != "exact_source_slice":
4921
+ source = selection_plan_source(recovered, identities, source_cache, root_arg)
4922
+ recovered["frozen_identity"] = source["identity"]
4923
+ recovered["exact_fallback"] = exact_source_fallback(
4924
+ root_arg, source["path"], source["lines"],
4925
+ expected_content_sha256=source["content_sha256"],
4926
+ )
4927
+ recovered_removed.append(recovered)
4928
+ decision["removed_sources"] = recovered_removed
4929
+ selected = [item for item in decisions if item.get("status") == "selected"]
4930
+ omitted = [item for item in decisions if item.get("status") != "selected"]
4931
+ replacement = [
4932
+ {"candidate_identity": item["frozen_identity"], "removed": copy.deepcopy(item.get("removed_sources", []))}
4933
+ for item in selected if item.get("removed_sources")
4934
+ ]
4935
+ fallback = [
4936
+ {"identity": item["identity"], "exact_fallback": copy.deepcopy(item["exact_fallback"])}
4937
+ for item in ordinary
4938
+ ] + [
4939
+ {"identity": item["frozen_identity"], "exact_fallback": copy.deepcopy(item["exact_fallback"])}
4940
+ for item in decisions
4941
+ ]
4942
+ material: dict[str, Any] = {
4943
+ "schema_version": SELECTION_PLAN_SCHEMA_VERSION,
4944
+ "ordinary": ordinary,
4945
+ "candidate": decisions,
4946
+ "selected": selected,
4947
+ "omitted": omitted,
4948
+ "replacement": replacement,
4949
+ "ceiling": {
4950
+ "unit": "rendered_bytes",
4951
+ "ordinary": int(receipt.get("ordinary_pack_bytes", 0) or 0),
4952
+ "selected": int(receipt.get("selected_rendered_bytes", 0) or 0),
4953
+ },
4954
+ "fallback": fallback,
4955
+ "provenance": {
4956
+ "query_sha256": sha256_text(str(args.query)),
4957
+ "diff": cap_label(args.diff) if args.diff else None,
4958
+ "ordinary_pack_id": ordinary_build.get("pack_id"),
4959
+ "selected_pack_id": selected_build.get("pack_id"),
4960
+ "source_identities": sorted(item["identity"] for item in ordinary),
4961
+ },
4962
+ "safety": {
4963
+ "read_only": True, "provider_free": True, "complete_scan": True,
4964
+ "source_revalidation_required_on_apply": True,
4965
+ },
4966
+ "claim_boundary": {"provider_token_or_cost_savings_claim_allowed": False},
4967
+ }
4968
+ plan_id = "sha256:" + sha256_text(json.dumps(material, ensure_ascii=False, sort_keys=True, separators=(",", ":")))
4969
+ return {"schema_version": material.pop("schema_version"), "plan_id": plan_id, **material}
4970
+
4971
+
4972
+ def read_selection_plan(root: Path, raw_path: str) -> dict[str, Any]:
4973
+ rel = output_rel_for_collision_check(raw_path, "--apply-selection-plan")
4974
+ try:
4975
+ value = json.loads(
4976
+ read_manifest_bytes_no_follow(root / rel).decode("utf-8"),
4977
+ object_pairs_hook=strict_json_object,
4978
+ parse_constant=reject_json_constant,
4979
+ parse_int=parse_receipt_int,
4980
+ )
4981
+ json_depth(value)
4982
+ except (UnicodeDecodeError, ValueError, RecursionError) as exc:
4983
+ raise PackError("invalid selection plan JSON") from exc
4984
+ if not isinstance(value, dict) or value.get("schema_version") != SELECTION_PLAN_SCHEMA_VERSION:
4985
+ raise PackError("unsupported selection plan schema")
4986
+ return value
4987
+
4988
+
4723
4989
  def apply_symbol_memory_graph(
4724
4990
  manifest: dict[str, Any],
4725
4991
  repo_map: dict[str, Any],
@@ -5095,6 +5361,9 @@ def build_auto_explain_payload(
5095
5361
 
5096
5362
 
5097
5363
  def auto_pack(root: Path, args: argparse.Namespace, *, root_arg: str) -> tuple[dict[str, Any], int]:
5364
+ plan_only = bool(getattr(args, "selection_plan", False))
5365
+ apply_plan_path = getattr(args, "apply_selection_plan", None)
5366
+ expected_plan = read_selection_plan(root, apply_plan_path) if apply_plan_path else None
5098
5367
  source_cache = _SourceSnapshotCache()
5099
5368
  input_budget = _SourceInputBudget()
5100
5369
  manifest_rel = output_rel_for_collision_check(args.manifest_out, "--manifest-out") if args.manifest_out else None
@@ -5113,7 +5382,8 @@ def auto_pack(root: Path, args: argparse.Namespace, *, root_arg: str) -> tuple[d
5113
5382
  validate_output_path_under_root(root, args.pack_out, "--pack-out")
5114
5383
  suggest_args = copy.copy(args)
5115
5384
  suggest_args.manifest_out = None
5116
- apply_adaptive_k = bool(getattr(args, "apply_adaptive_k", False))
5385
+ self_financing = bool(getattr(args, "self_financing_selection", False) or plan_only or apply_plan_path)
5386
+ apply_adaptive_k = bool(getattr(args, "apply_adaptive_k", False) or self_financing)
5117
5387
  if apply_adaptive_k:
5118
5388
  suggest_args.adaptive_k = True
5119
5389
  suggest_payload, rc = suggest_pack(
@@ -5124,6 +5394,18 @@ def auto_pack(root: Path, args: argparse.Namespace, *, root_arg: str) -> tuple[d
5124
5394
  _input_budget=input_budget,
5125
5395
  )
5126
5396
  manifest = suggest_payload["manifest"]
5397
+ ordinary_build_payload: dict[str, Any] | None = None
5398
+ if self_financing:
5399
+ ordinary_build_payload = build_pack(
5400
+ root,
5401
+ manifest_to_source_specs(manifest),
5402
+ budget_bytes=bounded_int(args.budget_bytes, DEFAULT_BUDGET_BYTES, MIN_BUDGET_BYTES, MAX_BUDGET_BYTES),
5403
+ root_arg=root_arg,
5404
+ store_artifact=False,
5405
+ sketch_duplicate_veto=getattr(args, "sketch_duplicate_veto", False),
5406
+ _source_cache=source_cache,
5407
+ _input_budget=input_budget,
5408
+ )
5127
5409
  adaptive_k_application: dict[str, Any] | None = None
5128
5410
  if apply_adaptive_k and isinstance(suggest_payload.get("adaptive_k"), dict):
5129
5411
  manifest, adaptive_k_application = apply_adaptive_k_manifest(
@@ -5166,7 +5448,7 @@ def auto_pack(root: Path, args: argparse.Namespace, *, root_arg: str) -> tuple[d
5166
5448
  )
5167
5449
  repo_map_payload: dict[str, Any] | None = None
5168
5450
  graph_application: dict[str, Any] | None = None
5169
- apply_symbol_memory = bool(getattr(args, "apply_symbol_memory", False))
5451
+ apply_symbol_memory = bool(getattr(args, "apply_symbol_memory", False) or self_financing)
5170
5452
  complete_secret_paths: set[str] | None = set() if apply_symbol_memory else None
5171
5453
  repo_map_source_identities: dict[
5172
5454
  str,
@@ -5184,7 +5466,295 @@ def auto_pack(root: Path, args: argparse.Namespace, *, root_arg: str) -> tuple[d
5184
5466
  repo_map_source_identities if apply_symbol_memory else None
5185
5467
  ),
5186
5468
  )
5187
- if apply_symbol_memory and isinstance(repo_map_payload, dict):
5469
+ self_financing_receipt: dict[str, Any] | None = None
5470
+ if self_financing and isinstance(repo_map_payload, dict) and ordinary_build_payload is not None:
5471
+ ordinary_ceiling = int(ordinary_build_payload.get("pack_bytes", 0) or 0)
5472
+ decisions: list[dict[str, Any]] = []
5473
+ recorded_secret_decisions: set[tuple[str, str]] = set()
5474
+ ordinary_sources = ordinary_build_payload.get("included_sources", [])
5475
+ retained_keys = {
5476
+ (str(item.get("path", "")), line_range_identity(item.get("lines")))
5477
+ for item in manifest.get("sources", []) if isinstance(item, dict)
5478
+ }
5479
+ for item in ordinary_sources if isinstance(ordinary_sources, list) else []:
5480
+ if not isinstance(item, dict):
5481
+ continue
5482
+ key = (str(item.get("path", "")), line_range_identity(item.get("requested_lines")))
5483
+ if key not in retained_keys:
5484
+ source = {"path": key[0], "lines": copy.deepcopy(item.get("requested_lines"))}
5485
+ decisions.append(self_financing_candidate_receipt(
5486
+ phase="adaptive", source=source, reason="adaptive_headroom_removal",
5487
+ status="selected", secret_decision="allow", identities=repo_map_source_identities,
5488
+ root_arg=root_arg, source_cache=source_cache,
5489
+ byte_delta=-int(item.get("bytes", 0) or 0),
5490
+ removed_sources=[source],
5491
+ ))
5492
+
5493
+ existing_paths = {str(item.get("path", "")) for item in manifest.get("sources", []) if isinstance(item, dict)}
5494
+ query_terms = suggest_tokens(str(suggest_payload.get("query", "")))
5495
+ candidates: list[tuple[str, dict[str, Any], str]] = []
5496
+ for signature in repo_map_payload.get("signature_index", []):
5497
+ if not isinstance(signature, dict):
5498
+ continue
5499
+ path = str(signature.get("path", ""))
5500
+ searchable = suggest_tokens(f"{signature.get('name', '')} {signature.get('signature', '')}")
5501
+ if not query_terms.intersection(searchable):
5502
+ continue
5503
+ source = {
5504
+ "path": path, "priority": 2, "label": f"symbol:{path}"[:MAX_LABEL_CHARS],
5505
+ "lines": copy.deepcopy(signature.get("lines")),
5506
+ }
5507
+ if path in (complete_secret_paths or set()):
5508
+ decisions.append(self_financing_candidate_receipt(
5509
+ phase="symbol", source=source, reason="secret_risk", status="no_op",
5510
+ secret_decision="reject", identities=repo_map_source_identities,
5511
+ root_arg=root_arg, source_cache=source_cache,
5512
+ ))
5513
+ recorded_secret_decisions.add(("symbol", path))
5514
+ continue
5515
+ if path in existing_paths:
5516
+ existing_source = next(
5517
+ (
5518
+ item for item in build_payload.get("included_sources", [])
5519
+ if isinstance(item, dict)
5520
+ and item.get("path") == path
5521
+ and item.get("status") == "included"
5522
+ and item.get("requested_lines") == item.get("included_lines")
5523
+ ),
5524
+ None,
5525
+ )
5526
+ if existing_source is not None:
5527
+ source["lines"] = copy.deepcopy(existing_source["requested_lines"])
5528
+ decisions.append(self_financing_candidate_receipt(
5529
+ phase="symbol", source=source, reason="duplicate_source", status="no_op",
5530
+ secret_decision="allow", identities=repo_map_source_identities,
5531
+ root_arg=root_arg, source_cache=source_cache,
5532
+ ))
5533
+ continue
5534
+ candidates.append(("symbol", source, "task_matching_symbol"))
5535
+ if len([item for item in candidates if item[0] == "symbol"]) >= MAX_GRAPH_APPLICATION_SOURCES:
5536
+ break
5537
+ graph_manifest, graph_preview = apply_symbol_memory_graph(
5538
+ manifest, repo_map_payload, complete_secret_paths=complete_secret_paths,
5539
+ )
5540
+ graph_sources = graph_manifest.get("sources", [])
5541
+ for source in graph_sources[len(manifest.get("sources", [])):]:
5542
+ if isinstance(source, dict):
5543
+ candidates.append(("graph", source, "direct_import_neighbor"))
5544
+
5545
+ frozen_candidate_sources = {
5546
+ (str(source.get("path", "")), line_range_identity(source.get("lines")))
5547
+ for _phase, source, _reason in candidates
5548
+ }
5549
+ candidate_specs = manifest_to_source_specs(build_suggest_manifest(
5550
+ [source for _phase, source, _reason in candidates]
5551
+ ))
5552
+ candidate_snapshot_rejections = bind_graph_sources_to_repo_snapshot(
5553
+ root, candidate_specs, frozen_candidate_sources, repo_map_source_identities,
5554
+ source_cache=source_cache, input_budget=input_budget,
5555
+ )
5556
+
5557
+ # Record secret-risk direct neighbors as explicit no-ops without exposing their contents.
5558
+ graph = repo_map_payload.get("graph", {})
5559
+ for edge in graph.get("edges", []) if isinstance(graph, dict) else []:
5560
+ if not isinstance(edge, dict):
5561
+ continue
5562
+ for path in (edge.get("from"), edge.get("to")):
5563
+ if isinstance(path, str) and path in (complete_secret_paths or set()) and path not in existing_paths:
5564
+ if ("graph", path) in recorded_secret_decisions:
5565
+ continue
5566
+ source = {"path": path, "lines": None}
5567
+ decisions.append(self_financing_candidate_receipt(
5568
+ phase="graph", source=source, reason="secret_risk", status="no_op",
5569
+ secret_decision="reject", identities=repo_map_source_identities,
5570
+ root_arg=root_arg, source_cache=source_cache,
5571
+ ))
5572
+ recorded_secret_decisions.add(("graph", path))
5573
+
5574
+ current_manifest = copy.deepcopy(manifest)
5575
+ current_build = build_payload
5576
+ protected_sources = [
5577
+ (
5578
+ str(item.get("path", "")),
5579
+ copy.deepcopy(item.get("lines")) if isinstance(item.get("lines"), dict) else None,
5580
+ )
5581
+ for item in current_manifest.get("sources", [])
5582
+ if isinstance(item, dict)
5583
+ and str(item.get("label", "")).startswith(
5584
+ ("file:", "output:", "test-output:", "diff:", "critical:")
5585
+ )
5586
+ ]
5587
+
5588
+ def protected_sources_are_exact(build: dict[str, Any]) -> bool:
5589
+ included_sources = [
5590
+ item for item in build.get("included_sources", [])
5591
+ if isinstance(item, dict)
5592
+ ]
5593
+ for protected_path, protected_lines in protected_sources:
5594
+ matched = False
5595
+ for item in included_sources:
5596
+ if (
5597
+ item.get("path") != protected_path
5598
+ or item.get("status") != "included"
5599
+ or item.get("requested_lines") != item.get("included_lines")
5600
+ ):
5601
+ continue
5602
+ if protected_lines is not None and item.get("requested_lines") != protected_lines:
5603
+ continue
5604
+ matched = True
5605
+ break
5606
+ if not matched:
5607
+ return False
5608
+ return True
5609
+
5610
+ seen_candidates: set[tuple[str, str]] = set()
5611
+ for phase, candidate, reason in candidates:
5612
+ key = (str(candidate.get("path", "")), line_range_identity(candidate.get("lines")))
5613
+ if key in seen_candidates or key[0] in {str(item.get("path", "")) for item in current_manifest.get("sources", []) if isinstance(item, dict)}:
5614
+ decisions.append(self_financing_candidate_receipt(
5615
+ phase=phase, source=candidate, reason="duplicate_source", status="no_op",
5616
+ secret_decision="allow", identities=repo_map_source_identities,
5617
+ root_arg=root_arg, source_cache=source_cache,
5618
+ ))
5619
+ continue
5620
+ seen_candidates.add(key)
5621
+ trial_sources = [copy.deepcopy(item) for item in current_manifest.get("sources", []) if isinstance(item, dict)] + [copy.deepcopy(candidate)]
5622
+ candidate_priority = int(candidate.get("priority", 0) or 0)
5623
+ removable = sorted(
5624
+ [
5625
+ item for item in trial_sources[:-1]
5626
+ if not str(item.get("label", "")).startswith(
5627
+ ("file:", "output:", "test-output:", "diff:", "critical:")
5628
+ )
5629
+ and int(item.get("priority", 0) or 0) < candidate_priority
5630
+ ],
5631
+ key=lambda item: (int(item.get("priority", 0) or 0), str(item.get("path", ""))),
5632
+ )
5633
+ removed: list[dict[str, Any]] = []
5634
+ accepted_build: dict[str, Any] | None = None
5635
+ while True:
5636
+ trial_manifest = build_suggest_manifest(trial_sources)
5637
+ trial_build = build_pack(
5638
+ root, manifest_to_source_specs(trial_manifest), budget_bytes=max(MIN_BUDGET_BYTES, ordinary_ceiling),
5639
+ root_arg=root_arg, store_artifact=False,
5640
+ sketch_duplicate_veto=getattr(args, "sketch_duplicate_veto", False),
5641
+ _source_cache=source_cache, _input_budget=input_budget,
5642
+ _required_snapshot_sources=frozen_candidate_sources,
5643
+ _expected_source_identities=repo_map_source_identities,
5644
+ _snapshot_rejections=candidate_snapshot_rejections,
5645
+ )
5646
+ candidate_included_exactly = any(
5647
+ isinstance(item, dict)
5648
+ and str(item.get("path", "")) == key[0]
5649
+ and line_range_identity(item.get("requested_lines")) == key[1]
5650
+ and line_range_identity(item.get("included_lines")) == key[1]
5651
+ and item.get("status") == "included"
5652
+ for item in trial_build.get("included_sources", [])
5653
+ )
5654
+ if (
5655
+ int(trial_build.get("pack_bytes", 0) or 0) <= ordinary_ceiling
5656
+ and candidate_included_exactly
5657
+ and protected_sources_are_exact(trial_build)
5658
+ ):
5659
+ accepted_build = trial_build
5660
+ break
5661
+ if not removable:
5662
+ break
5663
+ victim = removable.pop(0)
5664
+ trial_sources.remove(victim)
5665
+ victim_source = {
5666
+ "path": victim.get("path"),
5667
+ "lines": copy.deepcopy(victim.get("lines")),
5668
+ }
5669
+ if not isinstance(victim_source["lines"], dict):
5670
+ prior = next(
5671
+ (
5672
+ item for item in current_build.get("included_sources", [])
5673
+ if isinstance(item, dict)
5674
+ and item.get("path") == victim_source["path"]
5675
+ and item.get("status") == "included"
5676
+ and item.get("requested_lines") == item.get("included_lines")
5677
+ ),
5678
+ None,
5679
+ )
5680
+ if prior is not None:
5681
+ victim_source["lines"] = copy.deepcopy(prior["requested_lines"])
5682
+ removed.append({
5683
+ "path": victim_source["path"], "lines": victim_source["lines"],
5684
+ "reason": "lower_value_replacement",
5685
+ "frozen_identity": frozen_source_identity(
5686
+ str(victim_source["path"] or ""), victim_source["lines"],
5687
+ repo_map_source_identities, source_cache,
5688
+ ),
5689
+ "exact_fallback": exact_source_fallback(
5690
+ root_arg, str(victim_source["path"] or ""), victim_source["lines"],
5691
+ expected_content_sha256=frozen_source_content_sha256(
5692
+ str(victim_source["path"] or ""), victim_source["lines"], source_cache
5693
+ ),
5694
+ ),
5695
+ })
5696
+ if accepted_build is None:
5697
+ decisions.append(self_financing_candidate_receipt(
5698
+ phase=phase, source=candidate, reason="ordinary_ceiling_no_safe_replacement", status="no_op",
5699
+ secret_decision="allow", identities=repo_map_source_identities,
5700
+ root_arg=root_arg, source_cache=source_cache,
5701
+ ))
5702
+ continue
5703
+ previous_bytes = int(current_build.get("pack_bytes", 0) or 0)
5704
+ current_manifest = build_suggest_manifest(trial_sources)
5705
+ current_build = accepted_build
5706
+ decisions.append(self_financing_candidate_receipt(
5707
+ phase=phase, source=candidate, reason=reason, status="selected",
5708
+ secret_decision="allow", identities=repo_map_source_identities,
5709
+ root_arg=root_arg, source_cache=source_cache,
5710
+ byte_delta=int(current_build.get("pack_bytes", 0) or 0) - previous_bytes,
5711
+ removed_sources=removed,
5712
+ ))
5713
+ manifest = current_manifest
5714
+ build_payload = current_build
5715
+ selected_rendered_bytes = int(build_payload.get("pack_bytes", 0) or 0)
5716
+ if (
5717
+ selected_rendered_bytes > ordinary_ceiling
5718
+ or not protected_sources_are_exact(build_payload)
5719
+ ):
5720
+ raise PackError("self-financing selection invariant failed")
5721
+ suggest_payload["manifest"] = manifest
5722
+ suggest_payload["estimated_pack_bytes"] = build_payload.get("pack_bytes", 0)
5723
+ suggest_payload["token_proxy"] = copy.deepcopy(build_payload.get("token_proxy", {}))
5724
+ selected_graph_decisions = [
5725
+ item for item in decisions
5726
+ if item.get("phase") == "graph" and item.get("status") == "selected"
5727
+ ]
5728
+ graph_application = {
5729
+ **graph_preview,
5730
+ "selected_source_count": len(selected_graph_decisions),
5731
+ "selected_sources": [
5732
+ {
5733
+ "path": item.get("path"), "lines": copy.deepcopy(item.get("lines")),
5734
+ "reason": item.get("reason"),
5735
+ }
5736
+ for item in selected_graph_decisions
5737
+ ],
5738
+ }
5739
+ phase_results = {}
5740
+ for phase in ("adaptive", "symbol", "graph"):
5741
+ phase_decisions = [item for item in decisions if item.get("phase") == phase]
5742
+ phase_results[phase] = {
5743
+ "status": "applied" if any(item.get("status") == "selected" for item in phase_decisions) else "no_op",
5744
+ "selected_count": sum(item.get("status") == "selected" for item in phase_decisions),
5745
+ "no_op_count": sum(item.get("status") == "no_op" for item in phase_decisions),
5746
+ }
5747
+ self_financing_receipt = {
5748
+ "schema_version": SELF_FINANCING_SELECTION_SCHEMA_VERSION,
5749
+ "mode": "explicit_opt_in", "phase_order": ["adaptive", "symbol", "graph"],
5750
+ "ordinary_pack_bytes": ordinary_ceiling,
5751
+ "selected_rendered_bytes": selected_rendered_bytes,
5752
+ "ceiling_respected": selected_rendered_bytes <= ordinary_ceiling,
5753
+ "phase_results": phase_results,
5754
+ "decisions": decisions,
5755
+ "claim_boundary": {"provider_token_or_cost_savings_claim_allowed": False},
5756
+ }
5757
+ elif apply_symbol_memory and isinstance(repo_map_payload, dict):
5188
5758
  repo_map_payload["safety"]["explain_only"] = False
5189
5759
  repo_map_payload["safety"]["caveats"] = [
5190
5760
  "Repo-map bytes are local sampled UTF-8 bytes and estimated chars_div_4 token proxies, not provider-token or savings claims.",
@@ -5233,6 +5803,22 @@ def auto_pack(root: Path, args: argparse.Namespace, *, root_arg: str) -> tuple[d
5233
5803
  suggest_payload["token_proxy"] = copy.deepcopy(
5234
5804
  build_payload.get("token_proxy", {})
5235
5805
  )
5806
+ selection_plan_payload: dict[str, Any] | None = None
5807
+ if plan_only or apply_plan_path:
5808
+ if not isinstance(self_financing_receipt, dict) or not isinstance(repo_map_payload, dict) or ordinary_build_payload is None:
5809
+ raise PackError("selection plan unavailable")
5810
+ selection_plan_payload = build_selection_plan(
5811
+ args, ordinary_build_payload, build_payload, self_financing_receipt,
5812
+ repo_map_payload, suggest_payload, repo_map_source_identities, source_cache, root_arg=root_arg,
5813
+ )
5814
+ if plan_only:
5815
+ return {
5816
+ "tool": TOOL_NAME, "schema_version": AUTO_SCHEMA_VERSION,
5817
+ "version": VERSION, "mode": "selection_plan",
5818
+ "selection_plan": selection_plan_payload,
5819
+ }, rc
5820
+ if expected_plan != selection_plan_payload:
5821
+ raise PackError("selection plan drift; regenerate the plan")
5236
5822
  if not args.no_artifact:
5237
5823
  receipt_rel = Path(PACK_DIR) / f"{build_payload['pack_id']}.json"
5238
5824
  if manifest_rel is not None:
@@ -5301,6 +5887,14 @@ def auto_pack(root: Path, args: argparse.Namespace, *, root_arg: str) -> tuple[d
5301
5887
  payload["adaptive_k_application"] = adaptive_k_application
5302
5888
  if graph_application is not None:
5303
5889
  payload["graph_application"] = graph_application
5890
+ if self_financing_receipt is not None:
5891
+ payload["self_financing_selection"] = self_financing_receipt
5892
+ if selection_plan_payload is not None:
5893
+ payload["selection_plan"] = selection_plan_payload
5894
+ payload["selection_plan_application"] = {
5895
+ "status": "applied", "explicit": True,
5896
+ "revalidated_plan_id": selection_plan_payload["plan_id"],
5897
+ }
5304
5898
  if (getattr(args, "symbol_memory", False) or apply_symbol_memory) and isinstance(repo_map_payload, dict):
5305
5899
  payload["symbol_memory"] = build_symbol_memory_payload(
5306
5900
  repo_map_payload, applied=apply_symbol_memory
@@ -5436,6 +6030,19 @@ def print_auto_text(payload: dict[str, Any]) -> None:
5436
6030
  print(f"omitted reasons: {reason_text}")
5437
6031
  print_adaptive_k_text(payload)
5438
6032
  print_symbol_memory_text(payload)
6033
+ self_financing = payload.get("self_financing_selection")
6034
+ if isinstance(self_financing, dict):
6035
+ phases = self_financing.get("phase_results", {})
6036
+ phase_text = ",".join(
6037
+ f"{name}={phases.get(name, {}).get('status', 'no_op')}"
6038
+ for name in ("adaptive", "symbol", "graph")
6039
+ )
6040
+ print(
6041
+ "self-financing: "
6042
+ f"ceiling={self_financing.get('ordinary_pack_bytes', 0)} "
6043
+ f"selected={self_financing.get('selected_rendered_bytes', 0)} "
6044
+ f"{phase_text} provider_savings_claim=false"
6045
+ )
5439
6046
  if payload.get("manifest_path"):
5440
6047
  print(f"manifest: {payload['manifest_path']}")
5441
6048
  if payload.get("pack_path"):
@@ -5549,6 +6156,22 @@ def build_parser() -> argparse.ArgumentParser:
5549
6156
  "repo map to the manifest and pack; implies --symbol-memory"
5550
6157
  ),
5551
6158
  )
6159
+ auto.add_argument(
6160
+ "--self-financing-selection",
6161
+ action="store_true",
6162
+ help=(
6163
+ "explicitly apply Adaptive, then Symbol, then one-hop Graph selection while replacing "
6164
+ "only lower-value non-caller sources and never exceeding the ordinary pack bytes"
6165
+ ),
6166
+ )
6167
+ auto.add_argument(
6168
+ "--selection-plan", action="store_true",
6169
+ help="emit a read-only provider-free self-financing selection plan; requires --json",
6170
+ )
6171
+ auto.add_argument(
6172
+ "--apply-selection-plan", metavar="PLAN_JSON",
6173
+ help="explicitly apply a previously emitted selection plan after identity revalidation",
6174
+ )
5552
6175
  return parser
5553
6176
 
5554
6177