forgexa-cli 1.77.1__tar.gz → 1.77.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.77.1 → forgexa_cli-1.77.2}/PKG-INFO +1 -1
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/forgexa_cli/__init__.py +1 -1
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/forgexa_cli/agent_core.py +78 -14
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/forgexa_cli/daemon.py +9 -3
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/forgexa_cli/runtime_evidence.py +2 -2
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/forgexa_cli.egg-info/PKG-INFO +1 -1
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/pyproject.toml +1 -1
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/README.md +0 -0
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/forgexa_cli/_build_config.py +0 -0
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/forgexa_cli/_local_bind.py +0 -0
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/forgexa_cli/autoupgrade.py +0 -0
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/forgexa_cli/aws_evidence.py +0 -0
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/forgexa_cli/aws_logs_evidence.py +0 -0
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/forgexa_cli/main.py +0 -0
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/forgexa_cli/py.typed +0 -0
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/forgexa_cli/zcode_protocol.py +0 -0
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/forgexa_cli.egg-info/SOURCES.txt +0 -0
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/forgexa_cli.egg-info/dependency_links.txt +0 -0
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/forgexa_cli.egg-info/entry_points.txt +0 -0
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/forgexa_cli.egg-info/requires.txt +0 -0
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/forgexa_cli.egg-info/top_level.txt +0 -0
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/setup.cfg +0 -0
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/tests/test_auth_and_runtime_commands.py +0 -0
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/tests/test_autoupgrade.py +0 -0
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/tests/test_check_command.py +0 -0
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/tests/test_expiry_warnings_and_revoke.py +0 -0
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/tests/test_local_bind_commands.py +0 -0
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/tests/test_runtime_credentials.py +0 -0
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/tests/test_session_credentials.py +0 -0
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/tests/test_silent_install.py +0 -0
- {forgexa_cli-1.77.1 → forgexa_cli-1.77.2}/tests/test_upgrade_observability.py +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""forgexa-cli — Forgexa command-line client."""
|
|
2
|
-
__version__ = "1.77.
|
|
2
|
+
__version__ = "1.77.2"
|
|
@@ -142,33 +142,97 @@ def redact_implementation_audit(contract: dict, report: object) -> dict | None:
|
|
|
142
142
|
|
|
143
143
|
if not assess_implementation_audit(contract, report)["report_valid"]:
|
|
144
144
|
return None
|
|
145
|
+
def redact_check(row: dict) -> dict:
|
|
146
|
+
evidence = []
|
|
147
|
+
for reference in row["evidence"][:20]:
|
|
148
|
+
digest = reference.removeprefix("local-evidence-sha256:")
|
|
149
|
+
evidence.append(reference if digest != reference and len(digest) == 64
|
|
150
|
+
and all(character in "0123456789abcdef" for character in digest)
|
|
151
|
+
else "local-evidence-sha256:" + sha256(reference.encode()).hexdigest())
|
|
152
|
+
check = {"status": row["status"], "evidence": evidence}
|
|
153
|
+
if row["status"] != "covered":
|
|
154
|
+
check.update(description="Local implementation issue remains unresolved; inspect local task evidence.", files=[])
|
|
155
|
+
if row.get("plan_revision"):
|
|
156
|
+
check["plan_revision"] = {
|
|
157
|
+
"reason": "Local evidence conflicts with the approved plan",
|
|
158
|
+
"proposal": "Inspect the source in the requirement workspace and revise analysis and tasks together",
|
|
159
|
+
}
|
|
160
|
+
return check
|
|
161
|
+
|
|
145
162
|
summary = {"audit_id": contract["audit_id"], "task_checks": [], "acceptance_checks": []}
|
|
163
|
+
coding_ids = {task["id"] for task in contract.get("tasks") or [] if task.get("type") in ("coding", "fix")}
|
|
146
164
|
for key in ("task_checks", "acceptance_checks"):
|
|
147
165
|
for row in report[key]:
|
|
148
|
-
|
|
149
|
-
for reference in row["evidence"][:20]:
|
|
150
|
-
digest = reference.removeprefix("local-evidence-sha256:")
|
|
151
|
-
evidence.append(reference if digest != reference and len(digest) == 64
|
|
152
|
-
and all(character in "0123456789abcdef" for character in digest)
|
|
153
|
-
else "local-evidence-sha256:" + sha256(reference.encode()).hexdigest())
|
|
154
|
-
check = {"status": row["status"], "evidence": evidence}
|
|
166
|
+
check = redact_check(row)
|
|
155
167
|
if key == "task_checks":
|
|
156
168
|
check["task_id"] = row["task_id"]
|
|
169
|
+
if contract.get("version") == 2 and row["task_id"] in coding_ids and isinstance(row.get("implementation_check"), dict):
|
|
170
|
+
check["implementation_check"] = redact_check(row["implementation_check"])
|
|
157
171
|
else:
|
|
158
172
|
check.update(index=row["index"], task_ids=row["task_ids"])
|
|
159
|
-
if row["status"] != "covered":
|
|
160
|
-
check.update(description="Local implementation issue remains unresolved; inspect local task evidence.", files=[])
|
|
161
|
-
if row.get("plan_revision"):
|
|
162
|
-
check["plan_revision"] = {
|
|
163
|
-
"reason": "Local evidence conflicts with the approved plan",
|
|
164
|
-
"proposal": "Inspect the source in the requirement workspace and revise analysis and tasks together",
|
|
165
|
-
}
|
|
166
173
|
summary[key].append(check)
|
|
167
174
|
return summary
|
|
168
175
|
|
|
169
176
|
|
|
177
|
+
def _assess_implementation_contributions(contract: dict, report: object) -> dict:
|
|
178
|
+
original = assess_implementation_audit({**contract, "version": 1}, report)
|
|
179
|
+
if not original["report_valid"]:
|
|
180
|
+
return original
|
|
181
|
+
task_types = {task["id"]: task.get("type") for task in contract.get("tasks") or []}
|
|
182
|
+
verification_ids = [task_id for task_id, task_type in task_types.items() if task_type in ("testing", "verify")]
|
|
183
|
+
verification_statuses = {row["task_id"]: row["status"] for row in report["task_checks"]}
|
|
184
|
+
checks = []
|
|
185
|
+
issues = []
|
|
186
|
+
pending = []
|
|
187
|
+
for row in report["task_checks"]:
|
|
188
|
+
if not isinstance(row, dict) or task_types.get(row.get("task_id")) not in ("coding", "fix"):
|
|
189
|
+
checks.append(row)
|
|
190
|
+
continue
|
|
191
|
+
implementation = row.get("implementation_check")
|
|
192
|
+
if not isinstance(implementation, dict):
|
|
193
|
+
issues.append(f"Approved coding task '{row.get('task_id')}' requires an evidenced implementation_check")
|
|
194
|
+
checks.append(row)
|
|
195
|
+
continue
|
|
196
|
+
check = {"task_id": row.get("task_id"), **{
|
|
197
|
+
key: implementation[key] for key in ("status", "evidence", "description", "files", "plan_revision")
|
|
198
|
+
if key in implementation
|
|
199
|
+
}}
|
|
200
|
+
if row.get("plan_revision") is not None:
|
|
201
|
+
check["plan_revision"] = row["plan_revision"]
|
|
202
|
+
checks.append(check)
|
|
203
|
+
if implementation.get("status") == "covered" and row.get("status") == "gap":
|
|
204
|
+
issues.append(f"Approved task '{row.get('task_id')}' reports a gap despite claiming implementation coverage")
|
|
205
|
+
if implementation.get("status") == "covered" and row.get("status") in ("blocked", "unverified"):
|
|
206
|
+
if not verification_ids:
|
|
207
|
+
issues.append("Runtime acceptance is pending but the approved plan has no verification task")
|
|
208
|
+
pending.extend({
|
|
209
|
+
"task_id": task_id, "status": row.get("status"),
|
|
210
|
+
"description": row.get("description") or "Runtime acceptance remains pending",
|
|
211
|
+
"files": [], "evidence": row.get("evidence") or [],
|
|
212
|
+
} for task_id in verification_ids if verification_statuses.get(task_id) != "covered")
|
|
213
|
+
result = assess_implementation_audit({**contract, "version": 1}, {**report, "task_checks": checks})
|
|
214
|
+
result["report"] = report
|
|
215
|
+
result["issues"].extend(issues)
|
|
216
|
+
result["report_valid"] = not result["issues"]
|
|
217
|
+
result["node_complete"] = result["node_complete"] and not issues
|
|
218
|
+
result["verification_pending"] = sorted({item["task_id"] for item in pending})
|
|
219
|
+
result["findings"].extend(pending)
|
|
220
|
+
for request in original["revision_requests"]:
|
|
221
|
+
if request not in result["revision_requests"]:
|
|
222
|
+
result["revision_requests"].append(request)
|
|
223
|
+
result["followup_task_ids"] = sorted({finding["task_id"] for finding in result["findings"]})
|
|
224
|
+
if issues or pending:
|
|
225
|
+
result["status"] = "incomplete"
|
|
226
|
+
if result["report_valid"] and result["revision_requests"]:
|
|
227
|
+
result["status"] = "plan_revision_required"
|
|
228
|
+
result["node_complete"] = False
|
|
229
|
+
return result
|
|
230
|
+
|
|
231
|
+
|
|
170
232
|
def assess_implementation_audit(contract: dict, report: object) -> dict:
|
|
171
233
|
"""Validate implementation coverage independently of later runtime acceptance."""
|
|
234
|
+
if contract.get("version") == 2:
|
|
235
|
+
return _assess_implementation_contributions(contract, report)
|
|
172
236
|
issues: list[str] = []
|
|
173
237
|
findings: list[dict] = []
|
|
174
238
|
result = {
|
|
@@ -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.77.
|
|
1237
|
+
DAEMON_VERSION = "1.77.2"
|
|
1238
1238
|
|
|
1239
1239
|
|
|
1240
1240
|
def _detect_client_type() -> str:
|
|
@@ -9915,6 +9915,7 @@ class ServerConnection:
|
|
|
9915
9915
|
"acceptance_verification": {
|
|
9916
9916
|
"protocol_version": runtime_evidence.DEFERRED_ACCEPTANCE_VERIFICATION_PROTOCOL_VERSION,
|
|
9917
9917
|
},
|
|
9918
|
+
"implementation_audit": {"protocol_version": 2},
|
|
9918
9919
|
"task_attachment_staging": {"protocol_version": 1, "runtime_proxy": True, "image_agents": ["claude"]},
|
|
9919
9920
|
},
|
|
9920
9921
|
"server_url": self.server_url,
|
|
@@ -10338,6 +10339,7 @@ class ServerConnection:
|
|
|
10338
10339
|
"acceptance_verification": {
|
|
10339
10340
|
"protocol_version": runtime_evidence.DEFERRED_ACCEPTANCE_VERIFICATION_PROTOCOL_VERSION,
|
|
10340
10341
|
},
|
|
10342
|
+
"implementation_audit": {"protocol_version": 2},
|
|
10341
10343
|
"local_evidence": {"protocol_version": 1, "structured_summary": True},
|
|
10342
10344
|
"task_attachment_staging": {"protocol_version": 1, "runtime_proxy": True, "image_agents": ["claude"]},
|
|
10343
10345
|
},
|
|
@@ -13875,7 +13877,11 @@ class RuntimeDaemon:
|
|
|
13875
13877
|
issues: list[str] = []
|
|
13876
13878
|
node_type = task.node_type
|
|
13877
13879
|
input_data = task.input_data or {}
|
|
13878
|
-
|
|
13880
|
+
fix_context = input_data.get("fix_context")
|
|
13881
|
+
if node_type == "testing" or (
|
|
13882
|
+
node_type == "fix" and input_data.get("task") == "phase_remediation_fix"
|
|
13883
|
+
and isinstance(fix_context, dict) and fix_context.get("failed_node_type") == "testing"
|
|
13884
|
+
):
|
|
13879
13885
|
node_type = "testing"
|
|
13880
13886
|
req_type = input_data.get("requirement_type", "feature")
|
|
13881
13887
|
verification_work_item = bool(input_data.get("verification_work_item"))
|
|
@@ -13897,7 +13903,7 @@ class RuntimeDaemon:
|
|
|
13897
13903
|
result.error = "Approved analysis and task plan require revision before implementation can continue"
|
|
13898
13904
|
issues.append(result.error)
|
|
13899
13905
|
elif not audit_result["report_valid"] or not audit_result["node_complete"]:
|
|
13900
|
-
issues.extend(audit_result["issues"] or audit_result
|
|
13906
|
+
issues.extend(audit_result["issues"] or audit_result.get("blocking_reasons") or [
|
|
13901
13907
|
"The selected approved task still has implementation gaps",
|
|
13902
13908
|
])
|
|
13903
13909
|
|
|
@@ -15,7 +15,7 @@ from pathlib import Path
|
|
|
15
15
|
from typing import Any
|
|
16
16
|
|
|
17
17
|
RUNTIME_EVIDENCE_PLAN_VERSION = 1
|
|
18
|
-
DEFERRED_ACCEPTANCE_VERIFICATION_PROTOCOL_VERSION =
|
|
18
|
+
DEFERRED_ACCEPTANCE_VERIFICATION_PROTOCOL_VERSION = 3
|
|
19
19
|
TEST_COVERAGE_CHECK_KIND = "test_coverage"
|
|
20
20
|
STATIC_CHECK_KIND = "static_check"
|
|
21
21
|
TYPE_CHECK_KIND = "type_check"
|
|
@@ -804,7 +804,7 @@ def validate_deferred_acceptance_criteria(
|
|
|
804
804
|
or policy.get("enforcement") != "enforce"
|
|
805
805
|
or not isinstance(e2e_policy, dict)
|
|
806
806
|
or type(contract.get("version")) is not int
|
|
807
|
-
or contract["version"] not in (1, DEFERRED_ACCEPTANCE_VERIFICATION_PROTOCOL_VERSION)
|
|
807
|
+
or contract["version"] not in (1, 2, DEFERRED_ACCEPTANCE_VERIFICATION_PROTOCOL_VERSION)
|
|
808
808
|
or contract.get("execution_phase") != "verification_final"
|
|
809
809
|
or not isinstance(snapshot.get("fingerprint"), str)
|
|
810
810
|
or contract.get("qa_test_level_policy_fingerprint") != snapshot["fingerprint"]
|
|
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
|