Skip to content

fix(nomad): bound control requests with durable registration recovery - #1916

Merged
steipete merged 2 commits into
openclaw:mainfrom
SebTardif:fix/nomad-control-http-timeout
Sep 12, 2026
Merged

fix(nomad): bound control requests with durable registration recovery#1916
steipete merged 2 commits into
openclaw:mainfrom
SebTardif:fix/nomad-control-http-timeout

Conversation

@SebTardif

@SebTardif SebTardif commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Complete the Nomad control-request timeout repair with durable registration recovery. All eight finite JSON operations have a two-minute ceiling while retaining earlier caller deadlines. Regions discovery now carries cancellation. Default transports bound response-header waits to 30 seconds without adding a whole-request client timeout to established exec streams; injected client settings stay unchanged.

The original write-timeout concern is addressed, not omitted: a registration can be accepted even when its response is lost. Crabbox now durably records the exact job, lease, scope, and registration phase before dispatch. An unresolved submission is not discarded on the first 404. Matching owned observations use the existing guarded cleanup path, and setup failure still rolls back regardless of --keep.

Ownership and compatibility

The provider uses the existing atomic claim writer, durable compare-and-swap transactions, and shared run finalizer. A small explicit recovery carrier lets acquisition errors report a kept session without authorizing a second cleanup, running a command, or applying normal Keep policy to failed acquisition. Ordinary ID-only acquisition errors and other providers retain their behavior.

Primary failure codes and causes are preserved even when cleanup adds another typed error. Warmup diagnostics retain job/lease identity; Run session output and its single timing record retain recovery identity. Pending status is not readiness. Existing claims without registration markers keep their confirmed-job behavior, and unknown new marker versions fail closed.

The original complete setup-metadata check is retained using a key list and fingerprint, without storing arbitrary custom metadata values in the new claim. Normal ready-lease cleanup retains its prior reserved-ownership checks. Registration is not automatically retried, and elapsed time alone does not prove that an uncertain submission was rejected.

Head ab884aee666ffddda115286ea2991e1ee0d05b0a, tree 1f8181b15b40c1ea41906eab4f91d3302c2e7254, integrates main 159d54e4499d2a2661d6dc08aedb5b2168f59711 and preserves the original contributor commit. Thanks @SebTardif; contributor credit and the Unreleased entry are included.

Verification

  • Full race suites passed: Nomad 86 tests/84 subcases, shared provider helpers 93 tests/332 subcases, and four targeted core claim tests/31 subcases. Vet, documentation validation/build, 23 documentation tests, and the CLI build passed.
  • A permanent real-HTTP regression now lives in the existing registration test file. It exercises accepted registration with a delayed JSON acknowledgement, pre-dispatch durable identity, request cancellation, the owned GET/DELETE/confirmed-404 cleanup sequence, claim removal, and primary deadline exit code 1. Its short test-local timer is restored; the production default is unchanged.
  • The complete independent Codex review found no P0 findings. This is a scoped review, not an all-priority certificate.

Actual CLI over real HTTP

A separately frozen CLI harness used isolated configuration/state and an owned loopback service. It stored the originally submitted job and ownership metadata unchanged; no credentials, real Nomad scheduler, driver, remote job, or command were used.

Scenario Observed result
Warmup with accepted registration and an incomplete JSON response The unmodified two-minute request budget expired after about 121.6 seconds overall; exit 1 retained the deadline diagnostic and exact job/lease identity. Owned cleanup deregistered once, confirmed absence, and removed the claim.
Run interrupted after accepted registration while reads initially returned 404 Run exited 1 in about 0.94 seconds, with canceled timing/exitCode 1, commandMs 0, and an exact kept, non-reused recovery session. Status reported registration-pending/ready=false; Stop initially retained the claim without DELETE. After the same original job became visible, Stop performed one owned deletion and removed the claim.

