forgexa-cli 1.18.10__tar.gz → 1.18.12__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: forgexa-cli
3
- Version: 1.18.10
3
+ Version: 1.18.12
4
4
  Summary: Forgexa CLI — command-line client and AI agent runtime for the Forgexa platform
5
5
  Author-email: Jason Sun <dev.winds@gmail.com>
6
6
  License-Expression: MIT
@@ -1,2 +1,2 @@
1
1
  """forgexa-cli — Forgexa command-line client."""
2
- __version__ = "1.18.10"
2
+ __version__ = "1.18.12"
@@ -555,7 +555,7 @@ except (ImportError, ModuleNotFoundError):
555
555
  # DAEMON_VERSION is the protocol/logic version of the daemon code.
556
556
  # Kept in sync with pyproject.toml version via bump-version.sh.
557
557
  # CLIENT_TYPE identifies which packaging/distribution this daemon runs in.
558
- DAEMON_VERSION = "1.18.10"
558
+ DAEMON_VERSION = "1.18.12"
559
559
 
560
560
 
561
561
  def _detect_client_type() -> str:
@@ -7306,6 +7306,43 @@ class RuntimeDaemon:
7306
7306
  result.metrics["recovered_from_timeout_uncommitted_changes"] = True
7307
7307
  return True
7308
7308
 
7309
+ async def _discard_verification_scope_changes(
7310
+ self,
7311
+ workspace_path: Path,
7312
+ task: TaskInfo,
7313
+ result: TaskResult,
7314
+ before_sha: str,
7315
+ ) -> None:
7316
+ """Discard protected analysis changes from a failed verification task."""
7317
+ if not bool((task.input_data or {}).get("verification_work_item")):
7318
+ return
7319
+
7320
+ forbidden_paths = sorted(
7321
+ path
7322
+ for path in ProcessManager._normalize_repo_paths(result.files_changed)
7323
+ if _is_requirement_analysis_path(path)
7324
+ )
7325
+ if not forbidden_paths:
7326
+ return
7327
+
7328
+ try:
7329
+ await self.workspace_manager._git(
7330
+ "reset", "--hard", before_sha or "HEAD", cwd=workspace_path,
7331
+ )
7332
+ await self.workspace_manager._git(
7333
+ "clean", "-f", "--", *forbidden_paths, cwd=workspace_path,
7334
+ )
7335
+ logger.warning(
7336
+ "Discarded verification scope-violation changes for task %s: %s",
7337
+ task.task_id,
7338
+ forbidden_paths,
7339
+ )
7340
+ except Exception:
7341
+ logger.exception(
7342
+ "Failed to discard verification scope-violation changes for task %s",
7343
+ task.task_id,
7344
+ )
7345
+
7309
7346
  async def _execute_task(self, task: TaskInfo, conn: ServerConnection):
7310
7347
  """Execute a single task, reporting to the originating server connection."""
7311
7348
  reporter = conn.reporter
@@ -7783,6 +7820,7 @@ class RuntimeDaemon:
7783
7820
  result = await self._validate_and_retry(
7784
7821
  agent, task, workspace_path, result,
7785
7822
  reporter, on_output_chunk, max_retries=2,
7823
+ default_branch=default_branch, before_sha=node_before_sha,
7786
7824
  )
7787
7825
  # Re-collect git info if validation triggered retries
7788
7826
  if result.metrics.get("validation_issues") is not None or True:
@@ -7790,6 +7828,11 @@ class RuntimeDaemon:
7790
7828
  except Exception:
7791
7829
  logger.exception("Validation gate error for task %s (proceeding anyway)", task.task_id)
7792
7830
 
7831
+ if result.failure_code == "verification_work_item_scope_violation":
7832
+ await self._discard_verification_scope_changes(
7833
+ workspace_path, task, result, node_before_sha,
7834
+ )
7835
+
7793
7836
  # 4.6 Post-validation rate-limit fallback.
7794
7837
  # _validate_and_retry returns early (preserving the rate-limit error)
7795
7838
  # when the agent hits a quota wall mid-retry. The initial-run fallback
@@ -7831,6 +7874,7 @@ class RuntimeDaemon:
7831
7874
  result = await self._validate_and_retry(
7832
7875
  agent, task, workspace_path, result,
7833
7876
  reporter, on_output_chunk, max_retries=2,
7877
+ default_branch=default_branch, before_sha=node_before_sha,
7834
7878
  )
7835
7879
  pre_commit_git = await self.process_manager._collect_git_info(workspace_path)
7836
7880
  except Exception:
@@ -8793,6 +8837,8 @@ class RuntimeDaemon:
8793
8837
  reporter: "ProgressReporter",
8794
8838
  on_chunk: Any,
8795
8839
  max_retries: int = 2,
8840
+ default_branch: str = "",
8841
+ before_sha: str = "",
8796
8842
  ) -> TaskResult:
8797
8843
  """Validate agent output and retry with fix prompt if issues found.
8798
8844
 
@@ -8804,6 +8850,16 @@ class RuntimeDaemon:
8804
8850
  Returns the (possibly updated) TaskResult.
8805
8851
  """
8806
8852
  for attempt in range(max_retries):
8853
+ if before_sha and bool((task.input_data or {}).get("verification_work_item")):
8854
+ committed_git = await self.process_manager._collect_git_info_vs_parent(
8855
+ workspace_path,
8856
+ default_branch=default_branch,
8857
+ before_sha=before_sha,
8858
+ )
8859
+ result.files_changed = sorted(
8860
+ set(result.files_changed)
8861
+ | set(committed_git.get("files_changed") or [])
8862
+ )
8807
8863
  issues = self._validate_outputs(workspace_path, task, result)
8808
8864
  if not issues:
8809
8865
  return result # All validations passed
@@ -8892,27 +8948,46 @@ class RuntimeDaemon:
8892
8948
  return result
8893
8949
 
8894
8950
  # Final check after all retries
8951
+ if before_sha and bool((task.input_data or {}).get("verification_work_item")):
8952
+ committed_git = await self.process_manager._collect_git_info_vs_parent(
8953
+ workspace_path,
8954
+ default_branch=default_branch,
8955
+ before_sha=before_sha,
8956
+ )
8957
+ result.files_changed = sorted(
8958
+ set(result.files_changed)
8959
+ | set(committed_git.get("files_changed") or [])
8960
+ )
8895
8961
  remaining = self._validate_outputs(workspace_path, task, result)
8896
8962
  if remaining:
8897
8963
  # Distinguish critical issues (no output produced) from minor ones (syntax)
8964
+ scope_violations = [
8965
+ issue
8966
+ for issue in remaining
8967
+ if issue.startswith("Verification work item modified requirement analysis assets:")
8968
+ ]
8898
8969
  critical_patterns = ("missing", "not found", "is empty")
8899
8970
  critical_issues = [
8900
8971
  iss for iss in remaining
8901
8972
  if any(p in iss.lower() for p in critical_patterns)
8902
8973
  ]
8903
- if critical_issues:
8974
+ if critical_issues or scope_violations:
8904
8975
  # Agent didn't produce required output — mark as failed
8905
8976
  logger.warning(
8906
8977
  "Validation gate: %d critical issues remain after %d retries for task %s — "
8907
8978
  "marking as failed:\n%s",
8908
- len(critical_issues), max_retries, task.task_id,
8909
- "\n".join(f" - {iss}" for iss in critical_issues),
8979
+ len(critical_issues) + len(scope_violations), max_retries, task.task_id,
8980
+ "\n".join(f" - {iss}" for iss in critical_issues + scope_violations),
8910
8981
  )
8911
8982
  result.status = "failed"
8912
- result.error = (
8913
- f"Agent failed to produce required output after {max_retries} retries: "
8914
- + "; ".join(critical_issues[:3])
8915
- )
8983
+ if scope_violations:
8984
+ result.failure_code = "verification_work_item_scope_violation"
8985
+ result.error = "; ".join(scope_violations[:3])
8986
+ else:
8987
+ result.error = (
8988
+ f"Agent failed to produce required output after {max_retries} retries: "
8989
+ + "; ".join(critical_issues[:3])
8990
+ )
8916
8991
  else:
8917
8992
  # Non-critical issues (syntax warnings) — proceed with warning
8918
8993
  logger.warning(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: forgexa-cli
3
- Version: 1.18.10
3
+ Version: 1.18.12
4
4
  Summary: Forgexa CLI — command-line client and AI agent runtime for the Forgexa platform
5
5
  Author-email: Jason Sun <dev.winds@gmail.com>
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "forgexa-cli"
3
- version = "1.18.10"
3
+ version = "1.18.12"
4
4
  description = "Forgexa CLI — command-line client and AI agent runtime for the Forgexa platform"
5
5
  requires-python = ">=3.9"
6
6
  license = "MIT"
File without changes
File without changes