fix(company): stop Report #N storm and spill large Cursor prompts to files
Cursor-agent puts prompts on argv, so oversized company/report handoffs hit OS ARG_MAX, crash the card as FAILED, and reconcile kept minting new Report attempts forever. Spill large Cursor prompts to a workspace file and hold the report chain after consecutive failures.
This commit is contained in:
@@ -3503,6 +3503,38 @@ class ExternalAgentMonitoringTests(unittest.IsolatedAsyncioTestCase):
|
||||
self.assertEqual(result.status, TaskStatus.DONE)
|
||||
self.assertEqual(spawn_mock.await_args.kwargs["stdin"], asyncio.subprocess.DEVNULL)
|
||||
|
||||
def test_cursor_adapter_large_prompt_spills_to_workspace_file(self) -> None:
|
||||
adapter = CursorAdapter(config=ExternalAgentConfig(command="cursor-agent"))
|
||||
prompt = "x" * (CursorAdapter._ARGV_PROMPT_MAX_BYTES + 1)
|
||||
task = Task(id="task-large", title="large", description=prompt)
|
||||
full_prompt = adapter.build_task_prompt(task)
|
||||
tmpdir = _make_test_dir("cursor-large-prompt-file")
|
||||
try:
|
||||
cmd, metadata = adapter.build_interactive_invocation(
|
||||
task, workspace_path=tmpdir
|
||||
)
|
||||
self.assertEqual(metadata["prompt_transport"], "file")
|
||||
self.assertEqual(
|
||||
metadata["prompt_transport_reason"],
|
||||
"prompt_too_large_for_argv",
|
||||
)
|
||||
prompt_file = Path(str(metadata["prompt_file"]))
|
||||
self.assertTrue(prompt_file.is_file())
|
||||
self.assertEqual(prompt_file.read_text(encoding="utf-8"), full_prompt)
|
||||
self.assertNotIn(full_prompt, cmd)
|
||||
self.assertIn(str(prompt_file), str(cmd[-1]))
|
||||
self.assertNotIn(prompt[:64], metadata["command"])
|
||||
self.assertLess(len(cmd[-1].encode("utf-8")), CursorAdapter._ARGV_PROMPT_MAX_BYTES)
|
||||
finally:
|
||||
_cleanup_test_dir(tmpdir)
|
||||
|
||||
def test_cursor_adapter_small_prompt_stays_on_argv(self) -> None:
|
||||
adapter = CursorAdapter(config=ExternalAgentConfig(command="cursor-agent"))
|
||||
task = Task(title="demo", description="short body")
|
||||
cmd, metadata = adapter.build_invocation(task, workspace_path="/tmp/opc-ws")
|
||||
self.assertEqual(metadata["prompt_transport"], "argv")
|
||||
self.assertEqual(cmd[-1], adapter.build_task_prompt(task))
|
||||
|
||||
def test_codex_adapter_mirrors_user_auth_and_config_with_copy_fallback(self) -> None:
|
||||
adapter = CodexAdapter()
|
||||
tmpdir = _make_test_dir("codex-mirror-user-config")
|
||||
|
||||
@@ -1433,5 +1433,107 @@ class ReportCardRunnableFilterTests(unittest.TestCase):
|
||||
)
|
||||
|
||||
|
||||
class ReportFailureStormBrakeTests(unittest.IsolatedAsyncioTestCase):
|
||||
"""FAILED report cards must not mint Report #N forever on reconcile."""
|
||||
|
||||
async def asyncSetUp(self) -> None:
|
||||
self._tmpdir = tempfile.TemporaryDirectory()
|
||||
self.root = Path(self._tmpdir.name)
|
||||
self.store = OPCStore(self.root / "tasks.db")
|
||||
await self.store.initialize()
|
||||
self.executor = _build_executor(self.store, _make_org_engine(self.root))
|
||||
|
||||
async def asyncTearDown(self) -> None:
|
||||
await self.store.close()
|
||||
self._tmpdir.cleanup()
|
||||
|
||||
def _failed_report(self, attempt: int) -> DelegationWorkItem:
|
||||
report_id = report_work_item_id_for_attempt("wi-child", attempt)
|
||||
return DelegationWorkItem(
|
||||
work_item_id=report_id,
|
||||
run_id="run-1",
|
||||
cell_id="team::cto",
|
||||
team_id="team::cto",
|
||||
role_id="engineer",
|
||||
seat_id="seat::team::cto::engineer",
|
||||
parent_work_item_id="wi-child",
|
||||
kind="report",
|
||||
projection_id=report_id,
|
||||
phase=Phase.FAILED,
|
||||
batch_index=attempt,
|
||||
metadata={
|
||||
"runtime_model": "multi_team_org",
|
||||
"report_execution_work_item": True,
|
||||
"report_attempt": attempt,
|
||||
"report_target_work_item_id": "wi-child",
|
||||
"hidden_from_company_kanban": True,
|
||||
},
|
||||
)
|
||||
|
||||
async def test_reconcile_stops_after_consecutive_report_failures(self) -> None:
|
||||
parent = _build_child_work_item()
|
||||
parent.phase = Phase.AWAITING_MANAGER_REVIEW
|
||||
parent.metadata = {
|
||||
**dict(parent.metadata or {}),
|
||||
"review_owner_role_id": "cto",
|
||||
"review_owner_seat_id": "seat::team::cto::cto",
|
||||
"max_consecutive_report_failures": 3,
|
||||
}
|
||||
await self.store.save_delegation_work_item(parent)
|
||||
for attempt in (1, 2, 3):
|
||||
await self.store.save_delegation_work_item(self._failed_report(attempt))
|
||||
|
||||
before = [
|
||||
item.work_item_id
|
||||
for item in await self.store.list_delegation_work_items("run-1")
|
||||
if item.kind == "report"
|
||||
]
|
||||
await self.executor._reconcile_missing_review_chain(
|
||||
await self.store.list_delegation_work_items("run-1")
|
||||
)
|
||||
after = [
|
||||
item.work_item_id
|
||||
for item in await self.store.list_delegation_work_items("run-1")
|
||||
if item.kind == "report"
|
||||
]
|
||||
self.assertEqual(before, after)
|
||||
self.assertIsNone(
|
||||
await self.store.get_delegation_work_item(
|
||||
report_work_item_id_for_attempt("wi-child", 4)
|
||||
)
|
||||
)
|
||||
refreshed = await self.store.get_delegation_work_item("wi-child")
|
||||
self.assertEqual(
|
||||
refreshed.metadata.get("report_chain_hold"),
|
||||
"consecutive_report_failures",
|
||||
)
|
||||
|
||||
async def test_reconcile_allows_retry_below_failure_limit(self) -> None:
|
||||
parent = _build_child_work_item()
|
||||
parent.phase = Phase.AWAITING_MANAGER_REVIEW
|
||||
parent.metadata = {
|
||||
**dict(parent.metadata or {}),
|
||||
"review_owner_role_id": "cto",
|
||||
"review_owner_seat_id": "seat::team::cto::cto",
|
||||
"max_consecutive_report_failures": 3,
|
||||
}
|
||||
await self.store.save_delegation_work_item(parent)
|
||||
await self.store.save_delegation_work_item(self._failed_report(1))
|
||||
await self.store.save_delegation_work_item(self._failed_report(2))
|
||||
|
||||
await self.executor._reconcile_missing_review_chain(
|
||||
await self.store.list_delegation_work_items("run-1")
|
||||
)
|
||||
next_report = await self.store.get_delegation_work_item(
|
||||
report_work_item_id_for_attempt("wi-child", 3)
|
||||
)
|
||||
self.assertIsNotNone(next_report)
|
||||
self.assertEqual(next_report.phase, Phase.READY)
|
||||
refreshed = await self.store.get_delegation_work_item("wi-child")
|
||||
self.assertFalse(
|
||||
str((refreshed.metadata or {}).get("report_chain_hold", "") or "").strip()
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user