Skip to content

(MOT-4073) refactor(workers): adopt engine recoverable triggers — remove hand-rolled pending-bind dances#523

Merged
andersonleal merged 4 commits into
mainfrom
andersonleal/mot-4073-adopt-engine-recoverable-triggers-remove-hand-rolled-pending
Jul 16, 2026
Merged

(MOT-4073) refactor(workers): adopt engine recoverable triggers — remove hand-rolled pending-bind dances#523
andersonleal merged 4 commits into
mainfrom
andersonleal/mot-4073-adopt-engine-recoverable-triggers-remove-hand-rolled-pending

Conversation

@andersonleal

@andersonleal andersonleal commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

Engine #1962 "recoverable triggers" (15dc993eb) parks a register_trigger whose trigger type isn't registered yet and activates it automatically when the type registers, re-parking and re-activating across owner restarts. That makes every per-worker "watch the registry + probe engine::triggers::list + AtomicBool claim + retry the bind" dance dead weight — including the latent AtomicBool bug where a harness restart never re-bound the hook.

One commit per worker, −659/+97:

  • web — deleted web::on-registry-changed + its two engine::*-available bindings, the readiness probe, the claim flag, and the explicit registry-changed wire schemas; setup_harness_hooks is now one bind(...) call.
  • workflow — kept all three hook binds (turn-completed → wake, pre-trigger → stamp-reply, pre-generate → inject-guidance), dropped the watching/claim machinery around them.
  • scrapling — Python port of the same cleanup (BindOnce, try_bind_sync/async, warm-start probe, scrapling::on-registry-changed all gone); tests rewritten for the one-shot bind.
  • memory — deleted the retry_bindings delta-attribution loop; the existing one-shot bind_best_effort pass suffices for all four seams. Kept a minimal queue::define poll: enqueue rejects undefined queues (queue/src/runtime.rs), and trigger parking doesn't create queues.

Audited, no change (legit ongoing registry subscriptions, not deferred binds): rbac-proxy catalog refresh, harness discovery, lsp completions.

Guard

Requires an engine built after 15dc993eb (≥ 0.21.8) — on older engines a one-shot bind regresses to the old silent drop. Noted in each touched worker's README; the worker manifest schema cannot express an engine floor. Review also surfaced an engine-side parity gap (bind dropped instead of parked when the registrator fails on the found-type path) — filed as MOT-4081.

Verification

  • cargo fmt --check + clippy --all-targets -- -D warnings + cargo test green for web (102), workflow (137), memory (75); ruff + pytest green for scrapling (79).
  • Grep gate: no on-registry-changed / try_bind remnants outside legit subscriptions.
  • Live against a post-15dc993eb engine:
    • Cold start: web started with harness down → bind parked → harness start → activated (instance_count 2→3).
    • Warm start: worker start with harness up → bound immediately.
    • Harness restart: worker untouched → binding re-parked and re-activated (the case the AtomicBool dance lost).
    • workflow's three hooks each incremented their type's instance count; web::inject-guidance exercised directly.

Closes MOT-4073.

Summary by CodeRabbit

  • New Features

    • Web, workflow, and scrapling guidance is now registered through one-shot startup hooks and can activate when supported harness triggers become available.
    • The memory worker now ensures its extraction queue exists in the background.
  • Bug Fixes

    • Improved startup reliability by avoiding repeated trigger readiness polling.
    • Guidance hooks fail open when unavailable, preventing worker startup disruption.
  • Documentation

    • Added documentation covering hook activation, worker presence gating, and engine compatibility requirements.

…erable triggers

