Skip to content

feat(plugins): add durable scheduler outbox foundation#9139

Draft
JordanTheJet wants to merge 17 commits into
zeroclaw-labs:masterfrom
JordanTheJet:feat/plugin-durable-outbox
Draft

feat(plugins): add durable scheduler outbox foundation#9139
JordanTheJet wants to merge 17 commits into
zeroclaw-labs:masterfrom
JordanTheJet:feat/plugin-durable-outbox

Conversation

@JordanTheJet

Copy link
Copy Markdown
Collaborator

Summary

  • Base branch: master (all contributions)
  • What changed and why:
    • Adds a host-owned durable plugin outbox in the existing canonical cron SQLite database, avoiding another path, backend, scheduler, or persistence configuration.
    • Defines only two host-understood work classes (scheduled and delivery), typed completion outcomes, and one private persisted wire format. Provider action strings and arbitrary guest jobs are deliberately absent.
    • Makes enqueue idempotent per exact PluginInstanceId and caller key; reusing a key for different kind, payload, secret references, or due time fails closed instead of silently changing work.
    • Claims due work atomically with exact-instance selection, expiring leases, random lease tokens, and attempt fencing so restarted or concurrent workers cannot both complete the same claim.
    • Applies bounded exponential backoff, typed RetryAfter, permanent failure, retry exhaustion, envelope/idempotency limits, and database constraints for impossible lifecycle rows.
    • Persists only caller-attested public JSON plus canonical unresolved SecretPropertyRef values. Plaintext resolved secrets have no constructor path into the durable envelope.
  • Scope boundary: This is the durable coordinator and schema foundation only. It has no guest WIT, background worker, provider adapter, external delivery, secret resolution, or new scheduler/config surface. Child PRs must supply typed workers through existing host services.
  • Blast radius: Adds an idempotent plugin_outbox table and index to the same <data_dir>/cron/jobs.db opened by existing cron storage. No job is created or processed unless a later host caller uses the coordinator.
  • Linked issue(s): Related [Bug]: disabling an agent does not stop its bound Discord channel #8013, Move optional channels & tools from compile-time feature flags to runtime plugins #8850, feat(plugins): add gateway and dashboard capability catalog #8909, and feat(channels): bind-wechat / bind-line CLI verbs #9015. Depends on feat(plugins): add typed event routing foundation #9138.
  • Labels: enhancement, runtime, tests, risk:high, size:XL

Testing (required)

How you can test (when useful)

  • Reviewer testing requested? N/A; the foundation has no production worker or user-facing command, and its concurrency/restart/SQLite invariants are deterministic automated tests.

How I tested

  • CI checks relied on and why they cover this change: Fresh required CI is expected to cover supported platforms, the full runtime regression suite, formatting, and workspace lint. The focused feature-enabled suite exercises every public coordinator transition against real temporary SQLite databases.
  • Known CI coverage gap, if any: No external delivery or real provider retry behavior is attempted because this PR intentionally contains no worker/adapter.
  • Commands run and tail output:
$ cargo test --locked -p zeroclaw-runtime --features plugins-wasm \
    cron::outbox::tests --lib -- --nocapture
running 11 tests
test result: ok. 11 passed; 0 failed; 0 ignored

$ cargo clippy --locked -p zeroclaw-runtime --lib \
    --features plugins-wasm -- -D warnings
# passed

$ cargo fmt --all -- --check && git diff --check
# clean
  • Beyond CI, what did you manually verify? Schema initialization is idempotent in an existing cron database; two workers claim once; an earlier row for another instance cannot block the requested instance; stale lease tokens are fenced after recovery; completion is terminal; retry-after uses the injected clock; retries exhaust; permanent failures never requeue; exact package/capability/binding isolation is enforced on claim and completion; oversized/duplicate envelopes and impossible rows are rejected.
  • If any command was intentionally skipped, why: Live-provider and background-worker testing belong to later adapter PRs; no such code exists here.

Security & Privacy Impact (required)

  • New permissions, capabilities, or file system access scope? Yes
  • New external network calls? No
  • Secrets / tokens / credentials handling changed? Yes
  • PII, real identities, or personal data in diff, tests, fixtures, or docs? No
  • Prompt injection or untrusted model-visible text introduced/changed? No
  • If any Yes, describe the risk and mitigation: The runtime may create a new table in its existing cron database. The durable envelope accepts a package-private public-data marker and validated unresolved secret references only; no resolved-secret type is accepted. Every row is structurally bound to exact package/capability/binding identity, size-bounded, and accessed through parameterized SQL, atomic transactions, and opaque lease tokens. Later workers must resolve secrets transiently through the scoped service at delivery time.

Compatibility (required)

  • Backward compatible? Yes
  • Config / env / CLI surface changed? No
  • Rust/MSRV/toolchain floor changed? No
  • If backward compatibility is No or either surface/floor question is Yes: N/A. Schema creation is additive/idempotent, and no existing cron row or API is changed.

Rollback (required for medium/high-risk PRs)

  • Fast rollback command/path: Revert this PR. The unused additive table may remain in cron/jobs.db; leaving it preserves any queued data if a child PR is also rolled back and later restored.
  • Feature flags or config toggles: Existing plugins-wasm feature; no new runtime toggle or worker.
  • Observable failure symptoms: SQLite migration/open errors mentioning plugin_outbox, rejected leases, invalid persisted envelopes, or a later worker receiving LeaseRejected/RetryExhausted.

Supersede Attribution (required only when Supersedes # is used)

Not applicable.

@JordanTheJet JordanTheJet added the enhancement New feature or request label Jul 18, 2026
@github-actions github-actions Bot added ci Auto scope: CI/workflow/hook files changed. docs Auto scope: docs/markdown/template files changed. dependencies Auto scope: dependency manifest/lock/policy changed. core Auto scope: root src/*.rs files changed. config Auto scope: src/config/** changed. cron Auto scope: src/cron/** changed. runtime Auto scope: src/runtime/** changed. tool Auto scope: src/tools/** changed. security:secrets Auto module: security/secrets changed. runtime:wasm Auto module: runtime/wasm changed. labels Jul 18, 2026
@JordanTheJet
JordanTheJet force-pushed the feat/plugin-durable-outbox branch from 11ba8be to fddfc63 Compare July 18, 2026 08:45
@JordanTheJet
JordanTheJet force-pushed the feat/plugin-durable-outbox branch from fddfc63 to b35149d Compare July 18, 2026 09:56
@JordanTheJet

Copy link
Copy Markdown
Collaborator Author

Restacked the durable outbox foundation onto final #9138. New head: b35149df; former head fddfc633 remains at JordanTheJet/zeroclaw:backup/plugin-stack-pre-scope-fix-20260718/durable-outbox. The review slice is unchanged and fresh exact-head CI is running.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci Auto scope: CI/workflow/hook files changed. config Auto scope: src/config/** changed. core Auto scope: root src/*.rs files changed. cron Auto scope: src/cron/** changed. dependencies Auto scope: dependency manifest/lock/policy changed. docs Auto scope: docs/markdown/template files changed. enhancement New feature or request runtime:wasm Auto module: runtime/wasm changed. runtime Auto scope: src/runtime/** changed. security:secrets Auto module: security/secrets changed. tool Auto scope: src/tools/** changed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant