forgexa-cli 1.47.0__tar.gz → 1.47.2__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.47.0 → forgexa_cli-1.47.2}/PKG-INFO +1 -1
- {forgexa_cli-1.47.0 → forgexa_cli-1.47.2}/forgexa_cli/__init__.py +1 -1
- {forgexa_cli-1.47.0 → forgexa_cli-1.47.2}/forgexa_cli/daemon.py +326 -4
- forgexa_cli-1.47.2/forgexa_cli/runtime_evidence.py +1452 -0
- {forgexa_cli-1.47.0 → forgexa_cli-1.47.2}/forgexa_cli.egg-info/PKG-INFO +1 -1
- {forgexa_cli-1.47.0 → forgexa_cli-1.47.2}/pyproject.toml +1 -1
- forgexa_cli-1.47.0/forgexa_cli/runtime_evidence.py +0 -583
- {forgexa_cli-1.47.0 → forgexa_cli-1.47.2}/README.md +0 -0
- {forgexa_cli-1.47.0 → forgexa_cli-1.47.2}/forgexa_cli/_build_config.py +0 -0
- {forgexa_cli-1.47.0 → forgexa_cli-1.47.2}/forgexa_cli/_local_bind.py +0 -0
- {forgexa_cli-1.47.0 → forgexa_cli-1.47.2}/forgexa_cli/agent_core.py +0 -0
- {forgexa_cli-1.47.0 → forgexa_cli-1.47.2}/forgexa_cli/autoupgrade.py +0 -0
- {forgexa_cli-1.47.0 → forgexa_cli-1.47.2}/forgexa_cli/main.py +0 -0
- {forgexa_cli-1.47.0 → forgexa_cli-1.47.2}/forgexa_cli/py.typed +0 -0
- {forgexa_cli-1.47.0 → forgexa_cli-1.47.2}/forgexa_cli.egg-info/SOURCES.txt +0 -0
- {forgexa_cli-1.47.0 → forgexa_cli-1.47.2}/forgexa_cli.egg-info/dependency_links.txt +0 -0
- {forgexa_cli-1.47.0 → forgexa_cli-1.47.2}/forgexa_cli.egg-info/entry_points.txt +0 -0
- {forgexa_cli-1.47.0 → forgexa_cli-1.47.2}/forgexa_cli.egg-info/requires.txt +0 -0
- {forgexa_cli-1.47.0 → forgexa_cli-1.47.2}/forgexa_cli.egg-info/top_level.txt +0 -0
- {forgexa_cli-1.47.0 → forgexa_cli-1.47.2}/setup.cfg +0 -0
- {forgexa_cli-1.47.0 → forgexa_cli-1.47.2}/tests/test_auth_and_runtime_commands.py +0 -0
- {forgexa_cli-1.47.0 → forgexa_cli-1.47.2}/tests/test_autoupgrade.py +0 -0
- {forgexa_cli-1.47.0 → forgexa_cli-1.47.2}/tests/test_check_command.py +0 -0
- {forgexa_cli-1.47.0 → forgexa_cli-1.47.2}/tests/test_expiry_warnings_and_revoke.py +0 -0
- {forgexa_cli-1.47.0 → forgexa_cli-1.47.2}/tests/test_local_bind_commands.py +0 -0
- {forgexa_cli-1.47.0 → forgexa_cli-1.47.2}/tests/test_runtime_credentials.py +0 -0
- {forgexa_cli-1.47.0 → forgexa_cli-1.47.2}/tests/test_session_credentials.py +0 -0
- {forgexa_cli-1.47.0 → forgexa_cli-1.47.2}/tests/test_silent_install.py +0 -0
- {forgexa_cli-1.47.0 → forgexa_cli-1.47.2}/tests/test_upgrade_observability.py +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""forgexa-cli — Forgexa command-line client."""
|
|
2
|
-
__version__ = "1.47.
|
|
2
|
+
__version__ = "1.47.2"
|
|
@@ -1075,7 +1075,7 @@ except (ImportError, ModuleNotFoundError):
|
|
|
1075
1075
|
# DAEMON_VERSION is the protocol/logic version of the daemon code.
|
|
1076
1076
|
# Kept in sync with pyproject.toml version via bump-version.sh.
|
|
1077
1077
|
# CLIENT_TYPE identifies which packaging/distribution this daemon runs in.
|
|
1078
|
-
DAEMON_VERSION = "1.47.
|
|
1078
|
+
DAEMON_VERSION = "1.47.2"
|
|
1079
1079
|
|
|
1080
1080
|
|
|
1081
1081
|
def _detect_client_type() -> str:
|
|
@@ -5992,8 +5992,13 @@ class ProcessManager:
|
|
|
5992
5992
|
if not match:
|
|
5993
5993
|
return None
|
|
5994
5994
|
missing_model, suggested_model = match.groups()
|
|
5995
|
-
|
|
5995
|
+
missing_model = missing_model.strip()
|
|
5996
|
+
if missing_model.lower() == suggested_model.lower():
|
|
5996
5997
|
return None
|
|
5998
|
+
if "/" not in suggested_model and "/" in missing_model:
|
|
5999
|
+
provider = missing_model.split("/", 1)[0]
|
|
6000
|
+
if provider:
|
|
6001
|
+
suggested_model = f"{provider}/{suggested_model}"
|
|
5997
6002
|
return suggested_model
|
|
5998
6003
|
|
|
5999
6004
|
async def _run_opencode(
|
|
@@ -10446,6 +10451,24 @@ class RuntimeDaemon:
|
|
|
10446
10451
|
result,
|
|
10447
10452
|
before_sha=node_before_sha,
|
|
10448
10453
|
)
|
|
10454
|
+
await self._collect_runtime_type_evidence(
|
|
10455
|
+
workspace_path,
|
|
10456
|
+
task,
|
|
10457
|
+
result,
|
|
10458
|
+
before_sha=node_before_sha,
|
|
10459
|
+
)
|
|
10460
|
+
await self._collect_runtime_security_evidence(
|
|
10461
|
+
workspace_path,
|
|
10462
|
+
task,
|
|
10463
|
+
result,
|
|
10464
|
+
before_sha=node_before_sha,
|
|
10465
|
+
)
|
|
10466
|
+
await self._collect_runtime_acceptance_criteria_coverage_evidence(
|
|
10467
|
+
workspace_path,
|
|
10468
|
+
task,
|
|
10469
|
+
result,
|
|
10470
|
+
before_sha=node_before_sha,
|
|
10471
|
+
)
|
|
10449
10472
|
|
|
10450
10473
|
# 6. Report completion (include actual agent used if different from requested)
|
|
10451
10474
|
result.metrics["actual_agent"] = agent.agent_id
|
|
@@ -13275,11 +13298,15 @@ class RuntimeDaemon:
|
|
|
13275
13298
|
"summary": "The committed source revision is unavailable for evidence collection.",
|
|
13276
13299
|
}
|
|
13277
13300
|
else:
|
|
13301
|
+
evidence_check = {
|
|
13302
|
+
**check,
|
|
13303
|
+
"output_dir": self._resolve_task_output_dir(task),
|
|
13304
|
+
}
|
|
13278
13305
|
run = await self._run_runtime_evidence_in_worktree(
|
|
13279
13306
|
workspace_path,
|
|
13280
13307
|
task,
|
|
13281
13308
|
after_sha,
|
|
13282
|
-
|
|
13309
|
+
evidence_check,
|
|
13283
13310
|
runtime_evidence.run_test_coverage_evidence,
|
|
13284
13311
|
)
|
|
13285
13312
|
|
|
@@ -13327,7 +13354,10 @@ class RuntimeDaemon:
|
|
|
13327
13354
|
"artifact_refs": [artifact_ref],
|
|
13328
13355
|
"runtime_evidence": evidence_identity,
|
|
13329
13356
|
}
|
|
13330
|
-
for key in (
|
|
13357
|
+
for key in (
|
|
13358
|
+
"test_pass_rate", "passed", "failed", "skipped", "errors", "total",
|
|
13359
|
+
"failed_tests", "test_case_results", "exit_code",
|
|
13360
|
+
):
|
|
13331
13361
|
if key in run:
|
|
13332
13362
|
test_observation[key] = run[key]
|
|
13333
13363
|
if run.get("failure_reason"):
|
|
@@ -13491,6 +13521,298 @@ class RuntimeDaemon:
|
|
|
13491
13521
|
observation["collection_failure_reason"] = run["failure_reason"]
|
|
13492
13522
|
result.observations.append(observation)
|
|
13493
13523
|
|
|
13524
|
+
async def _collect_runtime_type_evidence(
|
|
13525
|
+
self,
|
|
13526
|
+
workspace_path: Path,
|
|
13527
|
+
task: TaskInfo,
|
|
13528
|
+
result: TaskResult,
|
|
13529
|
+
*,
|
|
13530
|
+
before_sha: str,
|
|
13531
|
+
) -> None:
|
|
13532
|
+
"""Run a frozen mypy check in a disposable post-commit worktree."""
|
|
13533
|
+
if (
|
|
13534
|
+
task.workspace_resolution != "cloud_worktree"
|
|
13535
|
+
or task.node_type not in ("testing", "fix")
|
|
13536
|
+
or not isinstance(task.runtime_evidence_plan, dict)
|
|
13537
|
+
):
|
|
13538
|
+
return
|
|
13539
|
+
|
|
13540
|
+
plan = task.runtime_evidence_plan
|
|
13541
|
+
plan_hash = plan.get("plan_hash")
|
|
13542
|
+
checks = plan.get("checks")
|
|
13543
|
+
check = next(
|
|
13544
|
+
(
|
|
13545
|
+
item for item in checks
|
|
13546
|
+
if isinstance(item, dict) and item.get("kind") == runtime_evidence.TYPE_CHECK_KIND
|
|
13547
|
+
),
|
|
13548
|
+
None,
|
|
13549
|
+
) if isinstance(checks, list) else None
|
|
13550
|
+
if not isinstance(check, dict):
|
|
13551
|
+
return
|
|
13552
|
+
after_sha = str((result.git or {}).get("commit_sha") or "").strip()
|
|
13553
|
+
if (
|
|
13554
|
+
plan.get("version") != runtime_evidence.RUNTIME_EVIDENCE_PLAN_VERSION
|
|
13555
|
+
or not isinstance(plan_hash, str)
|
|
13556
|
+
or re.fullmatch(r"[0-9a-f]{64}", plan_hash) is None
|
|
13557
|
+
):
|
|
13558
|
+
run = {
|
|
13559
|
+
"status": "invalid_plan",
|
|
13560
|
+
"collection_status": "unavailable",
|
|
13561
|
+
"failure_reason": "invalid_frozen_plan",
|
|
13562
|
+
"summary": "The frozen type-check evidence plan is invalid.",
|
|
13563
|
+
}
|
|
13564
|
+
elif re.fullmatch(r"[0-9a-fA-F]{40}", after_sha) is None:
|
|
13565
|
+
run = {
|
|
13566
|
+
"status": "source_commit_unavailable",
|
|
13567
|
+
"collection_status": "unavailable",
|
|
13568
|
+
"failure_reason": "source_commit_unavailable",
|
|
13569
|
+
"summary": "The committed source revision is unavailable for type checking.",
|
|
13570
|
+
}
|
|
13571
|
+
else:
|
|
13572
|
+
run = await self._run_runtime_evidence_in_worktree(
|
|
13573
|
+
workspace_path,
|
|
13574
|
+
task,
|
|
13575
|
+
after_sha,
|
|
13576
|
+
check,
|
|
13577
|
+
runtime_evidence.run_type_check_evidence,
|
|
13578
|
+
)
|
|
13579
|
+
|
|
13580
|
+
source_commit_range = {"after_sha": after_sha} if after_sha else {}
|
|
13581
|
+
if re.fullmatch(r"[0-9a-fA-F]{40}", before_sha or ""):
|
|
13582
|
+
source_commit_range["before_sha"] = before_sha
|
|
13583
|
+
report = {
|
|
13584
|
+
"version": runtime_evidence.RUNTIME_EVIDENCE_PLAN_VERSION,
|
|
13585
|
+
"plan_hash": plan_hash if isinstance(plan_hash, str) else "",
|
|
13586
|
+
"execution_attempt_id": task.execution_attempt_id,
|
|
13587
|
+
"check_kind": runtime_evidence.TYPE_CHECK_KIND,
|
|
13588
|
+
"command": check.get("command"),
|
|
13589
|
+
"source_commit_range": source_commit_range,
|
|
13590
|
+
"result": run,
|
|
13591
|
+
}
|
|
13592
|
+
report_content = json.dumps(report, sort_keys=True, separators=(",", ":"))
|
|
13593
|
+
report_checksum = hashlib.sha256(report_content.encode("utf-8")).hexdigest()
|
|
13594
|
+
artifact_path = (
|
|
13595
|
+
f".forgexa/evidence/{task.task_id}/{after_sha[:12] or 'unavailable'}/"
|
|
13596
|
+
"type-check.json"
|
|
13597
|
+
)
|
|
13598
|
+
artifact_ref = {"path": artifact_path, "checksum": report_checksum}
|
|
13599
|
+
result.artifacts.append({
|
|
13600
|
+
"path": artifact_path,
|
|
13601
|
+
"content": report_content,
|
|
13602
|
+
"content_hash": report_checksum,
|
|
13603
|
+
"type": "application/vnd.forgexa.gate-evidence+json",
|
|
13604
|
+
"system_generated": True,
|
|
13605
|
+
"artifact_role": "gate_evidence_report",
|
|
13606
|
+
})
|
|
13607
|
+
|
|
13608
|
+
evidence_identity = {
|
|
13609
|
+
"version": runtime_evidence.RUNTIME_EVIDENCE_PLAN_VERSION,
|
|
13610
|
+
"plan_hash": report["plan_hash"],
|
|
13611
|
+
"check_kind": runtime_evidence.TYPE_CHECK_KIND,
|
|
13612
|
+
"execution_attempt_id": task.execution_attempt_id,
|
|
13613
|
+
"source_commit_range": source_commit_range,
|
|
13614
|
+
}
|
|
13615
|
+
observation = {
|
|
13616
|
+
"type": "lint_results",
|
|
13617
|
+
"collection_status": run.get("collection_status"),
|
|
13618
|
+
"summary": run.get("summary"),
|
|
13619
|
+
"lint_tool": "mypy",
|
|
13620
|
+
"lint_command": check.get("command"),
|
|
13621
|
+
"command": check.get("command"),
|
|
13622
|
+
"artifact_refs": [artifact_ref],
|
|
13623
|
+
"runtime_evidence": evidence_identity,
|
|
13624
|
+
}
|
|
13625
|
+
for key in ("lint_score", "issues", "type_errors", "truncated_count"):
|
|
13626
|
+
if key in run:
|
|
13627
|
+
observation[key] = run[key]
|
|
13628
|
+
if run.get("failure_reason"):
|
|
13629
|
+
observation["collection_failure_reason"] = run["failure_reason"]
|
|
13630
|
+
result.observations.append(observation)
|
|
13631
|
+
|
|
13632
|
+
async def _collect_runtime_security_evidence(
|
|
13633
|
+
self,
|
|
13634
|
+
workspace_path: Path,
|
|
13635
|
+
task: TaskInfo,
|
|
13636
|
+
result: TaskResult,
|
|
13637
|
+
*,
|
|
13638
|
+
before_sha: str,
|
|
13639
|
+
) -> None:
|
|
13640
|
+
"""Run a frozen Bandit scan in a disposable post-commit worktree."""
|
|
13641
|
+
if (
|
|
13642
|
+
task.workspace_resolution != "cloud_worktree"
|
|
13643
|
+
or task.node_type not in ("testing", "fix")
|
|
13644
|
+
or not isinstance(task.runtime_evidence_plan, dict)
|
|
13645
|
+
):
|
|
13646
|
+
return
|
|
13647
|
+
|
|
13648
|
+
plan = task.runtime_evidence_plan
|
|
13649
|
+
plan_hash = plan.get("plan_hash")
|
|
13650
|
+
checks = plan.get("checks")
|
|
13651
|
+
check = next(
|
|
13652
|
+
(
|
|
13653
|
+
item for item in checks
|
|
13654
|
+
if isinstance(item, dict) and item.get("kind") == runtime_evidence.SECURITY_CHECK_KIND
|
|
13655
|
+
),
|
|
13656
|
+
None,
|
|
13657
|
+
) if isinstance(checks, list) else None
|
|
13658
|
+
if not isinstance(check, dict):
|
|
13659
|
+
return
|
|
13660
|
+
after_sha = str((result.git or {}).get("commit_sha") or "").strip()
|
|
13661
|
+
if (
|
|
13662
|
+
plan.get("version") != runtime_evidence.RUNTIME_EVIDENCE_PLAN_VERSION
|
|
13663
|
+
or not isinstance(plan_hash, str)
|
|
13664
|
+
or re.fullmatch(r"[0-9a-f]{64}", plan_hash) is None
|
|
13665
|
+
):
|
|
13666
|
+
run = {
|
|
13667
|
+
"status": "invalid_plan",
|
|
13668
|
+
"collection_status": "unavailable",
|
|
13669
|
+
"failure_reason": "invalid_frozen_plan",
|
|
13670
|
+
"summary": "The frozen security-check evidence plan is invalid.",
|
|
13671
|
+
}
|
|
13672
|
+
elif re.fullmatch(r"[0-9a-fA-F]{40}", after_sha) is None:
|
|
13673
|
+
run = {
|
|
13674
|
+
"status": "source_commit_unavailable",
|
|
13675
|
+
"collection_status": "unavailable",
|
|
13676
|
+
"failure_reason": "source_commit_unavailable",
|
|
13677
|
+
"summary": "The committed source revision is unavailable for security scanning.",
|
|
13678
|
+
}
|
|
13679
|
+
else:
|
|
13680
|
+
run = await self._run_runtime_evidence_in_worktree(
|
|
13681
|
+
workspace_path,
|
|
13682
|
+
task,
|
|
13683
|
+
after_sha,
|
|
13684
|
+
check,
|
|
13685
|
+
runtime_evidence.run_security_check_evidence,
|
|
13686
|
+
)
|
|
13687
|
+
|
|
13688
|
+
source_commit_range = {"after_sha": after_sha} if after_sha else {}
|
|
13689
|
+
if re.fullmatch(r"[0-9a-fA-F]{40}", before_sha or ""):
|
|
13690
|
+
source_commit_range["before_sha"] = before_sha
|
|
13691
|
+
report = {
|
|
13692
|
+
"version": runtime_evidence.RUNTIME_EVIDENCE_PLAN_VERSION,
|
|
13693
|
+
"plan_hash": plan_hash if isinstance(plan_hash, str) else "",
|
|
13694
|
+
"execution_attempt_id": task.execution_attempt_id,
|
|
13695
|
+
"check_kind": runtime_evidence.SECURITY_CHECK_KIND,
|
|
13696
|
+
"command": check.get("command"),
|
|
13697
|
+
"source_commit_range": source_commit_range,
|
|
13698
|
+
"result": run,
|
|
13699
|
+
}
|
|
13700
|
+
report_content = json.dumps(report, sort_keys=True, separators=(",", ":"))
|
|
13701
|
+
report_checksum = hashlib.sha256(report_content.encode("utf-8")).hexdigest()
|
|
13702
|
+
artifact_path = (
|
|
13703
|
+
f".forgexa/evidence/{task.task_id}/{after_sha[:12] or 'unavailable'}/"
|
|
13704
|
+
"security-check.json"
|
|
13705
|
+
)
|
|
13706
|
+
artifact_ref = {"path": artifact_path, "checksum": report_checksum}
|
|
13707
|
+
result.artifacts.append({
|
|
13708
|
+
"path": artifact_path,
|
|
13709
|
+
"content": report_content,
|
|
13710
|
+
"content_hash": report_checksum,
|
|
13711
|
+
"type": "application/vnd.forgexa.gate-evidence+json",
|
|
13712
|
+
"system_generated": True,
|
|
13713
|
+
"artifact_role": "gate_evidence_report",
|
|
13714
|
+
})
|
|
13715
|
+
|
|
13716
|
+
evidence_identity = {
|
|
13717
|
+
"version": runtime_evidence.RUNTIME_EVIDENCE_PLAN_VERSION,
|
|
13718
|
+
"plan_hash": report["plan_hash"],
|
|
13719
|
+
"check_kind": runtime_evidence.SECURITY_CHECK_KIND,
|
|
13720
|
+
"execution_attempt_id": task.execution_attempt_id,
|
|
13721
|
+
"source_commit_range": source_commit_range,
|
|
13722
|
+
}
|
|
13723
|
+
observation = {
|
|
13724
|
+
"type": "security_scan",
|
|
13725
|
+
"collection_status": run.get("collection_status"),
|
|
13726
|
+
"summary": run.get("summary"),
|
|
13727
|
+
"scanner": "bandit",
|
|
13728
|
+
"command": check.get("command"),
|
|
13729
|
+
"artifact_refs": [artifact_ref],
|
|
13730
|
+
"runtime_evidence": evidence_identity,
|
|
13731
|
+
}
|
|
13732
|
+
for key in ("security_scan_score", "vulnerabilities", "truncated_count"):
|
|
13733
|
+
if key in run:
|
|
13734
|
+
observation[key] = run[key]
|
|
13735
|
+
if run.get("failure_reason"):
|
|
13736
|
+
observation["collection_failure_reason"] = run["failure_reason"]
|
|
13737
|
+
result.observations.append(observation)
|
|
13738
|
+
|
|
13739
|
+
async def _collect_runtime_acceptance_criteria_coverage_evidence(
|
|
13740
|
+
self,
|
|
13741
|
+
workspace_path: Path,
|
|
13742
|
+
task: TaskInfo,
|
|
13743
|
+
result: TaskResult,
|
|
13744
|
+
*,
|
|
13745
|
+
before_sha: str,
|
|
13746
|
+
) -> None:
|
|
13747
|
+
"""Bind generated testing artifacts to the committed testing attempt."""
|
|
13748
|
+
if task.workspace_resolution != "cloud_worktree" or task.node_type != "testing":
|
|
13749
|
+
return
|
|
13750
|
+
|
|
13751
|
+
after_sha = str((result.git or {}).get("commit_sha") or "").strip()
|
|
13752
|
+
check = {"output_dir": self._resolve_task_output_dir(task)}
|
|
13753
|
+
if re.fullmatch(r"[0-9a-fA-F]{40}", after_sha) is None:
|
|
13754
|
+
run = {
|
|
13755
|
+
"status": "source_commit_unavailable",
|
|
13756
|
+
"collection_status": "unavailable",
|
|
13757
|
+
"failure_reason": "source_commit_unavailable",
|
|
13758
|
+
"coverage_links": [],
|
|
13759
|
+
"source_artifacts": [],
|
|
13760
|
+
"summary": "The committed source revision is unavailable for acceptance-criteria evidence.",
|
|
13761
|
+
}
|
|
13762
|
+
else:
|
|
13763
|
+
run = await self._run_runtime_evidence_in_worktree(
|
|
13764
|
+
workspace_path,
|
|
13765
|
+
task,
|
|
13766
|
+
after_sha,
|
|
13767
|
+
check,
|
|
13768
|
+
runtime_evidence.run_acceptance_criteria_coverage_evidence,
|
|
13769
|
+
)
|
|
13770
|
+
run.setdefault("coverage_links", [])
|
|
13771
|
+
run.setdefault("source_artifacts", [])
|
|
13772
|
+
|
|
13773
|
+
source_commit_range = {"after_sha": after_sha} if after_sha else {}
|
|
13774
|
+
if re.fullmatch(r"[0-9a-fA-F]{40}", before_sha or ""):
|
|
13775
|
+
source_commit_range["before_sha"] = before_sha
|
|
13776
|
+
report = {
|
|
13777
|
+
"version": runtime_evidence.RUNTIME_EVIDENCE_PLAN_VERSION,
|
|
13778
|
+
"execution_attempt_id": task.execution_attempt_id,
|
|
13779
|
+
"check_kind": runtime_evidence.ACCEPTANCE_CRITERIA_COVERAGE_CHECK_KIND,
|
|
13780
|
+
"source_commit_range": source_commit_range,
|
|
13781
|
+
"result": run,
|
|
13782
|
+
}
|
|
13783
|
+
report_content = json.dumps(report, sort_keys=True, separators=(",", ":"))
|
|
13784
|
+
report_checksum = hashlib.sha256(report_content.encode("utf-8")).hexdigest()
|
|
13785
|
+
artifact_path = (
|
|
13786
|
+
f".forgexa/evidence/{task.task_id}/{after_sha[:12] or 'unavailable'}/"
|
|
13787
|
+
"acceptance-criteria.json"
|
|
13788
|
+
)
|
|
13789
|
+
artifact_ref = {"path": artifact_path, "checksum": report_checksum}
|
|
13790
|
+
result.artifacts.append({
|
|
13791
|
+
"path": artifact_path,
|
|
13792
|
+
"content": report_content,
|
|
13793
|
+
"content_hash": report_checksum,
|
|
13794
|
+
"type": "application/vnd.forgexa.gate-evidence+json",
|
|
13795
|
+
"system_generated": True,
|
|
13796
|
+
"artifact_role": "gate_evidence_report",
|
|
13797
|
+
})
|
|
13798
|
+
observation = {
|
|
13799
|
+
"type": runtime_evidence.ACCEPTANCE_CRITERIA_COVERAGE_CHECK_KIND,
|
|
13800
|
+
"collection_status": run.get("collection_status"),
|
|
13801
|
+
"summary": run.get("summary"),
|
|
13802
|
+
"artifact_refs": [artifact_ref],
|
|
13803
|
+
"runtime_evidence": {
|
|
13804
|
+
"version": runtime_evidence.RUNTIME_EVIDENCE_PLAN_VERSION,
|
|
13805
|
+
"check_kind": runtime_evidence.ACCEPTANCE_CRITERIA_COVERAGE_CHECK_KIND,
|
|
13806
|
+
"execution_attempt_id": task.execution_attempt_id,
|
|
13807
|
+
"source_commit_range": source_commit_range,
|
|
13808
|
+
},
|
|
13809
|
+
"coverage_links": run.get("coverage_links", []),
|
|
13810
|
+
"source_artifacts": run.get("source_artifacts", []),
|
|
13811
|
+
}
|
|
13812
|
+
if run.get("failure_reason"):
|
|
13813
|
+
observation["collection_failure_reason"] = run["failure_reason"]
|
|
13814
|
+
result.observations.append(observation)
|
|
13815
|
+
|
|
13494
13816
|
async def _collect_staged_diff_stats(self, cwd: Path) -> dict:
|
|
13495
13817
|
"""Collect staged diff stats for building a rich commit message."""
|
|
13496
13818
|
status_map: dict[str, str] = {}
|