The five CLI calls made 11 HTTP requests. All 11 request-handler threads joined, all CLI processes were reaped, both listeners stopped, and no claims remained. The final test-only addition rebuilt a byte-identical CLI, so this execution remains applicable by binary identity; it is not presented as an additional run.

Earlier evidence is preserved: the first package run hit a proof-profile Unix-socket restriction, corrected only with a task-owned socket allowance and outside-prefix denial controls; an earlier CLI run met its nonzero-exit checks but exposed exit-code precedence 5 instead of 1. A regression reproduced nine failures before the correction, and the strengthened CLI assertions then passed.

Limits and remaining gate

This proves actual CLI/client transport and local durable recovery behavior against a synthetic service, not live scheduler or default-Docker behavior. No multi-minute real allocation exec session is claimed; its unchanged established-stream path is supported by source and configuration checks, with startup HTTP discovery documented separately. Current-head GitHub CI and review disposition remain to be checked before landing. No release, fleet update, or production Nomad operation is included.

Nomad used a pooled HTTP client with no request deadline. Finite
calls such as job inspect and regions.list could hang forever when
the API accepted TCP but never finished the JSON body.

Give those control calls a 2m request context, pass context through
Regions, and set ResponseHeaderTimeout on the default transport.
Leave Client.Timeout at 0 so AllocationExec streams can outlive the
control budget.

Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
@clawsweeper

clawsweeper Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

🦞👀
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 6, 2026
@clawsweeper

clawsweeper Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed September 12, 2026, 3:40 AM ET / 07:40 UTC (Revision 2).

ClawSweeper review

What this changes

Bounds Nomad control requests and records durable recovery claims so interrupted job registration remains discoverable and safely recoverable.

Merge readiness

Ready for maintainer review

This repair remains necessary on current main and v0.57.0. The revised head addresses the previous registration-recovery finding and both requested follow-ups; no new blocking defect was found.

Priority: P2
Reviewed head: ab884aee666ffddda115286ea2991e1ee0d05b0a

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) A focused reliability repair with relevant production-path evidence, compatibility coverage, and the previous blocker resolved.
Proof confidence 🐚 platinum hermit (4/6) Sufficient (live_output): The captured CLI observations exercise the changed Nomad client and durable claim owners over real HTTP: delayed acknowledgement triggers bounded rollback, while caller cancellation retains recoverable identity through pending reads and later owned deletion. The synthetic service is sufficient for this transport-recovery scope; live scheduling and long-running exec are not claimed.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Verified Sufficient (live_output): The captured CLI observations exercise the changed Nomad client and durable claim owners over real HTTP: delayed acknowledgement triggers bounded rollback, while caller cancellation retains recoverable identity through pending reads and later owned deletion. The synthetic service is sufficient for this transport-recovery scope; live scheduling and long-running exec are not claimed.
Evidence reviewed 8 items Policy and introduction boundary: Read the complete root AGENTS.md and checked tracked policy paths; no nested policy applies to the changed files, and no maintainer-notes directory exists. Reviewed the complete local base-to-head diff, rather than relying on truncated supplied patches. The checkout remained clean.
Repair remains absent from main: At the pinned main revision, Regions discards its caller context, finite client operations have no adapter request ceiling, and createJob returns immediately after a registration error before publishing a claim. The reviewed diff supplies distinct remaining work.
Latest release comparison: Inspected the Nomad client at v0.57.0 and resolved that tag to baa6c9a; it retains the unbounded control-call implementation and context-discarding Regions method.
Findings None None.
Security None None.

How this fits together

Crabbox’s Nomad adapter translates CLI lifecycle commands into scheduler requests and stores local lease claims. Those claims connect registration, readiness, command execution, and ownership-checked cleanup.

flowchart TD
  A[Run or warmup] --> B[Persist registration identity]
  B --> C[Bounded Nomad request]
  C --> D{Registration outcome}
  D -->|Acknowledged| E[Check allocation readiness]
  D -->|Uncertain| F[Retain recovery claim]
  F --> G[Ownership checked cleanup]
  E --> H[Execute or retain lease]
Loading

