--- name: project-agent-call-liveness-brd-written description: BRD for "agent call timeout must be liveness-based, not fixed-duration" written 2026-08-13; call-site inventory and open questions for SDD stage metadata: type: project --- BRD written 2026-08-13 at `docs/todo/agent-call-liveness-not-fixed-duration-timeout-brd.md`, based on requirements doc `docs/todo/agent-call-liveness-not-fixed-duration-timeout-requirements.md`. Triggered by a real incident: task `bfd3ee82`'s Validator planning call (large embedded SDD/PRD prompt) hit the 300s fixed timeout in `trigger_org_node_structured` and was misclassified as `blocked_reason='execution_error'` via scheduler.py's catch-all (not a real hang). **Why:** this closes the gap already flagged in [[feedback_agent_call_liveness_not_fixed_timeout]] (recorded same day, before the BRD existed) — the "no fixed-duration timeout" rule had been agreed before but never implemented; `bfd3ee82` is the first real incident it caused, not the first time the rule was stated. **How to apply (facts verified 2026-08-13, re-check before trusting long-term):** - Only `e2e_pipeline.py:1131` (`run_test_script_for_real`) catches `subprocess.TimeoutExpired` specifically — everywhere else it falls through to the `execution_error` catch-all (`task_execution.py::_mark_blocked` line 223, `scheduler.py` line 255-256). - Full call-site inventory with fixed `timeout=` values (300/600/1200/180 etc, all fixed-duration wall-clock): `org_node_trigger.py` (the two entry points), `e2e_pipeline.py` (plan/review/ implement/validate, several call sites), `work_graph_plan.py::plan_work_graph`, `work_graph_orchestrate.py` (judgment/reflection/skill-fix, several), `org_level_loop.py:: run_fix_loop`, `root_cause_trace.py::real_validator_perspective`, `org_node_lifecycle.py:: trigger_org_node_full`, `work_graph_execute.py::execute_work_graph` (separate — Dynamic Workflow script exec, not a direct `claude -p` call, scoped as an open question not pre-included). - `scheduler.py::_BLOCKED_REASON_FOR_EXCEPTION` (line 82-89) is the existing exception-type → blocked_reason registry to extend with a new "real stall" exception type → `agent_call_stalled`, rather than inventing a parallel routing mechanism. - `schema.py`'s `blocked_reason` column is unconstrained TEXT (no CHECK), and `frontend/app.js` only escapes/displays the raw string generically (no hardcoded enum switch beyond `user_paused`/`user_stopped`) — adding `agent_call_stalled` needs no schema migration and no frontend enum sync, unlike the drift pattern in [[project_task_status_enum_drift_gap]]. - BRD deliberately left the core technical question open for SDD: what "liveness signal" is even observable, given `claude -p --output-format json` is one-shot, not streaming (needs real research, not an assumption) — see BRD's 阻塞未知/open-questions section before writing PRD/SDD. - Related but independent Goal: `docs/todo/boss-agent-autonomous-blocked-task-arbiter- requirements.md` (handles what to do once a task IS truly stuck in blocked_pending_human) — this BRD's fix should land first to reduce false-positive noise Boss Agent would otherwise have to triage.