forgexa-cli 1.84.4__tar.gz → 1.85.1__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.
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/PKG-INFO +1 -1
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/forgexa_cli/__init__.py +1 -1
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/forgexa_cli/agent_core.py +69 -11
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/forgexa_cli/daemon.py +74 -7
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/forgexa_cli.egg-info/PKG-INFO +1 -1
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/pyproject.toml +1 -1
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/README.md +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/forgexa_cli/_build_config.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/forgexa_cli/_local_bind.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/forgexa_cli/autoupgrade.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/forgexa_cli/aws_evidence.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/forgexa_cli/aws_logs_evidence.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/forgexa_cli/main.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/forgexa_cli/observability_contract.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/forgexa_cli/observability_evidence.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/forgexa_cli/py.typed +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/forgexa_cli/runtime_evidence.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/forgexa_cli/zcode_protocol.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/forgexa_cli.egg-info/SOURCES.txt +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/forgexa_cli.egg-info/dependency_links.txt +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/forgexa_cli.egg-info/entry_points.txt +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/forgexa_cli.egg-info/requires.txt +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/forgexa_cli.egg-info/top_level.txt +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/setup.cfg +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/tests/test_auth_and_runtime_commands.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/tests/test_autoupgrade.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/tests/test_check_command.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/tests/test_expiry_warnings_and_revoke.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/tests/test_local_bind_commands.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/tests/test_publish_script.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/tests/test_runtime_credentials.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/tests/test_session_credentials.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/tests/test_silent_install.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.1}/tests/test_upgrade_observability.py +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""forgexa-cli — Forgexa command-line client."""
|
|
2
|
-
__version__ = "1.
|
|
2
|
+
__version__ = "1.85.1"
|
|
@@ -211,6 +211,17 @@ def extract_implementation_audit(text: str) -> dict | None:
|
|
|
211
211
|
return report
|
|
212
212
|
|
|
213
213
|
|
|
214
|
+
def _implementation_acceptance_index(row: dict) -> int | None:
|
|
215
|
+
index = row.get("index", row.get("acceptance_index"))
|
|
216
|
+
if type(index) is not int:
|
|
217
|
+
return None
|
|
218
|
+
if "index" in row and "acceptance_index" in row and (
|
|
219
|
+
type(row["acceptance_index"]) is not int or row["acceptance_index"] != index
|
|
220
|
+
):
|
|
221
|
+
return None
|
|
222
|
+
return index
|
|
223
|
+
|
|
224
|
+
|
|
214
225
|
def redact_implementation_audit(contract: dict, report: object) -> dict | None:
|
|
215
226
|
"""Keep contract-owned status and hashed evidence without local free text."""
|
|
216
227
|
from hashlib import sha256
|
|
@@ -244,7 +255,7 @@ def redact_implementation_audit(contract: dict, report: object) -> dict | None:
|
|
|
244
255
|
if contract.get("version") == 2 and row["task_id"] in coding_ids and isinstance(row.get("implementation_check"), dict):
|
|
245
256
|
check["implementation_check"] = redact_check(row["implementation_check"])
|
|
246
257
|
else:
|
|
247
|
-
check.update(index=row
|
|
258
|
+
check.update(index=_implementation_acceptance_index(row), task_ids=row["task_ids"])
|
|
248
259
|
summary[key].append(check)
|
|
249
260
|
return summary
|
|
250
261
|
|
|
@@ -305,22 +316,42 @@ def _assess_implementation_contributions(contract: dict, report: object) -> dict
|
|
|
305
316
|
and len(criteria) == len(reserved_ids)
|
|
306
317
|
and reserved_ids == [criterion.get("id") for criterion in criteria if isinstance(criterion, dict)]
|
|
307
318
|
):
|
|
319
|
+
index_map = deferred_contract.get("acceptance_criteria_indices")
|
|
320
|
+
expected_indices = {identifier: index for index, identifier in enumerate(reserved_ids, 1)}
|
|
321
|
+
valid_index_map = (
|
|
322
|
+
isinstance(index_map, dict) and set(index_map) == set(expected_indices)
|
|
323
|
+
and all(type(value) is int and value == expected_indices[identifier] for identifier, value in index_map.items())
|
|
324
|
+
)
|
|
308
325
|
for criterion_id in reserved_ids:
|
|
309
326
|
match = re.fullmatch(r"AC-([0-9]+)", criterion_id)
|
|
310
|
-
|
|
327
|
+
index = index_map[criterion_id] if valid_index_map else int(match.group(1)) if index_map is None and match else 0
|
|
328
|
+
if not 1 <= index <= len(contract.get("acceptance_criteria") or []):
|
|
311
329
|
reserved_indexes.clear()
|
|
312
330
|
break
|
|
313
|
-
reserved_indexes.add(
|
|
331
|
+
reserved_indexes.add(index)
|
|
314
332
|
if len(reserved_indexes) != len(reserved_ids):
|
|
315
333
|
reserved_indexes.clear()
|
|
316
334
|
acceptance_checks = []
|
|
317
335
|
deferred_acceptance = []
|
|
336
|
+
subject = contract.get("subject_task_id")
|
|
337
|
+
prepared_verifier = (
|
|
338
|
+
not contract.get("final_verification") and task_types.get(subject) in ("testing", "verify")
|
|
339
|
+
and implementation_statuses.get(subject) == "covered"
|
|
340
|
+
)
|
|
318
341
|
for row in report["acceptance_checks"]:
|
|
342
|
+
index = _implementation_acceptance_index(row)
|
|
319
343
|
if (
|
|
320
|
-
|
|
321
|
-
and
|
|
344
|
+
index in reserved_indexes and row["status"] == "unverified" and row.get("plan_revision") is None
|
|
345
|
+
and (
|
|
346
|
+
all(implementation_statuses.get(task_id) == "covered" for task_id in row["task_ids"])
|
|
347
|
+
or prepared_verifier and subject in row["task_ids"] and all(
|
|
348
|
+
implementation_statuses.get(task_id) == "covered"
|
|
349
|
+
or task_id not in implementation_statuses and task_types.get(task_id) in ("coding", "fix")
|
|
350
|
+
for task_id in row["task_ids"]
|
|
351
|
+
)
|
|
352
|
+
)
|
|
322
353
|
):
|
|
323
|
-
deferred_acceptance.append(
|
|
354
|
+
deferred_acceptance.append(index)
|
|
324
355
|
continue
|
|
325
356
|
if (
|
|
326
357
|
pending_verification_ids and row["status"] in ("blocked", "unverified")
|
|
@@ -399,7 +430,7 @@ def assess_implementation_audit(contract: dict, report: object) -> dict:
|
|
|
399
430
|
checked_criteria: set[int] = set()
|
|
400
431
|
criterion_count = len(contract.get("acceptance_criteria") or [])
|
|
401
432
|
for kind, rows in (("task", task_checks), ("acceptance", acceptance_checks)):
|
|
402
|
-
for row in rows:
|
|
433
|
+
for position, row in enumerate(rows, 1):
|
|
403
434
|
if not isinstance(row, dict):
|
|
404
435
|
issues.append(f"Invalid implementation {kind} check")
|
|
405
436
|
continue
|
|
@@ -426,8 +457,19 @@ def assess_implementation_audit(contract: dict, report: object) -> dict:
|
|
|
426
457
|
checked[task_id] = status
|
|
427
458
|
related = [task_id]
|
|
428
459
|
else:
|
|
429
|
-
index = row
|
|
460
|
+
index = _implementation_acceptance_index(row)
|
|
430
461
|
related = row.get("task_ids")
|
|
462
|
+
if index is None:
|
|
463
|
+
detail = (
|
|
464
|
+
"index and acceptance_index must be identical integers"
|
|
465
|
+
if "index" in row and "acceptance_index" in row else
|
|
466
|
+
"index must be a one-based integer; acceptance_index is an exact alias, but id is not an index"
|
|
467
|
+
)
|
|
468
|
+
issues.append(
|
|
469
|
+
f"Implementation audit has invalid acceptance criterion or task references; "
|
|
470
|
+
f"acceptance row {position}: {detail}"
|
|
471
|
+
)
|
|
472
|
+
continue
|
|
431
473
|
if type(index) is int and not 1 <= index <= criterion_count:
|
|
432
474
|
if criterion_count == 0:
|
|
433
475
|
issues.append(
|
|
@@ -443,11 +485,18 @@ def assess_implementation_audit(contract: dict, report: object) -> dict:
|
|
|
443
485
|
)
|
|
444
486
|
continue
|
|
445
487
|
if (
|
|
446
|
-
|
|
488
|
+
index in checked_criteria
|
|
447
489
|
or not isinstance(related, list) or not related
|
|
448
490
|
or any(not isinstance(task_id, str) or task_id not in expected for task_id in related)
|
|
449
491
|
):
|
|
450
|
-
|
|
492
|
+
detail = (
|
|
493
|
+
"duplicate index" if index in checked_criteria else
|
|
494
|
+
"task_ids must be a non-empty list of approved top-level task IDs"
|
|
495
|
+
)
|
|
496
|
+
issues.append(
|
|
497
|
+
f"Implementation audit has invalid acceptance criterion or task references; "
|
|
498
|
+
f"acceptance index {index}: {detail}"
|
|
499
|
+
)
|
|
451
500
|
continue
|
|
452
501
|
checked_criteria.add(index)
|
|
453
502
|
if len(related) > 1:
|
|
@@ -458,7 +507,16 @@ def assess_implementation_audit(contract: dict, report: object) -> dict:
|
|
|
458
507
|
description = row.get("description")
|
|
459
508
|
paths = row.get("files") or []
|
|
460
509
|
if not isinstance(description, str) or not description.strip() or not isinstance(paths, list):
|
|
461
|
-
|
|
510
|
+
reference = f"task '{row['task_id']}'" if kind == "task" else f"acceptance index {index}"
|
|
511
|
+
invalid_fields = []
|
|
512
|
+
if not isinstance(description, str) or not description.strip():
|
|
513
|
+
invalid_fields.append("description must be non-empty text")
|
|
514
|
+
if not isinstance(paths, list):
|
|
515
|
+
invalid_fields.append("files must be a list; use files=[] when no source file is implicated")
|
|
516
|
+
issues.append(
|
|
517
|
+
"Implementation gaps require an actionable description and file list; "
|
|
518
|
+
+ reference + ": " + "; ".join(invalid_fields)
|
|
519
|
+
)
|
|
462
520
|
continue
|
|
463
521
|
if any(
|
|
464
522
|
not isinstance(path, str) or not path.strip()
|
|
@@ -1234,7 +1234,7 @@ except (ImportError, ModuleNotFoundError):
|
|
|
1234
1234
|
# DAEMON_VERSION is the protocol/logic version of the daemon code.
|
|
1235
1235
|
# Kept in sync with pyproject.toml version via bump-version.sh.
|
|
1236
1236
|
# CLIENT_TYPE identifies which packaging/distribution this daemon runs in.
|
|
1237
|
-
DAEMON_VERSION = "1.
|
|
1237
|
+
DAEMON_VERSION = "1.85.1"
|
|
1238
1238
|
|
|
1239
1239
|
|
|
1240
1240
|
def _detect_client_type() -> str:
|
|
@@ -3040,7 +3040,7 @@ def _runtime_critic_reference_candidates(
|
|
|
3040
3040
|
if isinstance(related, dict):
|
|
3041
3041
|
texts.append(str(related.get("description") or ""))
|
|
3042
3042
|
texts.extend(str(item.get("description") or "") for item in related.get("items") or [] if isinstance(item, dict))
|
|
3043
|
-
if planned.get("type")
|
|
3043
|
+
if planned.get("type") in ("design", "testing", "review"):
|
|
3044
3044
|
texts.append(json.dumps({"analysis": baseline.get("approved_analysis"),
|
|
3045
3045
|
"documents": baseline.get("analysis_documents")}, ensure_ascii=True)[:200_000])
|
|
3046
3046
|
text = re.sub(r"https?://\S+", " ", "\n".join(texts))
|
|
@@ -3063,7 +3063,7 @@ def _runtime_critic_reference_candidates(
|
|
|
3063
3063
|
php_context = any(Path(path).suffix.lower() == ".php" for path in source_paths)
|
|
3064
3064
|
directories = {Path(path).parent.as_posix() for path in source_paths if safe_path(path)} - {"."}
|
|
3065
3065
|
explicit = set()
|
|
3066
|
-
for token in re.findall(r"(?<![
|
|
3066
|
+
for token in re.findall(r"(?<![A-Za-z0-9_./\\:-])[A-Za-z0-9_.-]+(?:/[A-Za-z0-9_.-]+)+", text):
|
|
3067
3067
|
candidate = safe_path(token)
|
|
3068
3068
|
if candidate is None:
|
|
3069
3069
|
continue
|
|
@@ -4843,15 +4843,43 @@ class WorkspaceManager:
|
|
|
4843
4843
|
if branch_name == default_branch:
|
|
4844
4844
|
raise RuntimeError("Fresh analysis cannot reset the project default branch")
|
|
4845
4845
|
await self._git("check-ref-format", branch_ref, cwd=repository)
|
|
4846
|
-
|
|
4846
|
+
await self._git(
|
|
4847
|
+
"fetch", "origin", f"+{default_branch}:refs/remotes/origin/{default_branch}", cwd=repository,
|
|
4848
|
+
timeout=settings.GIT_FETCH_TIMEOUT, project_key=project_key,
|
|
4849
|
+
)
|
|
4850
|
+
tracking_ref = f"refs/remotes/origin/{branch_name}"
|
|
4851
|
+
advertised = (await self._git(
|
|
4852
|
+
"ls-remote", "--heads", "origin", branch_ref, cwd=repository,
|
|
4853
|
+
timeout=settings.GIT_FETCH_TIMEOUT, project_key=project_key,
|
|
4854
|
+
)).strip()
|
|
4855
|
+
if not advertised:
|
|
4856
|
+
await self._git("update-ref", "-d", tracking_ref, cwd=repository)
|
|
4857
|
+
logger.info("Fresh analysis branch %s is absent on origin; removed its stale tracking reference", branch_name)
|
|
4858
|
+
return
|
|
4859
|
+
remote_fields = advertised.split()
|
|
4860
|
+
if len(remote_fields) != 2 or remote_fields[1] != branch_ref or not re.fullmatch(r"[0-9a-fA-F]{40}", remote_fields[0]):
|
|
4861
|
+
raise RuntimeError("Fresh analysis requires an exact remote branch advertisement")
|
|
4862
|
+
try:
|
|
4847
4863
|
await self._git(
|
|
4848
|
-
"fetch", "origin", f"+{
|
|
4864
|
+
"fetch", "origin", f"+{branch_name}:{tracking_ref}", cwd=repository,
|
|
4849
4865
|
timeout=settings.GIT_FETCH_TIMEOUT, project_key=project_key,
|
|
4850
4866
|
)
|
|
4867
|
+
except RuntimeError:
|
|
4868
|
+
remaining = (await self._git(
|
|
4869
|
+
"ls-remote", "--heads", "origin", branch_ref, cwd=repository,
|
|
4870
|
+
timeout=settings.GIT_FETCH_TIMEOUT, project_key=project_key,
|
|
4871
|
+
)).strip()
|
|
4872
|
+
if remaining:
|
|
4873
|
+
raise
|
|
4874
|
+
await self._git("update-ref", "-d", tracking_ref, cwd=repository)
|
|
4875
|
+
logger.info("Fresh analysis branch %s disappeared during fetch; removed its stale tracking reference", branch_name)
|
|
4876
|
+
return
|
|
4851
4877
|
old_sha = (await self._git("rev-parse", "--verify", f"refs/remotes/origin/{branch_name}", cwd=repository)).strip()
|
|
4852
4878
|
default_sha = (await self._git("rev-parse", "--verify", f"refs/remotes/origin/{default_branch}", cwd=repository)).strip()
|
|
4853
4879
|
if not all(re.fullmatch(r"[0-9a-fA-F]{40}", value) for value in (old_sha, default_sha)):
|
|
4854
4880
|
raise RuntimeError("Fresh analysis requires verified remote branch commits")
|
|
4881
|
+
if old_sha != remote_fields[0]:
|
|
4882
|
+
raise RuntimeError("Remote requirement branch changed during fresh analysis preparation")
|
|
4855
4883
|
if old_sha == default_sha:
|
|
4856
4884
|
return
|
|
4857
4885
|
backup_ref = f"refs/tags/forgexa/fresh-analysis-backup/{branch_name}/{old_sha}"
|
|
@@ -13970,6 +13998,7 @@ class RuntimeDaemon:
|
|
|
13970
13998
|
|
|
13971
13999
|
after_sha = str((result.git or {}).get("commit_sha") or before_sha or "").strip()
|
|
13972
14000
|
committed_sources: dict[str, str] = {}
|
|
14001
|
+
unavailable_sources: list[dict[str, str]] = []
|
|
13973
14002
|
source_bytes = 0
|
|
13974
14003
|
coverage = {
|
|
13975
14004
|
"candidate_files": 0, "full_source_files": 0, "unreadable_files": 0,
|
|
@@ -13998,6 +14027,27 @@ class RuntimeDaemon:
|
|
|
13998
14027
|
"test-cases.json", "coverage-matrix.json", "test-evidence.json", "test-report.md",
|
|
13999
14028
|
}
|
|
14000
14029
|
))
|
|
14030
|
+
if output_dir.startswith(f"docs/requirements/{task.requirement_key}/") and ".." not in Path(output_dir).parts:
|
|
14031
|
+
verification_dir = output_dir + "/verification"
|
|
14032
|
+
try:
|
|
14033
|
+
verification_paths = await self.workspace_manager._git(
|
|
14034
|
+
"ls-tree", "-r", "--name-only", "-z", after_sha, "--", *asset_paths, verification_dir,
|
|
14035
|
+
cwd=workspace_path, timeout=10,
|
|
14036
|
+
)
|
|
14037
|
+
if len(verification_paths.encode("utf-8")) > _RUNTIME_CRITIC_SOURCE_MAX_BYTES:
|
|
14038
|
+
raise ValueError("Committed verification inventory exceeds its bounded evidence budget")
|
|
14039
|
+
existing_artifacts = set(verification_paths.split("\0"))
|
|
14040
|
+
asset_paths = [path for path in asset_paths if path in existing_artifacts]
|
|
14041
|
+
paths.extend(path for path in verification_paths.split("\0") if (
|
|
14042
|
+
path.startswith(verification_dir + "/") and ".." not in Path(path).parts
|
|
14043
|
+
and re.fullmatch(r"[A-Za-z0-9_./-]+", path)
|
|
14044
|
+
and Path(path).suffix.lower() in {".php", ".py", ".ts", ".js", ".json", ".md", ".txt"}
|
|
14045
|
+
and not _is_sensitive_local_path(path)
|
|
14046
|
+
and Path(path).name.lower() not in {"env.php", "auth.json", "credentials.json", "secrets.json", "service-account.json"}
|
|
14047
|
+
and not any(part in {".git", ".forgexa", ".aws", ".ssh", ".env"} or part.startswith(".env.") for part in Path(path).parts)
|
|
14048
|
+
))
|
|
14049
|
+
except Exception:
|
|
14050
|
+
coverage["verification_inventory_unavailable"] = True
|
|
14001
14051
|
paths.extend(asset_paths)
|
|
14002
14052
|
support_dirs = sorted({(Path(path).parent / "support").as_posix() for path in asset_paths if path.startswith("tests/")})
|
|
14003
14053
|
for directory in support_dirs[:5]:
|
|
@@ -14089,8 +14139,19 @@ class RuntimeDaemon:
|
|
|
14089
14139
|
source = await self.workspace_manager._git(
|
|
14090
14140
|
"show", f"{after_sha}:{path}", cwd=workspace_path, timeout=10,
|
|
14091
14141
|
)
|
|
14092
|
-
except Exception:
|
|
14142
|
+
except Exception as error:
|
|
14093
14143
|
coverage["unreadable_files"] += 1
|
|
14144
|
+
detail = str(error).lower()
|
|
14145
|
+
reason = "source_git_read_failed"
|
|
14146
|
+
if isinstance(error, UnicodeDecodeError):
|
|
14147
|
+
reason = "source_encoding_unavailable"
|
|
14148
|
+
elif isinstance(error, TimeoutError) or "timed out after" in detail:
|
|
14149
|
+
reason = "source_read_timeout"
|
|
14150
|
+
elif "cannot be mapped" in detail or "cannot allocate memory" in detail:
|
|
14151
|
+
reason = "source_memory_unavailable"
|
|
14152
|
+
elif "does not exist in" in detail or "exists on disk, but not in" in detail:
|
|
14153
|
+
reason = "source_path_unavailable"
|
|
14154
|
+
unavailable_sources.append({"path": path, "reason": reason})
|
|
14094
14155
|
continue
|
|
14095
14156
|
encoded_size = len(source.encode("utf-8"))
|
|
14096
14157
|
if source_bytes + encoded_size <= _RUNTIME_CRITIC_SOURCE_MAX_BYTES:
|
|
@@ -14108,7 +14169,10 @@ class RuntimeDaemon:
|
|
|
14108
14169
|
diff = (diff or "No new changes; review committed delivery instead.") + "".join(source_context)
|
|
14109
14170
|
coverage["full_source_files"] = len(committed_sources)
|
|
14110
14171
|
coverage["omitted_files"] = coverage["candidate_files"] - coverage["full_source_files"]
|
|
14111
|
-
coverage["capsule_incomplete"] =
|
|
14172
|
+
coverage["capsule_incomplete"] = (
|
|
14173
|
+
coverage["omitted_files"] > 0 or coverage.get("reference_inventory_unavailable", False)
|
|
14174
|
+
or coverage.get("verification_inventory_unavailable", False)
|
|
14175
|
+
)
|
|
14112
14176
|
result.metrics["runtime_critic_coverage"] = coverage
|
|
14113
14177
|
if not diff:
|
|
14114
14178
|
return {
|
|
@@ -14136,6 +14200,8 @@ class RuntimeDaemon:
|
|
|
14136
14200
|
work_item={},
|
|
14137
14201
|
)
|
|
14138
14202
|
review_task.input_prompt += "\n## Review Evidence Coverage\n" + json.dumps(coverage, sort_keys=True)
|
|
14203
|
+
if unavailable_sources:
|
|
14204
|
+
review_task.input_prompt += "\n## Unavailable Committed Sources\n" + json.dumps(unavailable_sources, sort_keys=True)
|
|
14139
14205
|
if coverage["capsule_incomplete"]:
|
|
14140
14206
|
review_task.input_prompt += (
|
|
14141
14207
|
"\nSome candidate files were not available as complete source. Assess the evidence you can inspect; "
|
|
@@ -14155,6 +14221,7 @@ class RuntimeDaemon:
|
|
|
14155
14221
|
"reviewed_commit_sha": after_sha,
|
|
14156
14222
|
"baseline_fingerprint": baseline.get("fingerprint") if isinstance(baseline, dict) else None,
|
|
14157
14223
|
"input_sha256": hashlib.sha256(review_task.input_prompt.encode("utf-8")).hexdigest(),
|
|
14224
|
+
"unavailable_sources": unavailable_sources,
|
|
14158
14225
|
"snapshots": [
|
|
14159
14226
|
{"path": path, "sha256": hashlib.sha256(source.encode("utf-8")).hexdigest(),
|
|
14160
14227
|
"bytes": len(source.encode("utf-8"))}
|
|
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
|