The engine now parks a register_trigger for an unregistered trigger type as
a pending intent and activates it when the type registers, re-parking and
re-activating across harness restarts (recoverable triggers, iii #1962).
Delete the web::on-registry-changed watcher, its two engine::*-available
event bindings, the engine::triggers::list warm-start probe, the AtomicBool
bind claim, and the explicit registry-changed wire schemas; bind the
pre-generate guidance hook once at startup. Requires engine >= 0.21.8 —
noted in the README (older engines silently drop the bind).
… recoverable triggers

Keep the three harness hook binds (turn-completed → wake, pre-trigger →
stamp-reply, pre-generate → inject-guidance) but drop the watching/claim
machinery around them: the workflow::on-registry-changed watcher, its two
engine::*-available event bindings, the engine::triggers::list readiness
probe, and the AtomicBool bind claim. The engine parks binds to unregistered
trigger types and activates them when the type registers, re-parking across
harness restarts (recoverable triggers, iii #1962). Requires engine >=
0.21.8 — noted in the README (older engines silently drop the binds).
…e recoverable triggers

Python port of the same cleanup: delete scrapling::on-registry-changed and
its two engine::*-available bindings, the engine::triggers::list warm-start
probe, the BindOnce claim, try_bind_sync/try_bind_async, and the
registry-changed wire schemas; register_trigger the pre-generate hook once
at startup. The engine parks the bind until the harness registers the type
and re-parks across harness restarts (recoverable triggers, iii #1962).
Requires engine >= 0.21.8 — noted in the README.
…e poll

The delta-attribution retry loop (snapshot engine::triggers::info counts,
re-request, confirm on increase) existed only because binds to
not-yet-registered sibling trigger types were silently dropped. The engine
now parks them as pending intents and activates them when the owner
registers, re-parking across restarts (recoverable triggers, iii #1962), so
the one-shot bind pass suffices for all four seams. queue::define is the one
guarantee parking does not cover — it is an RPC the queue worker must serve
before enqueue works — so keep a minimal poll for just that. Requires
engine >= 0.21.8 — noted in the README.
@vercel

vercel Bot commented Jul 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
workers Ready Ready Preview, Comment Jul 16, 2026 9:42pm
workers-tech-spec Ready Ready Preview, Comment Jul 16, 2026 9:42pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

skill-check — worker

0 verified, 44 skipped (no docs/).

Layer Result
structure
vale
ai
render

Four for four. Nicely done.

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Workers now use one-shot harness hook bindings instead of readiness polling and registry-event retries. Memory separately retries extraction queue definition until successful, with updated documentation and focused scrapling tests.

Changes

One-shot harness bindings

Layer / File(s) Summary
Direct hook binding implementation
scrapling/src/guidance.py, web/src/configuration.rs, workflow/src/configuration.rs
Hook setup now binds directly during startup with fail-open behavior, removing probes, retry handlers, registry wiring, and atomic coordination.
Synchronous startup wiring
web/src/main.rs, workflow/src/main.rs
Web and workflow call hook setup without awaiting it and describe parked bindings until harness trigger types are registered.
Binding contract validation
scrapling/tests/test_guidance.py, scrapling/README.md, web/README.md, workflow/README.md, memory/README.md
Scrapling tests validate one-shot binding, typed schemas, and fail-open configuration; documentation records gating and engine compatibility behavior.

Memory boot readiness

Layer / File(s) Summary
Binding and extraction queue startup
memory/src/main.rs, memory/README.md
Memory requests required bindings once and starts a retrying extraction-queue definition task that logs after success.

Estimated code review effort: 4 (Complex) | ~45 minutes

Poem

I’m a rabbit with bindings to spare,
One hop at startup, then wait in the air.
Queues retry till they’re ready to run,
Hooks wake when their triggers are spun.
Fail-open whiskers twitch with delight—
Clean little boot paths, cozy and light!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the core change: workers now adopt engine recoverable triggers and remove custom pending-bind retry logic.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch andersonleal/mot-4073-adopt-engine-recoverable-triggers-remove-hand-rolled-pending

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scrapling/src/guidance.py`:
- Line 122: Update the log message in the guidance binding setup to state that
binding was requested rather than implying guidance injection is already active.
Preserve the existing log call and context while using wording such as “binding
requested.”
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b2a97211-9aab-4d1d-99a8-0828315a1b03

📥 Commits

Reviewing files that changed from the base of the PR and between 782b331 and ce983ff.

📒 Files selected for processing (11)
  • memory/README.md
  • memory/src/main.rs
  • scrapling/README.md
  • scrapling/src/guidance.py
  • scrapling/tests/test_guidance.py
  • web/README.md
  • web/src/configuration.rs
  • web/src/main.rs
  • workflow/README.md
  • workflow/src/configuration.rs
  • workflow/src/main.rs

Comment thread scrapling/src/guidance.py
# on_error fail_open is MANDATORY: pre_generate defaults fail-CLOSED, and a
# missing guidance line must never abort a turn.
iii.register_trigger({"type": HOOK_TRIGGER_TYPE, "function_id": HOOK_ID, "config": {"on_error": "fail_open"}})
log.info("scrapling pre-generate hook bound (guidance injection active)")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Log the binding request rather than immediate activation.

On a cold start, Line 122 executes while the intent may still be parked, so “guidance injection active” is inaccurate until the harness registers its trigger type. Use wording such as “binding requested.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scrapling/src/guidance.py` at line 122, Update the log message in the
guidance binding setup to state that binding was requested rather than implying
guidance injection is already active. Preserve the existing log call and context
while using wording such as “binding requested.”

@andersonleal
andersonleal merged commit 2692326 into main Jul 16, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants