Skip to content

fix: make bridge launch readiness timeout configurable - #276

Merged
steipete merged 3 commits into
openclaw:mainfrom
omarshahine:fix/launch-readiness-timeout
Sep 7, 2026
Merged

fix: make bridge launch readiness timeout configurable#276
steipete merged 3 commits into
openclaw:mainfrom
omarshahine:fix/launch-readiness-timeout

Conversation

@omarshahine

@omarshahine omarshahine commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Messages can take longer than 15 seconds to become bridge-ready after a cold start. The existing timeout then reports a launch error with misleading SIP/permission guidance, even when the process becomes ready shortly afterward.

Allow CLI, library, and supervisor callers to set IMSG_LAUNCH_READY_TIMEOUT in seconds. Keep the 15-second default, bound positive overrides at 600 seconds, and fall back to the default for invalid values. Recheck readiness at the deadline and explain that Messages may still be starting, with an imsg status check before another launch. The public payload-free MessagesLauncherError.socketTimeout case and readiness-based success contract are preserved.

The advanced IMCore guide documents the setting, bounds, defaults, and supervisor use. Tests cover timeout resolution, diagnostics, and external-style construction/matching of the public error. Native evidence from the contributor's Mac shows a successful cold start in 18.054 seconds with a 60-second budget and the new diagnostic with a one-second budget. The maintainer proof comment records the final head's validation and remaining host limitations.

Fixes #273. Cross-process launch serialization remains independently handled by #274. Changelog notes are consolidated in the final release-notes PR.

…ow starts as failures

`imsg launch` waited a hardcoded 15s for Messages.app to publish the bridge-ready
lock file, then threw. On a cold start after a crash, or a loaded machine, that
window is too short: the instance comes up healthy moments later but the command
has already exited non-zero with an error naming SIP and permissions, neither of
which was the cause.

That misreport has a real consequence. A supervisor that relaunches on a non-zero
exit starts a second Messages.app while the first is still coming up, and two
injected instances then compete for the same bridge queue.

- resolve the timeout from IMSG_LAUNCH_READY_TIMEOUT, defaulting to 15s and
  clamped to 600s so a typo cannot hang a launch
- re-check readiness once after the deadline, so a launch that actually
  succeeded in the final polling gap is not reported as a failure
- carry the timeout in the error and point at `imsg status` and the override
  before blaming SIP or permissions

Measured on macOS 26 (Apple Silicon, SIP disabled): a cold start after a hard
kill took 18.05s to report ready, past the previous hardcoded limit.
@clawsweeper

clawsweeper Bot commented Sep 7, 2026

Copy link
Copy Markdown

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

@clawsweeper clawsweeper Bot added P2 Normal priority bug or improvement with limited blast radius. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. proof: sufficient Contributor real behavior proof is sufficient. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Sep 7, 2026
@clawsweeper

clawsweeper Bot commented Sep 7, 2026

Copy link
Copy Markdown

Codex review: needs maintainer review before merge. Reviewed September 7, 2026, 4:25 AM ET / 08:25 UTC.

ClawSweeper review

What this changes

Adds a bounded environment setting for Messages bridge startup waits, a final readiness check, clearer timeout guidance, regression tests, and documentation.

Merge readiness

Ready for maintainer review

This remains useful: current main and v0.15.1 retain the fixed timeout. The prior public-error compatibility defect is resolved, and no blocking defect remains in the reviewed patch.

