Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

Daemon startup: wait for a live daemon instead of a fixed 15 s deadline on slow or overloaded hosts

已关闭
#3,782 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

已经有一个关联 PR 被合并了。

  • #3791 来自 @thymikee —— 已合并

评估

难度
5/5
预计耗时
一周以上
新手友好度
35/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
领域
cli, devtools

调研方向

Start with startLocalDaemon and waitForDaemonStartup in src/daemon-client/daemon-client-lifecycle.ts; trace how startup results, timeouts, and daemon logs are handled. Then read the existing lifecycle and startup-race tests and the daemon environment-variable documentation. Done when the completion conditions pass, including typed failure reasons, configurable timing, progress reporting, and updated help or docs.

由索引模型根据 Issue 内容生成。

描述

needs-triage

Purpose

On a slow or overloaded host, the CLI kills a healthy daemon that is still starting. The result is daemon_startup_failed, and the run fails before any work starts. This happens on any CI that can clog: shared macOS runners, small Linux containers, Windows runners, and nested virtualization. It also happens on a developer machine under load. A slow host must make startup slower, not fail it.

#3342 records this failure class (more than 12 instances on unrelated diffs since 2026-10-08; re-runs at the same head pass). This issue is the fix contract for that class.

Current behavior (origin/main)

startLocalDaemon in src/daemon-client/daemon-client-lifecycle.ts:

  • It sets one wall-clock deadline: DAEMON_STARTUP_TIMEOUT_MS = 15_000 (line 83, used at line 480).
  • waitForDaemonStartup (line 818) polls until the daemon publishes a reachable daemon.json, or until the process exits, or until the deadline.
  • At the deadline, the client retires the daemon, even when the process is alive. A timeout result never retries (retry is true only for early_exit), so DAEMON_STARTUP_ATTEMPTS = 2 does not help. Every report shows startupAttempts: 1.
  • No setting can change the 15 s budget.

Evidence from run 39019020372 (PR #3760, 2026-10-17):

  • The step pnpm clean:daemon took 70 s on that runner, which shows that the host was overloaded.
  • The CLI spawned daemon pid 6849 at 09:30:03. The client killed it 15 s later (cleanupResults: retired / forced / exited).
  • daemon.log was empty (0 bytes), so the daemon did not reach its first log line. The process was alive but did not get enough CPU.
  • The same signature occurred on refactor/host-environment-root (run 39008970059) and fix/provider-not-registered (run 38993655494), which do not contain that PR's commits.

Required behavior

  1. Wait for a live daemon. While the spawned daemon process is alive and has not exited, the client continues to wait for registration. A slow start is not a failure.

  2. Fail fast on a real failure. If the daemon process exits before it registers, the client reports the failure immediately, as it does now (early_exit, unproven).

  3. Keep a hard ceiling. A daemon that stays alive but never registers must not hang the client. The client stops at a ceiling. The default ceiling must be large enough for an overloaded CI host (proposal: 120 s). Measure the slowest successful startup in CI logs before you choose the number.

  4. Show progress. The daemon writes startup phase markers (for example spawned, modules-loaded, lock-acquired, listening, registered) with timestamps, to the place the client already reads (daemon.log tail or the startup metadata). The client can then tell "slow but progressing" from "stuck". If a marker shows progress, the client keeps waiting up to the ceiling. If no marker appears for a long time, the client can stop earlier than the ceiling. Put the stall window next to the ceiling, as one constant.

  5. Make the budget configurable. An environment variable sets the ceiling, for example AGENT_DEVICE_DAEMON_STARTUP_TIMEOUT_MS. It applies to the CLI, MCP, and the Node client, because all three use startLocalDaemon (via ensureDaemon(), #3765 and #3766). Document it with the other daemon environment variables. Do not add CI auto-detection. Rule 1 makes the default safe on every host.

  6. Use typed failure reasons. details.kind stays daemon_startup_failed. A new details.reason tells the caller what happened:

    • startup-exited: the process exited before it registered.
    • startup-stalled: the process is alive, but no progress marker appeared within the stall window.
    • startup-ceiling: the process made progress, but did not register before the ceiling.

    Each reason has its own hint. For startup-stalled and startup-ceiling, the hint says that the host can be overloaded, and names the environment variable. Callers branch on details.reason, never on the message.

  7. Record timings. The failure details include the time of each phase marker that the client saw, and startupTimeoutMs shows the effective ceiling.

Example failure payload:

{
  "code": "COMMAND_FAILED",
  "message": "Failed to start daemon",
  "details": {
    "kind": "daemon_startup_failed",
    "reason": "startup-stalled",
    "startupTimeoutMs": 120000,
    "phases": { "spawned": 0, "modules-loaded": 41250 },
    "hint": "The daemon is alive but made no startup progress for 60 s. The host can be overloaded. Retry, or raise AGENT_DEVICE_DAEMON_STARTUP_TIMEOUT_MS."
  }
}

Completion conditions

  • A test starts a daemon that is alive but slow (for example a test-only startup delay of 20 s, longer than the old 15 s budget). Startup succeeds, and the client does not kill the daemon.
  • A test starts a daemon that exits during startup. The client reports startup-exited within 1 s after the exit.
  • A test starts a daemon that stays alive and writes no phase marker. The client reports startup-stalled at the stall window, not at the ceiling.
  • A test sets the environment variable to a small value and sees startup-ceiling with the new startupTimeoutMs.
  • The existing lifecycle and startup-race tests pass without change in meaning.
  • The daemon-client help or user docs name the environment variable and the three reasons.
  • After the change merges, #3342 gets a Current state: comment. Then the iOS Smoke preflight step is observed for one week. The issue closes if no daemon_startup_failed with reason startup-stalled or startup-ceiling appears below the new ceiling.

Out of scope

  • Re-running CI lanes to hide the failure.
  • The slow pnpm clean:daemon step in .github/workflows/ios.yml (70 s on an overloaded runner). That is a separate CI cost. Report it in #3342 if it repeats.

Dependencies

  • #3342: failure-class log and evidence.
  • #3765 and #3766 (merged): ensureDaemon() and attach mode. They call startLocalDaemon, so the change applies to all clients.
主要语言
TypeScript
星标
5k
派生
334
平均合并
12 小时 14 分钟
30 天内合并 PR
713

环境准备

  • 没有 Dockerfile 或 Docker Compose 文件
  • 没有 Pull Request 模板
  • 阅读贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

callstack/agent-device 的其他 Issue

查看 callstack/agent-device 的全部 Issue

相似的 Issue

更多 TypeScript Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。