Before merge

None.

Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Production and test delta Production +454/-77 lines; tests +844/-25 lines Production growth is justified by durable registration recovery and its integration with existing claim and run-session owners.

Technical review

Best possible solution:

Keep request bounds and registration reconciliation inside the Nomad adapter, using shared durable claim transactions while preserving legacy claims and established exec streams.

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

Yes, source establishes a concrete path: a stalled JSON response can leave main’s finite Nomad calls unbounded, and Regions ignores caller cancellation. This review did not execute a current-main reproduction.

Is this the best way to solve the issue?

Yes. Adapter-local request contexts preserve stream behavior, and durable identity plus guarded reconciliation addresses ambiguous registration without introducing a global HTTP timeout or competing claim store.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning medium; reviewed against 159d54e4499d.

Labels

Label changes:

  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The captured CLI observations exercise the changed Nomad client and durable claim owners over real HTTP: delayed acknowledgement triggers bounded rollback, while caller cancellation retains recoverable identity through pending reads and later owned deletion. The synthetic service is sufficient for this transport-recovery scope; live scheduling and long-running exec are not claimed.
  • remove merge-risk: 🚨 compatibility: Current PR review selected no merge-risk labels.
  • remove rating: 🦐 gold shrimp: Current PR rating is rating: 🐚 platinum hermit, so this older rating label is no longer current.
  • remove status: ⏳ waiting on author: Current PR status label is status: 👀 ready for maintainer look.

Label justifications:

  • P2: Repairs bounded Nomad control requests and recoverability after interrupted registration, with provider-limited impact.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The captured CLI observations exercise the changed Nomad client and durable claim owners over real HTTP: delayed acknowledgement triggers bounded rollback, while caller cancellation retains recoverable identity through pending reads and later owned deletion. The synthetic service is sufficient for this transport-recovery scope; live scheduling and long-running exec are not claimed.
  • proof: sufficient: Contributor real behavior proof is sufficient. The captured CLI observations exercise the changed Nomad client and durable claim owners over real HTTP: delayed acknowledgement triggers bounded rollback, while caller cancellation retains recoverable identity through pending reads and later owned deletion. The synthetic service is sufficient for this transport-recovery scope; live scheduling and long-running exec are not claimed.

Evidence

What I checked:

  • Policy and introduction boundary: Read the complete root AGENTS.md and checked tracked policy paths; no nested policy applies to the changed files, and no maintainer-notes directory exists. Reviewed the complete local base-to-head diff, rather than relying on truncated supplied patches. The checkout remained clean. (AGENTS.md:1, ab884aee666f)
  • Repair remains absent from main: At the pinned main revision, Regions discards its caller context, finite client operations have no adapter request ceiling, and createJob returns immediately after a registration error before publishing a claim. The reviewed diff supplies distinct remaining work. (internal/providers/nomad/client.go:171, 159d54e4499d)
  • Latest release comparison: Inspected the Nomad client at v0.57.0 and resolved that tag to baa6c9a; it retains the unbounded control-call implementation and context-discarding Regions method. (internal/providers/nomad/client.go:171, baa6c9a783f5)
  • Previous finding addressed: The revised implementation durably advances the exact claim to submitting before RegisterJob. Failure flows through rollbackRegistration; an absent submitting job retains its claim, while a matching observed job follows fenced validation, deletion, and absence confirmation. This addresses the finding preserved from fix(nomad): bound control requests with durable registration recovery #1916 (comment). (internal/providers/nomad/registration.go:182, ab884aee666f)
  • Production-path behavior evidence: The complete captured PR body, sourceRevision 65d94658ee34e57c78c6a0240982d3e1ad658815601f2ad2a64726c1dbce367e, reports actual CLI execution through the real HTTP client against an owned loopback service. Warmup expired after approximately 121.6 seconds and completed owned cleanup; caller-canceled Run retained an exact recovery session, pending Status and initial Stop avoided deletion, and later Stop removed the original observed job. Five CLI calls produced 11 HTTP requests. This establishes transport and local recovery behavior, not live scheduler or long-running allocation execution. (ab884aee666f)
  • Regression and compatibility coverage: Read the permanent real-HTTP regression asserting PUT, GET, DELETE, GET after accepted-but-delayed registration, claim removal, cancellation, and primary exit code 1. Additional tests cover removed prepared claims preventing submission, changed claims preventing cleanup, legacy marker-free claims, unsupported marker versions, and recovery reporting without shared execution or cleanup authority. Tests were inspected, not executed during this read-only review. (internal/providers/nomad/registration_test.go:132, ab884aee666f)

