Skip to content

feat(lab): CL-08 bounded automation with trusted live-route dispatch - #1447

Merged
Wibias merged 67 commits into
devfrom
feat/cl-08-lab-automation
Aug 11, 2026
Merged

feat(lab): CL-08 bounded automation with trusted live-route dispatch#1447
Wibias merged 67 commits into
devfrom
feat/cl-08-lab-automation

Conversation

@Wibias

@Wibias Wibias commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Starting SHA: 849ab5e35fdacddb4d47416c00a315111c78f97e
  • Head SHA: e5332346943526b7b833e3fb202aecd2af11425e
  • Prerequisite: CL-07 task-effectiveness producer (merged on dev via feat(lab): CL-07 task effectiveness evidence #1438)
  • CL-08: Bounded, default-off Compatibility Lab automation that plans and dispatches to existing CL-01 protocol harness and CL-03 trusted live-route execution without minting provider authority.

Architecture

  • Policy, routes, state, and queue in src/lab/automation/ with hard ceilings, cross-process state serialization, and crash recovery.
  • Deterministic planner + run-key deduplication + exact-subject/manifest freshness planning.
  • Budgets, concurrency, and cooldowns enforced around atomic dispatch claims.
  • Closed dispatch seam (dispatch.ts) to CL-01 / CL-03 only; task-effectiveness background remains disabled.
  • Management API (/api/lab/automation) and CLI (ocx lab automation …, ocx lab run …).

Default-off behavior

  • automation-policy.json defaults to enabled: false and all layers off.
  • Scheduler starts only when policy is explicitly enabled at server startup.
  • Read surfaces do not enqueue or dispatch work.
  • Manual runs execute independently of the background automation enable flag.

Production trusted live dispatch

  • src/server/index.ts creates createProductionLabRouteExecutor({ configDir, loadConfig }) at startup and passes it through setLabAutomationDispatchDeps.
  • Production executor is host-issued (createHostIssuedLabRouteExecutor) and routes through CL-03 credential lease, pinned transport, and observation normalization (src/lib/lab-live-route-production.ts).
  • CL-08 never constructs provider HTTP transport, credentials, or destination bypasses; missing executor fails closed as route_ineligible with zero provider traffic.
  • Queued live runs are revalidated against the current exact subject, suite/scenario manifests, and run-key contract before provider execution. Contract drift blocks as run_contract_changed with no live request.
  • Live evidence persistence is gated on executionAuthority === "trusted_route"; cancellation is checked again before persistence.

Independent review hardening

The initial draft was independently reviewed before ready-for-review. The correction pass added regressions and fixes for:

  • freshness leaking between different route subjects;
  • queued-run TOCTOU / stale contract identity;
  • manual runs being stranded while automation was disabled;
  • terminal history saturating the queue and unbounded queued accumulation;
  • queued work surviving disabled layers or removed route enrollment;
  • open/weak automation JSON schemas and unsafe persisted counters;
  • cancellation persisting live compatibility evidence;
  • fixed-window budget accounting and cooldown cleanup;
  • missing/incompatible projection causing accidental reprobes;
  • cross-process automation-state.json write races;
  • CLI --protocol handling and trusted live manual dispatch.

Scheduled private-network probing still requires both explicit route enrollment in automation-routes.json and the provider's existing allowPrivateNetwork: true; CL-03 destination/SSRF policy remains authoritative.

Task-effectiveness background

  • Deferred: taskEffectivenessBackgroundEnabled remains false; unattended CL-07 execution is not proven safe. Manual CL-07 paths unchanged.

Persistence / security / privacy

  • Automation JSON stores policy refs and bounded orchestration state only (no credentials, URLs, headers, or executor objects).
  • Policy/routes/state schemas are closed and strictly validated.
  • Automation state mutations use a bounded cross-process lock to prevent CLI/server lost updates.
  • Management PUT cannot inject a trusted executor; authority is process-local host wiring only.

Non-goals

  • CL-09, UI, routing-profile mutation, production routing changes from scheduler activity, arbitrary cron/shell execution.

Known limitation

  • Trusted automation contract advertises live_transport harness feature only (CL08_TRUSTED_LIVE_HARNESS_FEATURES). Scenarios requiring inert tools, MCP stubs, or other harness features are not planned until a broader trusted contract is defined.

Verification

Current rebased head e5332346943526b7b833e3fb202aecd2af11425e on dev 849ab5e35fdacddb4d47416c00a315111c78f97e:

  • Rebase ancestry: 57 commits ahead, 0 behind, merge base equals current dev.
  • React Doctor: success on the rebased head.
  • Cross-platform CI: queued/running after the rebase; exact-head results pending.
  • The pre-rebase head had all Linux test shards and gates green, including the CL-08 cooldown regression fix, but those results are not treated as exact-head verification after the rebase.

Focused CL-08 coverage includes tests/lab-automation.test.ts, tests/lab-automation-review-regressions.test.ts, and the CodeRabbit regression suites.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults.
  • Independent review blockers fixed with regression coverage.
  • Final exact-head cross-platform run fully completed after rebase.
  • CodeRabbit / maintainer review complete.

Do not merge yet.

Summary by CodeRabbit

  • New Features
    • Added Compatibility Lab automation for protocol and approved live-route checks, disabled by default.
    • Added CLI and management controls for policies, routes, status, run listing, manual execution, and cancellation.
    • Added scheduling, queueing, persistence, budgets, cooldowns, recovery, freshness handling, and paginated results.
    • Added trusted live-route execution with cancellation and secure credential handling.
  • Bug Fixes
    • Improved shutdown, crash recovery, validation, and fail-closed behavior.
  • Tests
    • Added comprehensive coverage for workflows, limits, security, cancellation, retries, and live-route execution.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

CL-08 adds default-off automation for protocol and trusted live-route compatibility evidence. It adds policy validation, deterministic planning, bounded persistence, dispatch, scheduling, management APIs, CLI controls, trusted-route wiring, cancellation, recovery, budgets, cooldowns, and regression tests.

Changes

Compatibility Lab automation

Layer / File(s) Summary
Policy, contracts, and persistence
devlog/_plan/260807_compatibility_lab/008_cl08_automation.md, src/lab/automation/types.ts, src/lab/automation/policy.ts, src/lab/automation/constants.ts, src/lab/automation/persistence.ts, src/lab/automation/run-key.ts, src/lab/automation/index.ts, src/lab/paths.ts, src/lab/index.ts, src/lab/query/freshness.ts, src/lab/query/latest-observation.ts, src/lab/projection/schema.ts
Defines versioned automation contracts, default-off policy validation, deterministic identities, exact freshness lookup, bounded JSON persistence, filesystem locking, and public exports.
Planning, queueing, budgets, and recovery
src/lab/automation/planner.ts, src/lab/automation/queue.ts, src/lab/automation/budgets.ts, src/lab/automation/cooldown.ts, src/lab/automation/recovery.ts
Plans protocol and live-route runs with freshness, deduplication, cooldown, budget, route, queue, and concurrency rules. Recovers interrupted runs.
Dispatch and trusted live execution
src/lab/automation/dispatch.ts, src/lab/automation/route-context.ts, src/lab/live/executor.ts, src/lib/lab-live-route-production.ts
Dispatches supported protocol and live-route runs, validates contracts, propagates cancellation, and persists trusted observations.
Scheduler orchestration and server lifecycle
src/lab/automation/orchestrator.ts, src/server/index.ts, src/server/lifecycle.ts
Adds scheduler ticks, queue reconciliation, dispatch finalization, cancellation, startup recovery, startup wiring, and shutdown handling.
Management API and CLI controls
src/server/management/lab-automation-routes.ts, src/server/management-api.ts, src/cli/lab.ts
Adds status, policy, route, run-listing, manual-run, and cancellation controls.
Regression coverage and traceability
tests/lab-automation*.test.ts, devlog/_plan/260807_compatibility_lab/008_cl08_automation.md
Covers disabled operation, planning, persistence, budgets, cooldowns, cancellation, security boundaries, trusted execution, recovery, shutdown, pagination, and implementation scope.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested reviewers: ingwannu, lidge-jun

Sequence Diagram(s)

sequenceDiagram
  participant Server as startServer
  participant Scheduler as startLabAutomationScheduler
  participant Orchestrator as runLabAutomationTick
  participant Dispatch as dispatchLabAutomationRun
  participant Executor as createProductionLabRouteExecutor
  participant Lifecycle as drainAndShutdown
  Server->>Scheduler: start when persisted policy is enabled
  Scheduler->>Orchestrator: run planned compatibility ticks
  Orchestrator->>Dispatch: execute a claimed run
  Dispatch->>Executor: run trusted live-route evidence collection
  Lifecycle->>Scheduler: stop scheduler and request shutdown
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.93% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: bounded CL-08 automation with trusted live-route dispatch.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cl-08-lab-automation

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.

@github-actions

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

@github-actions github-actions Bot added the enhancement New feature or request label Aug 11, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 31

🤖 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 `@devlog/_plan/260807_compatibility_lab/008_cl08_automation.md`:
- Line 443: Change the two “Attributable compatibility result” headings at the
affected locations from level-3 (`###`) to level-2 (`##`), preserving the
heading text and surrounding content to satisfy MD001.
- Around line 673-688: Standardize the budget reason code across the
observability list, Section 7, and the persisted contract used by
src/lab/automation/persistence.ts: choose one canonical value and replace the
other occurrences, ensuring callers can persist the listed reason without
triggering invalid_state.

In `@src/cli/lab.ts`:
- Around line 352-373: Update the `enable` case to preserve the stored
`liveRouteCompatibility` value from `loadLabAutomationPolicy(configDir)` when
neither `--protocol` nor `--live` is provided. Keep explicit flag behavior
unchanged, while avoiding replacement of the previously enabled live layer with
false.
- Around line 367-372: Update the CLI automation enable/disable output near
saveLabAutomationPolicy and buildLabAutomationStatus so it does not use
status.schedulerRunning, which reflects only the CLI module’s local timer.
Report the persisted scheduler intent from the saved policy/configuration
instead, or omit the scheduler-running field from CLI output while preserving
the existing daemon behavior.

In `@src/lab/automation/budgets.ts`:
- Around line 14-27: Prevent budget evidence from being evicted: update
evictOldestTerminal and trimTerminalRuns to receive now from enqueuePlannedRuns
and both trim call sites, and exclude terminal records whose numeric startedAt
remains within the current LAB_AUTOMATION_HARD_MAX.budgetWindowMs. Preserve
existing eviction rules for older records, and document trimTerminalRuns’ keepMs
dependency. Add a focused regression test near the existing budget tests that
fills maxPersistedRuns with recent completions, rolls the window, and verifies
runBudgetRemaining never exceeds the true unspent budget.

In `@src/lab/automation/cooldown.ts`:
- Around line 8-19: Update setCooldown to accept the scheduler-provided now
value, remove expired entries from cooldownUntilByKey before inserting the new
key, and enforce the persistence hard limit by refusing new scheduling when
active entries already reach that limit instead of returning invalid state.
Preserve existing active cooldown entries and use the established persistence
limit symbol.

In `@src/lab/automation/dispatch.ts`:
- Around line 121-124: Update the dispatch flow around persistConformanceResult
and rebuildLabProjection to apply the newly persisted observation incrementally
instead of replaying the full ledger after every run. Add or reuse an
incremental projection-update operation, and make rebuildLabProjection perform a
full replay only when the projection is missing or incompatible; apply this
behavior consistently in both dispatch branches.

In `@src/lab/automation/orchestrator.ts`:
- Around line 258-259: Add a concise comment immediately above the dispatch loop
controlled by maxDispatches in the orchestration flow, explicitly stating that
dispatchLabAutomationRun is awaited sequentially and maxConcurrentRuns
represents the number of sequential dispatches per tick here, not parallel
concurrency. Preserve the existing loop and await behavior unchanged.
- Around line 210-213: Update the cancellation branch in the run orchestration
flow around transitionRun so it calls setCooldown before returning the terminal
cancelled state, using the cancelled code and the same cooldown mechanism as the
other terminal paths. Inspect cooldownForFailure and ensure cancelled resolves
to a positive defined duration rather than the zero default.
- Around line 167-192: Replace the 30-second time-based eviction in
acquireStateLock with ownership-safe locking, preferably an OS lock tied to the
owning descriptor or equivalent fencing. Ensure a paused holder’s lock cannot be
removed or bypassed by another process, and release only the lock owned by the
current descriptor so the mutateLabAutomationState read-modify-write cycle
remains serialized.

In `@src/lab/automation/persistence.ts`:
- Around line 148-182: Update acquireStateLock to prevent stale-lock reclamation
based solely on mtime, using an ownership mechanism that cannot classify a
paused or suspended live owner as dead before deleting its lock. Ensure lock
release verifies that the lock is still owned by the acquiring process before
unlinking it, so a later owner’s lock cannot be removed.
- Around line 247-321: Extend the state-loading flow around normalizeRunRecord
to validate whole-state lifecycle invariants after records are normalized and
before returning state. Reject duplicate runId values, more than one queued or
running record per runKey, and any state-specific timestamp or terminal-field
combinations that violate the lifecycle contract. Ensure violations throw
LabAutomationError with invalid_state so corrupt persistence fails closed.

In `@src/lab/automation/planner.ts`:
- Around line 153-157: Update both planner sites in
src/lab/automation/planner.ts: lines 153-157 in the caseRecord flow and lines
230-234 in planLiveScenarios. Resolve suite-level and scenario-level maxAgeMs
independently, then pass those distinct values to effectiveMaxAgeMs before
freshnessReason; prioritize the planLiveScenarios change to prevent unnecessary
provider requests.
- Around line 199-229: Batch observation reads in the planner instead of calling
latestMatchingObservationCompletedAt inside the nested route/scenario loop. In
the route planning flow around planProtocolScenarios and the live scenario loop,
fetch one projection per (layer, subjectId), reduce results into a lookup keyed
by suite and scenario identity, and reuse it for freshness checks while
preserving deterministic, side-effect-free planning behavior.
- Around line 81-93: Update latestMatchingObservationCompletedAt to apply the
identity’s version and digest predicates plus the relevant exclusion predicate
within the database query before selecting a result. Return the newest usable
observation using completed_at/event_id descending order with LIMIT 1, and keep
this query’s limit independent of LAB_AUTOMATION_HARD_MAX.maxPersistedRuns.

In `@src/lab/automation/policy.ts`:
- Around line 86-100: Update the three concurrency validations in the policy
parser—maxConcurrentRuns, maxConcurrentLiveRuns, and
maxConcurrentRunsPerRoute—to require positive integers rather than non-negative
integers, using the existing positive-integer assertion if available. Leave
maxRunsPerHour and maxLiveRequestsPerHour unchanged so they continue accepting
zero.

In `@src/lab/automation/queue.ts`:
- Around line 164-175: Move the terminal-run retention duration into the
LAB_AUTOMATION_HARD_MAX definition in constants.ts, alongside blockedCooldownMs,
using a named retention setting. Update trimTerminalRuns to read that
centralized value instead of the hard-coded seven-day calculation, keeping
terminal records retained for at least the configured cooldown duration.

In `@src/lab/automation/runs-query.ts`:
- Around line 21-24: Update listLabAutomationRuns so a non-empty cursor absent
from state.runs returns the typed invalid_cursor error instead of resetting
start to zero; preserve normal pagination for known cursors. Map invalid_cursor
to HTTP 400 in the lab automation route handler, and add unknown and evicted
cursor coverage in the lab automation tests. If cursor recovery is needed,
encode and use the (createdAt, runId) sort boundary.

In `@src/lib/lab-live-route-production.ts`:
- Around line 56-58: Update the OAuth error handling around the catch block to
distinguish non-credential token-endpoint failures from missing or expired
credentials: classify network or other harness-side failures as TransportError
code "harness_failure", while preserving "auth_blocked" for credential-related
failures. Keep the thrown message generic and ensure it contains no error
details or token material; update only the relevant classification path in the
surrounding authentication flow.
- Around line 84-102: Move the input.initiatingRequest validation to the
beginning of the executor callback, before createCredentialLease and
buildLabProviderAuthHeaders. Preserve the existing TransportError behavior, and
only create the lease, auth headers, sender, and transport after the guard
succeeds.
- Around line 52-58: In the OAuth branch of the route handling flow, validate
routeContext.baseUrl with URL before calling getValidAccessTokenSnapshot, and
reject any protocol other than https: with the existing auth_blocked
TransportError path. Leave non-OAuth providers unchanged so http: remains
supported there.

In `@src/server/index.ts`:
- Around line 1656-1668: Update src/server/index.ts lines 1656-1668 to release
the dependencies established by startServer from its stop override at lines
1592-1613, preventing another server from redirecting the active scheduler;
update src/lab/automation/orchestrator.ts lines 335-341 so
resetLabAutomationSchedulerStateForTests clears both dispatchDeps and
schedulerTimer. Add the release function beside setLabAutomationDispatchDeps and
invoke it from both lifecycle paths.

In `@src/server/lifecycle.ts`:
- Around line 450-451: Update the shutdown flow containing
requestLabAutomationShutdown and stopLabAutomationScheduler to join in-flight
automation runs before returning. Expose an in-flight count or settle promise
from the automation orchestrator, then await it with the existing
drainAndShutdown deadline, preserving the current call order and bounded-timeout
behavior.

In `@src/server/management/lab-automation-routes.ts`:
- Around line 97-103: Guard the decodeURIComponent call in the
cancelLabAutomationRun route, using the cancelMatch/runId handling to catch
malformed percent-encoding and return a 400 automationErrorResponse for an
invalid run id. Preserve the existing cancellation and 404 behavior for
successfully decoded run IDs.
- Around line 146-180: Validate and normalize both policy and routes payloads
before persisting either update in the PUT handler. Restructure the flow around
loadLabAutomationPolicy, normalizeLabAutomationPolicyV1, and
saveLabAutomationRoutes so route validation runs first, then save the policy and
routes only after both inputs succeed; preserve applySchedulerPolicy after the
writes and keep invalid requests free of partial disk updates.
- Around line 126-136: Decouple the manual-run route’s submission response from
provider dispatch: update enqueueManualLabRun and its orchestration path so the
route receives and returns a queued record immediately, while a guaranteed
background worker performs dispatch. Ensure the background dispatch is started
reliably without awaiting it in the request handler, and preserve the existing
202 response shape for the queued manual run.

In `@tests/lab-automation-review-regressions.test.ts`:
- Around line 414-415: Update the runBudgetRemaining call in the
rollBudgetWindow test to pass the captured now value as its third argument,
ensuring the assertion evaluates the same clock instant used to create rolled.

In `@tests/lab-automation.test.ts`:
- Around line 352-361: Add HTTP-level regression tests in
tests/lab-automation.test.ts alongside the existing management tests for the
missing lab automation routes: verify cancel returns 200 and cancelled:true for
a queued run, returns 404 with code not_found for an unknown id, and handles a
malformed encoded id without leaking URIError; verify run-list limits 0 and 101
return 400 with code invalid_limit and valid pagination returns nextCursor;
verify POST /api/lab/automation/run rejects unknown body keys with 400 and code
invalid_body.
- Around line 337-350: Wrap the “task background execution stays disabled by
default” test body with the existing withHome helper, ensuring OPENCODEX_HOME is
set to the temporary test directory before calling planLabAutomationRuns.
Preserve the current policy, routes, state, and empty-plan assertion.
- Around line 553-571: Update the test “management PUT cannot inject trusted
executor authority” to dispatch a lab automation request after the PUT, rather
than only checking isTrustedLabRouteExecutor on locally created objects. Assert
the dispatch result is route_ineligible with liveRequest: false, matching the
existing contract test, and ensure no trusted executor is installed before
dispatch.
- Around line 356-357: Update the test around handleManagementAPI to explicitly
guard the nullable response before accessing status, asserting that res is
non-null and then checking for status 200. Preserve the existing request and
configuration setup while ensuring unhandled routes produce a clear assertion
failure instead of a TypeError.
🪄 Autofix

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: ASSERTIVE

Plan: Pro Plus

Run ID: 7a94e073-aba7-44c4-b0c9-013ec6502b02

📥 Commits

Reviewing files that changed from the base of the PR and between da8ebd3 and ab892e9.

📒 Files selected for processing (27)
  • devlog/_plan/260807_compatibility_lab/008_cl08_automation.md
  • src/cli/lab.ts
  • src/lab/automation/budgets.ts
  • src/lab/automation/constants.ts
  • src/lab/automation/cooldown.ts
  • src/lab/automation/dispatch.ts
  • src/lab/automation/index.ts
  • src/lab/automation/orchestrator.ts
  • src/lab/automation/persistence.ts
  • src/lab/automation/planner.ts
  • src/lab/automation/policy.ts
  • src/lab/automation/queue.ts
  • src/lab/automation/recovery.ts
  • src/lab/automation/route-context.ts
  • src/lab/automation/run-key.ts
  • src/lab/automation/runs-query.ts
  • src/lab/automation/types.ts
  • src/lab/index.ts
  • src/lab/live/executor.ts
  • src/lab/paths.ts
  • src/lib/lab-live-route-production.ts
  • src/server/index.ts
  • src/server/lifecycle.ts
  • src/server/management-api.ts
  • src/server/management/lab-automation-routes.ts
  • tests/lab-automation-review-regressions.test.ts
  • tests/lab-automation.test.ts

Comment thread devlog/_plan/260807_compatibility_lab/008_cl08_automation.md Outdated
Comment thread devlog/_plan/260807_compatibility_lab/008_cl08_automation.md
Comment thread src/cli/lab.ts
Comment thread src/cli/lab.ts Outdated
Comment thread src/lab/automation/budgets.ts
Comment thread tests/lab-automation-review-regressions.test.ts Outdated
Comment thread tests/lab-automation.test.ts
Comment thread tests/lab-automation.test.ts
Comment thread tests/lab-automation.test.ts Outdated
Comment thread tests/lab-automation.test.ts
@Wibias
Wibias marked this pull request as ready for review August 11, 2026 05:45
@Wibias
Wibias marked this pull request as draft August 11, 2026 05:45

Wibias commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Wibias commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

♻️ Duplicate comments (1)
src/lab/automation/persistence.ts (1)

348-356: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Reject running records without startedAt.

Lines 351-353 allow a running record with no startedAt. isRollingBudgetEvidence then ignores the run, while queue selection still counts it as running. A corrupted state file can therefore reduce dispatch capacity until recovery runs.

Reject this state with invalid_state.

Proposed fix
-  if (state === "running" && (completedAt !== undefined || terminalCode !== undefined)) {
+  if (state === "running" && (startedAt === undefined || completedAt !== undefined || terminalCode !== undefined)) {
     throw new LabAutomationError(`running run has terminal fields at ${index}`, "invalid_state");
   }
🤖 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 `@src/lab/automation/persistence.ts` around lines 348 - 356, Update the
running-state validation near the existing lifecycle checks to throw
LabAutomationError with code "invalid_state" when state is "running" and
startedAt is undefined. Preserve the current validation for completedAt and
terminalCode, and keep valid running records with startedAt unchanged.
🤖 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 `@src/lab/automation/cooldown.ts`:
- Around line 17-32: Update the scheduled-run claiming flow and setCooldown so
cooldown capacity is reserved before dispatch: account for active cooldowns plus
scheduled runs already in progress, and refuse claims that would exceed
LAB_AUTOMATION_HARD_MAX.maxPersistedRuns. Release reservations only after
successful terminal results, ensuring multiple failed scheduled runs retain
their cooldowns instead of being silently dropped; add coverage for 511 active
cooldowns and multiple failed scheduled runs.

In `@src/lab/automation/orchestrator.ts`:
- Around line 50-53: Update dispatchDepsFor so it returns only the dependencies
from dispatchDepsByConfigDir.get(configKey(configDir)); remove the fallback
lookup for the default key while preserving the empty-object fallback when no
scoped entry exists.

In `@src/lab/automation/persistence.ts`:
- Around line 186-198: Update the state-lock acquisition flow around
reclaimDeadStateLock to write PID/token metadata to a private temporary file
first, then atomically publish it at stateLockPath using create-if-absent
semantics, so readers only observe complete metadata and existing ownership
checks remain intact. Handle publication races and cleanup of temporary files
without reverting to mtime-based reclamation, and add a crash-injection test
covering termination after acquisition but before publication.

In `@src/lab/automation/planner.ts`:
- Around line 101-126: Bound freshness-index construction in the planner’s
pagination loop around queryLabObservations, preferably by querying only the
exact suite/scenario identities required by the current plan using an indexed
query; otherwise enforce documented retention and scan limits without
sacrificing freshness correctness. Update the relevant freshness lookup flow and
add a regression test with many irrelevant observations that verifies query work
remains bounded.

---

Duplicate comments:
In `@src/lab/automation/persistence.ts`:
- Around line 348-356: Update the running-state validation near the existing
lifecycle checks to throw LabAutomationError with code "invalid_state" when
state is "running" and startedAt is undefined. Preserve the current validation
for completedAt and terminalCode, and keep valid running records with startedAt
unchanged.
🪄 Autofix

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: ASSERTIVE

Plan: Pro Plus

Run ID: d7520ea5-89fe-4796-a168-7f6620a84330

📥 Commits

Reviewing files that changed from the base of the PR and between ab892e9 and da635b3.

📒 Files selected for processing (17)
  • devlog/_plan/260807_compatibility_lab/008_cl08_automation.md
  • src/cli/lab.ts
  • src/lab/automation/constants.ts
  • src/lab/automation/cooldown.ts
  • src/lab/automation/orchestrator.ts
  • src/lab/automation/persistence.ts
  • src/lab/automation/planner.ts
  • src/lab/automation/policy.ts
  • src/lab/automation/queue.ts
  • src/lab/automation/recovery.ts
  • src/lab/automation/runs-query.ts
  • src/lib/lab-live-route-production.ts
  • src/server/management/lab-automation-routes.ts
  • tests/lab-automation-coderabbit-regressions.test.ts
  • tests/lab-automation-management-http.test.ts
  • tests/lab-automation-review-regressions.test.ts
  • tests/lab-automation.test.ts

Comment thread src/lab/automation/cooldown.ts
Comment thread src/lab/automation/orchestrator.ts Outdated
Comment thread src/lab/automation/persistence.ts
Comment thread src/lab/automation/planner.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/lab/automation/orchestrator.ts (1)

414-425: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Persist cancellation requests for in-flight runs owned by another process.

If a persisted run is already running but its controller belongs to another process, inFlightControllers.get(runId) returns no controller. The function then accepts only queued runs and returns false. The owning process receives no abort signal, so its provider dispatch continues.

Persist a cancellation request under the automation state lock. Make the owning dispatcher observe the request and finalize the run as cancelled. Scope the request by configDir and run ID. Add a regression with a persisted running run and an empty local controller map.

This finding is based on the PR objective for cross-process automation-state serialization.

🤖 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 `@src/lab/automation/orchestrator.ts` around lines 414 - 425, Update the
cancellation flow around the inFlightControllers lookup and
mutateLabAutomationState so running runs without a local controller persist a
cancellation request keyed by configDir and runId under the state lock. Make the
owning dispatcher observe that request and finalize the run as cancelled while
preserving existing local-controller and queued-run behavior, and add a
regression covering a persisted running run with an empty controller map.
🤖 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 `@src/lab/automation/orchestrator.ts`:
- Around line 233-239: Make cancellation backoff durable when the cooldown store
reaches capacity: update setCooldown and the queued and in-flight cancellation
paths around cancellationCooldownUntil so run.runKey is always persisted or
represented by a planner-honored cancellation tombstone until expiry. Add
regression coverage that fills the cooldown map to
LAB_AUTOMATION_HARD_MAX.maxPersistedRuns and verifies both cancellation paths
prevent the run from being re-admitted.

In `@tests/lab-automation-coderabbit-regressions.test.ts`:
- Around line 184-198: Extend the cancellation regression coverage beside the
existing queued test with a focused in-flight scenario that reaches finalizeRun
through an aborted controller. Wait for finalization after invoking
cancelLabAutomationRun, then assert the run is cancelled and
cooldownUntilByKey["key-1"] is at least before +
LAB_AUTOMATION_HARD_MAX.schedulerTickMs.

---

Outside diff comments:
In `@src/lab/automation/orchestrator.ts`:
- Around line 414-425: Update the cancellation flow around the
inFlightControllers lookup and mutateLabAutomationState so running runs without
a local controller persist a cancellation request keyed by configDir and runId
under the state lock. Make the owning dispatcher observe that request and
finalize the run as cancelled while preserving existing local-controller and
queued-run behavior, and add a regression covering a persisted running run with
an empty controller map.
🪄 Autofix

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: ASSERTIVE

Plan: Pro Plus

Run ID: c9c21a13-dc3e-4b36-804d-431b3b9ec450

📥 Commits

Reviewing files that changed from the base of the PR and between da635b3 and 9ed9129.

📒 Files selected for processing (2)
  • src/lab/automation/orchestrator.ts
  • tests/lab-automation-coderabbit-regressions.test.ts

Comment thread src/lab/automation/orchestrator.ts
Comment thread tests/lab-automation-coderabbit-regressions.test.ts
@Wibias
Wibias marked this pull request as ready for review August 11, 2026 10:22

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/lab/automation/queue.ts (1)

29-66: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Keep persisted state within maxPersistedRuns.

isCancellationBackoffEvidence protects every cancelled scheduled run during backoff. If protected records exceed LAB_AUTOMATION_HARD_MAX.maxPersistedRuns, evictOldestTerminal returns false and Lines 203-205 return an oversized state.

Distinct run keys can accumulate during route or manifest changes. State reads and writes then grow beyond the configured hard limit.

Store bounded cancellation suppression separately from terminal history, with expiry and a hard key limit. If that capacity is full, fail scheduled planning closed. Do not return a state that exceeds maxPersistedRuns.

Also applies to: 203-205

🤖 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 `@src/lab/automation/queue.ts` around lines 29 - 66, Bound cancellation
suppression independently from persisted terminal history so protected cancelled
scheduled runs cannot exceed maxPersistedRuns. Update
isCancellationBackoffEvidence and the queue state read/write flow around
evictOldestTerminal to use an expiring, hard-limited suppression store; when
that capacity is exhausted, fail scheduled planning closed and ensure no
returned or persisted state exceeds LAB_AUTOMATION_HARD_MAX.maxPersistedRuns.
🤖 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.

Outside diff comments:
In `@src/lab/automation/queue.ts`:
- Around line 29-66: Bound cancellation suppression independently from persisted
terminal history so protected cancelled scheduled runs cannot exceed
maxPersistedRuns. Update isCancellationBackoffEvidence and the queue state
read/write flow around evictOldestTerminal to use an expiring, hard-limited
suppression store; when that capacity is exhausted, fail scheduled planning
closed and ensure no returned or persisted state exceeds
LAB_AUTOMATION_HARD_MAX.maxPersistedRuns.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 44df6558-5dab-4d1a-b8e4-8eecab4eb089

📥 Commits

Reviewing files that changed from the base of the PR and between 3be4c8e and f9096b7.

📒 Files selected for processing (6)
  • src/lab/automation/planner.ts
  • src/lab/automation/queue.ts
  • src/lab/projection/schema.ts
  • src/lab/query/latest-observation.ts
  • tests/lab-automation-coderabbit-regressions.test.ts
  • tests/lab-automation-final-coderabbit-regressions.test.ts

@flyingsquirrel0419 flyingsquirrel0419 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.

I am pausing maintainer approval at exact head 2583e1e72b8e20f023e8c7d48b688f46efbc7c76.

Current dev@849ab5e35fdacddb4d47416c00a315111c78f97e is not an ancestor of this head; the merge base is still da8ebd3135553c1d4dd85c1f258e998a5de14f28. For a 39-file / +6,091-line change that can acquire OAuth credentials and dispatch real provider traffic, I need the trust-boundary review and required CI to be against the current integration base, not the pre-rebase snapshot.

Please rebase onto current dev, keep the resulting diff focused, and let exact-head CI complete. git diff --check origin/dev...2583e1e72 also currently reports four trailing-whitespace lines at the top of devlog/_plan/260807_compatibility_lab/008_cl08_automation.md; please clean those during the rebase. I will resume the credential-destination, authority, persistence-lock, cancellation, and shutdown review on the rebased head. This is not a rejection of the CL-08 direction.

@Ingwannu Ingwannu left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

I am pausing maintainer approval at exact head 2583e1e72b8e20f023e8c7d48b688f46efbc7c76.

Current dev@849ab5e35fdacddb4d47416c00a315111c78f97e is not an ancestor of this head; the merge base is still da8ebd3135553c1d4dd85c1f258e998a5de14f28. For a 39-file / +6,091-line change that can acquire OAuth credentials and dispatch real provider traffic, I need the trust-boundary review and required CI to be against the current integration base, not the pre-rebase snapshot.

Please rebase onto current dev, keep the resulting diff focused, and let exact-head CI complete. git diff --check origin/dev...2583e1e72 also currently reports four trailing-whitespace lines at the top of devlog/_plan/260807_compatibility_lab/008_cl08_automation.md; please clean those during the rebase. I will resume the credential-destination, authority, persistence-lock, cancellation, and shutdown review on the rebased head. This is not a rejection of the CL-08 direction.

Wibias added 12 commits August 11, 2026 19:20
Record bounded orchestration scope, default-off policy, and implementation traceability for feat/cl-08-lab-automation.
Add default-off CL-08 policy, planner, queue, budgets, dispatch seam, scheduler lifecycle, and persistence without provider authority.
Issue production CL-03 executor at server startup, expose management API and CLI, and gate live evidence persistence on trusted_route authority.
Cover trusted dispatch, missing executor fail-closed, budget and shutdown behavior, and management API authority boundaries.
@Wibias
Wibias force-pushed the feat/cl-08-lab-automation branch from 2583e1e to e533234 Compare August 11, 2026 17:20

@Ingwannu Ingwannu left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Requesting changes on exact head e5332346943526b7b833e3fb202aecd2af11425e. The rebase is correct and current dev@849ab5e35 is now an ancestor, but two lifecycle/commit blockers remain for credentialed background execution.

  1. src/server/index.ts:1603-1625,1669-1681 — the server stop lifecycle does not release the Lab scheduler or its credential executor. startServer registers setLabAutomationDispatchDeps and may start a timer for this config root, but the server.stop override releases only the listener, overlay reconciler, background lifecycle, and native-main lifecycle. The keyed map prevents cross-root collisions, but it does not solve ownership for a stopped/restarted server using the same root: the old timer survives, the next startServer overwrites that key with a new loadConfig/routeExecutor, and automation can keep issuing provider requests after the original listener has stopped. Add an owner-scoped registration receipt/release operation, invoke it from the stop override and failed-start path, and stop the keyed timer only when the stopping server still owns it. Add a same-root start/stop/restart regression proving that the stopped owner cannot dispatch or clear a successor.

  2. src/server/management/lab-automation-routes.ts:189-193 — policy and route updates are not one commit. Validation happens before I/O, but saveLabAutomationRoutes commits one file before saveLabAutomationPolicy commits the other. If the second write fails, the endpoint returns 500 while the new route set is already durable. With an existing enabled live policy, the still-running scheduler can then send credentialed probes to routes from an update the operator was told had failed. Persist one versioned combined configuration, or implement an ownership-safe transaction/rollback that leaves the scheduler fail-closed until both files share one committed generation. Add a fault-injection test that fails the second write and proves neither effective policy nor effective routes change and no dispatch occurs.

git diff --check origin/dev...e53323469 also still reports the four trailing-whitespace lines at devlog/_plan/260807_compatibility_lab/008_cl08_automation.md:5-8; please clean them. Exact-head Linux shards and gates are green, while macOS is still running. I am not treating CI as a substitute for these lifecycle guarantees.

@Wibias
Wibias merged commit 3b8f948 into dev Aug 11, 2026
24 checks passed
@Wibias
Wibias deleted the feat/cl-08-lab-automation branch August 11, 2026 18:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants