Skip to content

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

Open
Wibias wants to merge 56 commits into
devfrom
feat/cl-08-lab-automation
Open

feat(lab): CL-08 bounded automation with trusted live-route dispatch#1447
Wibias wants to merge 56 commits into
devfrom
feat/cl-08-lab-automation

Conversation

@Wibias

@Wibias Wibias commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Starting SHA: da8ebd3135553c1d4dd85c1f258e998a5de14f28
  • Head SHA: ab892e9ac07858b72e019296e83a7573d1e9a292
  • 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 corrected head ab892e9ac07858b72e019296e83a7573d1e9a292:

  • React Doctor: success
  • Cross-platform gates: success (tsc, GUI tests, privacy scan, CLI smoke)
  • Linux test shards 1/4, 2/4, 3/4, 4/4: success
  • API usage / storage / keyring / npm-global lanes completed so far: success
  • macOS full test lane: running at the time of this description update

Focused CL-08 coverage now includes tests/lab-automation.test.ts plus tests/lab-automation-review-regressions.test.ts.

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.
  • CodeRabbit / maintainer review complete.

Draft PR — 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.

Wibias added 4 commits August 11, 2026 05:42
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.
@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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely identifies the CL-08 bounded automation feature and its trusted live-route dispatch, which are the main changes.
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 💡 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
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 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

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.

1 participant