feat: add automation PR1 contract surface#960
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (7)
🚧 Files skipped from review as they are similar to previous changes (6)
📝 WalkthroughWalkthroughAdds a typed in-memory Automation feature: Zod schemas, ID types, validation, lifecycle APIs (create/list/get/update/remove/run), Hono REST routes, an "automate" tool, event publishing/replay inclusion, fixtures, SDK event typings, integration into instance routes and tool registry, and extensive tests. ChangesAutomation System Feature
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces a new automation framework, including definitions, runs, API routes, and tools. It defines Zod schemas for automation definitions and runs, implements in-memory state management, registers Hono routes under /automation, and adds an automate tool along with generated SDK types and clients. The feedback highlights three key areas for improvement: resolving a pagination reset bug when a cursor is not found in runs, adding an optional now timestamp parameter to update for deterministic testing, and aligning the Effect schema constraints in the automate tool with the Zod schema to prevent unhandled validation crashes.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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 `@packages/opencode/src/automation/index.ts`:
- Around line 103-121: The Definition and Tombstone Zod schemas currently allow
unknown keys to be stripped; update the exported contract schemas to be strict
by adding .strict() to the object schemas so extra properties are rejected.
Specifically, make each branch of the discriminated union for Definition (the
z.object({...}) for kind "oneshot" and for kind "recurring") call .strict(), and
also call .strict() on the Tombstone z.object(...) schema so unknown fields
cause validation errors; keep the existing .meta(...) and exported type
inference unchanged.
In `@packages/opencode/src/id/id.ts`:
- Around line 95-98: The timestamp(id: string) function currently slices out hex
without validating the id shape; update timestamp to first verify the separator
(variable separator) is found (separator > -1), that the extracted hex substring
(variable hex) has the expected length (at least 12 characters) and matches a
hex regexp /^[0-9a-fA-F]+$/, and if any check fails throw a clear Error
indicating an invalid ID format; then safely convert the hex to BigInt (wrap
parse in try/catch or validate before BigInt) and proceed to compute and return
the timestamp as before.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 8e45e73a-b4e3-4c29-89e8-e2fa81fe81b3
⛔ Files ignored due to path filters (2)
packages/sdk/js/src/v2/gen/sdk.gen.tsis excluded by!**/gen/**packages/sdk/js/src/v2/gen/types.gen.tsis excluded by!**/gen/**
📒 Files selected for processing (17)
packages/opencode/script/route-inventory.tspackages/opencode/src/automation/fixtures.tspackages/opencode/src/automation/index.tspackages/opencode/src/id/id.tspackages/opencode/src/server/event-replay.tspackages/opencode/src/server/instance/automation.tspackages/opencode/src/server/instance/index.tspackages/opencode/src/server/routes/instance/index.tspackages/opencode/src/tool/automate.tspackages/opencode/src/tool/registry.tspackages/opencode/test/server/automation-event-fixtures.test.tspackages/opencode/test/server/automation-routes.test.tspackages/opencode/test/server/event-replay.test.tspackages/opencode/test/session/id-monotonicity.test.tspackages/opencode/test/tool/automate.test.tspackages/opencode/test/tool/registry.test.tspackages/sdk/js/src/v2/event-types.test-d.ts
|
Actionable comments posted: 0 |
Closes the backend gaps left after PR1-5 so the PR6/PR7 frontend slice can
build on a complete, stable automation contract.
Goal / change boundary:
- model { providerID, modelID } now required on every AutomationDefinition;
runner passes it through so runs no longer depend on the runtime model
fallback chain (which drifts across restarts).
- variant? optional reasoning/effort, validated against the live provider
catalog on create/update; invalid variant returns 422 instead of failing
late in the run.
- stop.kind === "condition" rejected at create/update with structured
{ field: "stop", message: "unsupported_stop_condition" } (scheduler never
schedules condition stops, so accepting them only leaked dead UI surface).
- Derived fields nextFireAt / nextFires / failureStreak populated at
create/update and refreshed after every terminal run; scheduler re-publishes
automation.definition.updated with a bumped revision for global-sync.
- cron validation consolidated into src/automation/cron.ts; scheduler and
derived both consume it.
- automate tool schema picks up model/variant + Provider-backed create-time
validation.
- Migration 20260601100000 drops pre-release rows so model can be NOT NULL.
Verification:
- CI green on 5e177ba (unit-opencode flake on an unrelated VCS-routes
20s timeout cleared on rerun).
- Local: 129 automation tests (routes/runner/scheduler/event-fixtures/tool/
cron) + 35 session processor tests pass.
Review follow-ups addressed:
- codex: scheduler self-loop guard keyed by id:revision (not id alone).
- P2: recordRunOutcome retries on ConflictError instead of silently dropping
the run outcome.
- CodeRabbit: 422 create test uses a guaranteed-invalid model; fixed sleeps
replaced with terminal-state polling.
- Test seam for the ConflictError path moved off the public Automation API
into an internal __test_hooks module.
Residual risk / deferred (tracked, not blocking PR6):
- needs_user_input / loop_gate run-error codes remain reserved; producing
them needs prompt-loop semantics changes.
- Session.automationID reverse lookup deferred (session-contract migration).
- stop=condition kept in the schema layer for SDK shape parity but rejected
at validate time; collapses once a condition evaluator lands.
Linked: issue #950; follows PR #960 #983 #984 #998 #1004; unblocks PR6/PR7.
Summary
Adds PR1 of #950: the frozen automation contract, an in-memory definition/run store, CRUD and run-history route signatures, the hidden
automatetool, replayable automation events, fixtures, and regenerated v2 SDK types.Why
Issue #950 needs the backend/frontend seam frozen before execution, scheduler, persistence, or UI work can safely build on it. This PR intentionally does not execute automations and keeps the agent-facing tool hidden until the manageability UI slice.
Related Issue
Closes part of #950.
Human Review Status
Pending
Review Focus
Please focus on the frozen contract shape:
AutomationDefinition,AutomationRun, terminal reason mapping, route operation IDs, the default-hiddenautomatetool boundary, and whether the replayable event snapshots are small and complete enough for PR6 frontend work.Risk Notes
No scheduler, persistence, runNow execution, or frontend wiring is included.
AutomationRunis now frozen as a six-state ledger shape:scheduled,running,awaiting_input,succeeded,failed, andstopped.failedis the count-consuming terminal failure bucket and carries a non-null run error, including hard-stop causes such asstep_capandloop_gate;stoppedis the no-count terminal bucket forprevious_run_awaiting_input,missed_schedule,cancelled,expired, andblocker_lost.awaiting_inputis the frozen run state for both permission and question blockers;blocker.kinddistinguishespermissionfromquestion, and blocker payloads carry only the locator (requestIDorcallID) because the run-levelsessionIDowns the session.runNowonly creates a scheduled run record as a PR1 contract stub. The default SDK surface undersrc/genis unchanged by the existing SDK build script; the app-facing/current v2 SDK surface is regenerated and type-tested. No visible UI changed, so screenshots are not applicable.How To Verify
Screenshots or Recordings
Not applicable: no visible UI changes.
Checklist
bug,enhancement,task,documentation. Type labels are author-added; the labeler bot does NOT assign them. Add the label in the GitHub UI, then tick this.app,ui,platform,harness,ci. The labeler bot assigns these on PR open based on changed paths. Confirm the bot's choice (or override if wrong), then tick this.P0,P1,P2,P3. The priority-triage bot suggests one on PR open. Confirm or override, then tick this.Pending,Approved by @<reviewer>, orNot required: <reason>(default isPending; "not required" is restricted to bot-authored low-risk PRs).dev, and my PR title and commit messages use Conventional Commits in English.Summary by CodeRabbit
New Features
Tests