Priority: P2
Reviewed head: 00345471bf45cd7333ce16941086830d924758d5

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) A focused, compatible implementation with native behavior evidence and no remaining blocking finding.
Proof confidence 🦞 diamond lobster (5/6) Sufficient (terminal): The captured native evidence exercises the real Messages launch path: a 60-second budget permits an 18.054-second cold start, and a one-second budget produces the revised diagnostic. Production code is unchanged since the accepted proof; full transcript retrieval was unavailable during this review.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Verified Sufficient (terminal): The captured native evidence exercises the real Messages launch path: a 60-second budget permits an 18.054-second cold start, and a one-second budget produces the revised diagnostic. Production code is unchanged since the accepted proof; full transcript retrieval was unavailable during this review.
Evidence reviewed 7 items Repository policy and ownership: The origin identifies openclaw/imsg. The full root AGENTS.md was read; no nested AGENTS.md or maintainer-notes directory was found. Its focused-change, regression-test, and validation guidance was applied.
Current main still needs the change: Current main calls waitForReady(timeout: 15.0), lacks the final readiness check, and retains the SIP/permission-first timeout diagnostic. The supplied latest-release identity and local tag check place v0.15.1 at this same revision.
Bounded configuration with compatible defaults: The resolver accepts finite positive values, caps them at 600 seconds, and preserves the 15-second fallback. The launcher uses it at the existing wait boundary without changing SIP checks, injection permissions, or persisted settings.
Findings None None.
Security None None.

How this fits together

The Messages launcher starts the injected helper used by advanced messaging commands. CLI and library callers wait for its readiness file before proceeding or receiving a timeout error.

flowchart TD
  A[CLI and library callers] --> B[Messages launcher]
  C[Environment timeout setting] --> D[Validate and bound wait]
  B --> E[Start Messages helper]
  D --> F[Poll readiness file]
  E --> F
  F --> G[Continue when ready]
  F --> H[Timeout with recovery guidance]
Loading

Before merge

None.

Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Production and test growth production +50/-3; tests +72/-0; docs +13/-0 Production growth is justified by bounded timeout parsing and recovery guidance, with focused configuration and compatibility coverage.

Technical review

Best possible solution:

Retain the bounded startup override and readiness-based success contract while keeping cross-process launch serialization in its separate fix.

Do we have a high-confidence way to reproduce the issue?

Yes: current main deterministically times out if the readiness file appears after its fixed 15-second wait; the linked report supplies a native example. This read-only review did not execute a reproduction.

Is this the best way to solve the issue?

Yes: resolving the budget in the shared launcher covers CLI and library callers while preserving defaults and the public error API; the final documentation commit supports the chosen configuration contract.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning medium; reviewed against 646ea7af9616.

Labels

Label changes:

  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • remove rating: 🦞 diamond lobster: Current PR rating is rating: 🐚 platinum hermit, so this older rating label is no longer current.

Label justifications:

  • P2: This addresses slow startup handling in the optional advanced Messages bridge with a limited, backward-compatible change.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The captured native evidence exercises the real Messages launch path: a 60-second budget permits an 18.054-second cold start, and a one-second budget produces the revised diagnostic. Production code is unchanged since the accepted proof; full transcript retrieval was unavailable during this review.
  • proof: sufficient: Contributor real behavior proof is sufficient. The captured native evidence exercises the real Messages launch path: a 60-second budget permits an 18.054-second cold start, and a one-second budget produces the revised diagnostic. Production code is unchanged since the accepted proof; full transcript retrieval was unavailable during this review.

Evidence

