forgexa-cli 1.84.4__tar.gz → 1.85.0__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.0}/PKG-INFO +1 -1
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/forgexa_cli/__init__.py +1 -1
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/forgexa_cli/agent_core.py +32 -4
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/forgexa_cli/daemon.py +31 -3
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/forgexa_cli.egg-info/PKG-INFO +1 -1
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/pyproject.toml +1 -1
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/README.md +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/forgexa_cli/_build_config.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/forgexa_cli/_local_bind.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/forgexa_cli/autoupgrade.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/forgexa_cli/aws_evidence.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/forgexa_cli/aws_logs_evidence.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/forgexa_cli/main.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/forgexa_cli/observability_contract.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/forgexa_cli/observability_evidence.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/forgexa_cli/py.typed +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/forgexa_cli/runtime_evidence.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/forgexa_cli/zcode_protocol.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/forgexa_cli.egg-info/SOURCES.txt +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/forgexa_cli.egg-info/dependency_links.txt +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/forgexa_cli.egg-info/entry_points.txt +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/forgexa_cli.egg-info/requires.txt +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/forgexa_cli.egg-info/top_level.txt +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/setup.cfg +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/tests/test_auth_and_runtime_commands.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/tests/test_autoupgrade.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/tests/test_check_command.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/tests/test_expiry_warnings_and_revoke.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/tests/test_local_bind_commands.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/tests/test_publish_script.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/tests/test_runtime_credentials.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/tests/test_session_credentials.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/tests/test_silent_install.py +0 -0
- {forgexa_cli-1.84.4 → forgexa_cli-1.85.0}/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.0"
|
|
@@ -305,20 +305,39 @@ def _assess_implementation_contributions(contract: dict, report: object) -> dict
|
|
|
305
305
|
and len(criteria) == len(reserved_ids)
|
|
306
306
|
and reserved_ids == [criterion.get("id") for criterion in criteria if isinstance(criterion, dict)]
|
|
307
307
|
):
|
|
308
|
+
index_map = deferred_contract.get("acceptance_criteria_indices")
|
|
309
|
+
expected_indices = {identifier: index for index, identifier in enumerate(reserved_ids, 1)}
|
|
310
|
+
valid_index_map = (
|
|
311
|
+
isinstance(index_map, dict) and set(index_map) == set(expected_indices)
|
|
312
|
+
and all(type(value) is int and value == expected_indices[identifier] for identifier, value in index_map.items())
|
|
313
|
+
)
|
|
308
314
|
for criterion_id in reserved_ids:
|
|
309
315
|
match = re.fullmatch(r"AC-([0-9]+)", criterion_id)
|
|
310
|
-
|
|
316
|
+
index = index_map[criterion_id] if valid_index_map else int(match.group(1)) if index_map is None and match else 0
|
|
317
|
+
if not 1 <= index <= len(contract.get("acceptance_criteria") or []):
|
|
311
318
|
reserved_indexes.clear()
|
|
312
319
|
break
|
|
313
|
-
reserved_indexes.add(
|
|
320
|
+
reserved_indexes.add(index)
|
|
314
321
|
if len(reserved_indexes) != len(reserved_ids):
|
|
315
322
|
reserved_indexes.clear()
|
|
316
323
|
acceptance_checks = []
|
|
317
324
|
deferred_acceptance = []
|
|
325
|
+
subject = contract.get("subject_task_id")
|
|
326
|
+
prepared_verifier = (
|
|
327
|
+
not contract.get("final_verification") and task_types.get(subject) in ("testing", "verify")
|
|
328
|
+
and implementation_statuses.get(subject) == "covered"
|
|
329
|
+
)
|
|
318
330
|
for row in report["acceptance_checks"]:
|
|
319
331
|
if (
|
|
320
332
|
row["index"] in reserved_indexes and row["status"] == "unverified" and row.get("plan_revision") is None
|
|
321
|
-
and
|
|
333
|
+
and (
|
|
334
|
+
all(implementation_statuses.get(task_id) == "covered" for task_id in row["task_ids"])
|
|
335
|
+
or prepared_verifier and subject in row["task_ids"] and all(
|
|
336
|
+
implementation_statuses.get(task_id) == "covered"
|
|
337
|
+
or task_id not in implementation_statuses and task_types.get(task_id) in ("coding", "fix")
|
|
338
|
+
for task_id in row["task_ids"]
|
|
339
|
+
)
|
|
340
|
+
)
|
|
322
341
|
):
|
|
323
342
|
deferred_acceptance.append(row["index"])
|
|
324
343
|
continue
|
|
@@ -458,7 +477,16 @@ def assess_implementation_audit(contract: dict, report: object) -> dict:
|
|
|
458
477
|
description = row.get("description")
|
|
459
478
|
paths = row.get("files") or []
|
|
460
479
|
if not isinstance(description, str) or not description.strip() or not isinstance(paths, list):
|
|
461
|
-
|
|
480
|
+
reference = f"task '{row['task_id']}'" if kind == "task" else f"acceptance index {row['index']}"
|
|
481
|
+
invalid_fields = []
|
|
482
|
+
if not isinstance(description, str) or not description.strip():
|
|
483
|
+
invalid_fields.append("description must be non-empty text")
|
|
484
|
+
if not isinstance(paths, list):
|
|
485
|
+
invalid_fields.append("files must be a list; use files=[] when no source file is implicated")
|
|
486
|
+
issues.append(
|
|
487
|
+
"Implementation gaps require an actionable description and file list; "
|
|
488
|
+
+ reference + ": " + "; ".join(invalid_fields)
|
|
489
|
+
)
|
|
462
490
|
continue
|
|
463
491
|
if any(
|
|
464
492
|
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.0"
|
|
1238
1238
|
|
|
1239
1239
|
|
|
1240
1240
|
def _detect_client_type() -> str:
|
|
@@ -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}"
|
|
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
|