Likely related people:

  • Peter Steinberger: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)
  • Vincent Koc: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)
  • Coy Geek: 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 (1 earlier review cycle)
  • reviewed 2026-09-06T14:25:09.600Z sha 9ddfc1a :: blocked before merge. :: [P2] Reconcile job acceptance before returning a registration timeout

Bound finite Nomad JSON requests without imposing a whole-request timeout on established exec streams. Persist registration identity before submission, retain uncertain outcomes for ownership-checked recovery, and preserve primary failure codes and existing setup rollback semantics.

Reuse the shared claim transaction and run-session owners. Cover accepted-but-delayed registration with a permanent HTTP regression and actual CLI timeout/cancellation proof; keep provider docs and Unreleased notes current.

Co-authored-by: Sebastien Tardif <sebtardif@ncf.ca>
@steipete steipete changed the title fix(nomad): bound stalled control JSON without cutting exec fix(nomad): bound control requests with durable registration recovery Sep 12, 2026
@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. and removed merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. 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 12, 2026
@steipete
steipete merged commit 94058fa into openclaw:main Sep 12, 2026
26 checks passed
@steipete

steipete commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Merge verification update for 94058fa:

The fresh, actual-merge focused race/static/docs/build gates passed, followed by actual CLI runs against a synthetic Nomad HTTP service. The normal registration timeout and interrupted-run cases both preserved exit 1; registration recovery, pending-claim retention, later owned cleanup, and timing/session output passed. All eleven HTTP requests/handlers and five CLI processes finished, with no remaining fixture claims. This is real CLI/HTTP transport proof, not a hosted Nomad scheduler or cloud-driver smoke.

Post-merge CI was not entirely green: https://github.com/openclaw/crabbox/actions/runs/34682158837 failed in the existing POSIX workspace-owner renewal fixture under coverage. Its three-second wall-clock lease reported EXPIRED; the log does not identify the exact scheduling or lock delay. That fixture does not call Nomad or the shared acquisition finalizer. I preserved the failed run and prepared the test-only timing repair at #2138. The follow-up merged at 751f6a4 after full CI passed, and fresh actual-merge normal/coverage/race fixture tests also passed. Production renewal/expiry policy is unchanged; this does not claim to fix #1712.

CI closeout is complete through that follow-up: all 12 main CI jobs, including coverage, passed at https://github.com/openclaw/crabbox/actions/runs/34685602834; the five connector and four CodeQL analysis jobs also passed. Source comparison from the Nomad merge to that verified main commit confirms the Nomad/shared provider code and CLI production code are unchanged; the only change in those components is the repaired test fixture. The original failed run has not been rerun or presented as successful. No hosted Nomad proof or serving-version/deployment claim is implied.

steipete added a commit that referenced this pull request Sep 12, 2026
Control only the positive fixture's protocol clock and synchronize on actual child startup. Verify renewal extends persisted expiry past the initial deadline while the real witness continues streaming and preserves exit 23. Bound test subprocess cleanup and identify the task-owned detached daemon before signaling it.

Production owner policy and negative coverage are unchanged. This repairs the timing-sensitive fixture observed after #1916; it does not claim to resolve #1712.

Repeated focused normal, coverage, and race tests passed locally; full CI, connector, CodeQL, and release checks passed for the exact PR head. Managed Codex review was P0 scoped-clean.
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.

2 participants