What I checked:

  • Repository policy and ownership: The origin identifies openclaw/imsg. The full root AGENTS.md was read; no nested AGENTS.md or maintainer-notes directory was found. Its focused-change, regression-test, and validation guidance was applied. (AGENTS.md:1, 00345471bf45)
  • Current main still needs the change: Current main calls waitForReady(timeout: 15.0), lacks the final readiness check, and retains the SIP/permission-first timeout diagnostic. The supplied latest-release identity and local tag check place v0.15.1 at this same revision. (Sources/IMsgCore/MessagesLauncher.swift:159, 646ea7af9616)
  • Bounded configuration with compatible defaults: The resolver accepts finite positive values, caps them at 600 seconds, and preserves the 15-second fallback. The launcher uses it at the existing wait boundary without changing SIP checks, injection permissions, or persisted settings. (Sources/IMsgCore/LaunchReadinessTimeout.swift:21, 00345471bf45)
  • Prior finding resolved and production code unchanged: The public socketTimeout case is payload-free, and the regression fixture covers construction, matching, collection use, and throwing. Comparison with the previous reviewed head shows only the advanced-guide addition; the earlier compatibility blocker is not present. (Tests/IMsgCoreTests/IMCoreBridgeTests.swift:106, 00345471bf45)
  • Native behavior evidence and validation: The captured PR body records a contributor-Mac cold start succeeding in 18.054 seconds with a 60-second budget and the revised diagnostic with a one-second budget. The supplied previous review accepted native proof; the production code is unchanged since that review. The contributor comment reports passing timeout/compatibility tests, unchanged lint warnings, and three baseline RPC failures tracked separately at tests: three RPCServerTests fail on any host with a live injected bridge (green only in CI) #275. Snapshot sourceRevision: 7393dcf1422df3191fd9c605b5ea589c2a2ded4186a39f141342ea56b400e48d. Attempts to retrieve the complete proof comment failed through both GitHub API and browser access; this limits independent transcript inspection without negating the supplied evidence. (b0994f44b56c)
  • Distinct remaining work and maintainer context: The supplied discussion links this implementation to launch: 15s readiness timeout is hardcoded and reports a successful launch as an error #273. Cross-process serialization remains separately owned by fix: serialize Messages bridge launches across processes #274. The final documentation commit by steipete explicitly documents this setting, its compatibility defaults, and its limits. (docs/advanced-imcore.md:68, 00345471bf45)

Likely related people:

  • steipete: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)
  • omarshahine: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

History

Review history (3 earlier review cycles)
  • reviewed 2026-09-07T01:11:50.260Z sha 36d7cd2 :: blocked before merge. :: [P1] Preserve the existing public timeout-error construction
  • reviewed 2026-09-07T01:36:57.619Z sha b0994f4 :: needs maintainer review before merge. :: none
  • reviewed 2026-09-07T01:40:21.055Z sha b0994f4 :: needs maintainer review before merge. :: none

IMsgCore is an exported library, so external callers construct and match
`MessagesLauncherError.socketTimeout` directly. Adding a required associated
value made those previously valid expressions fail to compile on upgrade;
updating this repository's own caller only migrated the in-repo use.

Swift cannot rescue this with a default on the payload — `case
socketTimeout(seconds: TimeInterval = 15)` turns `.socketTimeout` into a
function reference rather than a value, so the old spelling still breaks.

Restore the payload-free case and resolve the timeout inside `description`
instead. `waitForReady` is only ever called with LaunchReadinessTimeout.resolve(),
so the reported number matches the wait that actually elapsed, and the public
construction contract is unchanged.

Adds a source-compatibility regression fixture covering the shapes an external
consumer relies on: member-syntax and fully-qualified construction, use in a
collection literal, switch matching, and throw/catch. Reintroducing the
associated value fails to compile against that fixture, which is the point.
@omarshahine

Copy link
Copy Markdown
Contributor Author

Good catch — that was a real source-compatibility break and I had only considered in-repo callers. Fixed in b0994f4.

What changed

MessagesLauncherError.socketTimeout is payload-free again, so the public construction contract is unchanged.

I first checked whether a default could preserve the old spelling. It cannot — Swift turns the case into a function reference:

public enum E: Error { case socketTimeout(seconds: TimeInterval = 15) }
let a: E = .socketTimeout
// error: member 'socketTimeout(seconds:)' is a function that produces expected
//        type 'E'; did you mean to call it?

So the associated value had to go. The diagnostic is kept by resolving the timeout inside description instead:

case .socketTimeout:
  // Resolved rather than stored: the case stays payload-free so external
  // `.socketTimeout` construction keeps compiling, and `waitForReady` is
  // only ever called with this same resolved value.
  let seconds = LaunchReadinessTimeout.resolve()

waitForReady has exactly one call site, always LaunchReadinessTimeout.resolve(), so the reported number still matches the wait that actually elapsed.

Source-compatibility regression fixture

Added socketTimeoutKeepsPayloadFreePublicConstruction(), covering the shapes an external consumer actually uses: member-syntax construction, fully-qualified construction, use in a collection literal, switch matching, and throw/catch.

I verified it fails closed rather than assuming it does. Reintroducing the associated value (with the source tree otherwise made self-consistent, so only the fixture is left failing) breaks the build:

Tests/IMsgCoreTests/IMCoreBridgeTests.swift:106:49: error: member 'socketTimeout(seconds:)' expects argument of type 'TimeInterval'
Tests/IMsgCoreTests/IMCoreBridgeTests.swift:108:49: error: member 'socketTimeout(seconds:)' expects argument of type 'TimeInterval'
Tests/IMsgCoreTests/IMCoreBridgeTests.swift:118:20: error: cannot convert value of type '@Sendable (TimeInterval) -> MessagesLauncherError' to expected argument type 'MessagesLauncherError'

Gates at this head

make lint: Found 15 violations, 0 serious in 249 files, exit 0 — identical to base 646ea7a; I diffed the violation sets and this branch introduces no new rule violation.

make test: 750 tests, 3 failing — the same three pre-existing rpcSend* cases that fail on base on any host with a live injected bridge (#275, fixed by #277). All 7 tests in this change pass:

✔ readinessTimeoutFallsBackToDefaultWithoutOverride()
✔ readinessTimeoutHonorsEnvironmentOverride()
✔ readinessTimeoutRejectsUnusableOverrides()
✔ readinessTimeoutIsClampedToAnUpperBound()
✔ socketTimeoutDescriptionReportsTheTimeoutAndOverride()
✔ socketTimeoutKeepsPayloadFreePublicConstruction()
✔ messagesLauncherErrorDescriptions()

@omarshahine

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Sep 7, 2026

Copy link
Copy Markdown

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event exact_review_queue).
Result: when the review finishes, ClawSweeper will create the durable review comment if needed or update the existing comment in place.

Re-review progress:

@clawsweeper clawsweeper Bot added rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. labels Sep 7, 2026
@steipete steipete changed the title fix: make launch readiness timeout configurable and stop reporting slow starts as failures fix: make bridge launch readiness timeout configurable Sep 7, 2026
@steipete

steipete commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

Verification for 00345471bf45cd7333ce16941086830d924758d5 (the contributor's implementation plus maintainer documentation):

  • All seven timeout-resolution, diagnostic, and public-error compatibility tests pass. Documentation tests pass. make lint passes with 15 existing warnings and no serious violations; git diff --check passes.
  • Local and committed-branch autoreviews against origin/main are clean at P0–P2.
  • A compiled integration harness used the complete production launcher and its dependencies with a temporary readiness file. Readiness arriving in the final polling interval was accepted; absent readiness returned the updated diagnostic and retained timeout error. No Messages lifecycle or SIP setting was changed by this harness.
  • Built and signed the actual CLI with the matching Developer ID and ran it successfully. The native cold-start evidence in this PR remains applicable: production code is unchanged from b0994f4, where a 60-second budget accepted the 18.054-second cold start and a one-second budget produced the updated error. This verification Mac has SIP enabled.

The new guide section documents IMSG_LAUNCH_READY_TIMEOUT, the 15-second default, positive seconds, 600-second cap, invalid-value fallback, and readiness-based failure semantics. It tells supervisors to check status before relaunching.

Exact-head macOS/Linux CI: https://github.com/openclaw/imsg/actions/runs/34100099441. Fixes #273. Changelog notes are consolidated in the final notes PR.

@clawsweeper clawsweeper Bot added rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. and removed rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. labels Sep 7, 2026
@steipete
steipete merged commit b859513 into openclaw:main Sep 7, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Normal priority bug or improvement with limited blast radius. proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

launch: 15s readiness timeout is hardcoded and reports a successful launch as an error

2 participants