Skip to content

refactor(agent): establish runtime ownership boundaries - #2023

Merged
yyhhyyyyyy merged 16 commits into
devfrom
refactor/agent-runtime-ownership
Jul 25, 2026
Merged

refactor(agent): establish runtime ownership boundaries#2023
yyhhyyyyyy merged 16 commits into
devfrom
refactor/agent-runtime-ownership

Conversation

@yyhhyyyyyy

@yyhhyyyyyy yyhhyyyyyy commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR decomposes DeepChatRuntimeCoordinator into explicit runtime owners while preserving existing public APIs, durable queue semantics, Tape/Memory behavior, provider behavior, and visible-turn semantics except for the documented corrections below.

  • Introduce stable per-instance SessionRuntimeScope identity and stale-instance fencing.
  • Move run lifecycle, cancellation, settlement, and queue wakeup into RunLifecycleCoordinator.
  • Move input admission into PendingInputAdmissionCoordinator.
  • Centralize queue selection, claim ownership, recovery, and single-flight draining in PendingInputPump.
  • Extend dedicated owners for compaction, status publication, context budgeting, pre-stream watchdogs, permissions, and runtime hooks.
  • Reduce the root coordinator to composition and compatibility responsibilities.
  • Replace regex architecture checks with fail-closed TypeScript AST ownership guards.
  • Refresh the layered runtime architecture baseline.

The design and implementation stages are documented in:

  • docs/architecture/deepchat-agent-harness-boundaries/spec.md
  • docs/architecture/deepchat-agent-harness-boundaries/plan.md
  • docs/architecture/deepchat-agent-harness-boundaries/tasks.md

Included Runtime Corrections

This refactor intentionally retains three explicit fixes and five ownership corrections because subsequent extraction rewrote or relocated the same control flow:

  1. Preserve deferred tool progress by re-reading the assistant message after execution.
  2. Serialize concurrent tool-question follow-up admission.
  3. Roll back transcript, compaction, and Memory facts when a claimed live send is rejected.
  4. Clear active steer markers only when the claimed input ID still matches.
  5. Preserve direct steer inputs already accepted by another durable or runtime owner.
  6. Avoid restoring promoted steer inputs already claimed or consumed elsewhere.
  7. Clear operation controllers only when the exact controller is still owned.
  8. Keep question follow-ups pending during an overlapping queue drain, then admit them after the active transition settles.

Each correction is pinned by focused regression coverage, including an explicit draining + question-follow-up overlap test.

Reliability And Diagnostics

  • Treat concurrent terminal interaction settlement as clean ownership loss.
  • Preserve primary admission and settlement errors when compensation or verification also fails.
  • Fence claims conservatively when durable settlement cannot be verified.
  • Distinguish claim-consistency failures from message-processing failures.
  • Classify missing hydrated steer owners as stale runtime instances.
  • Share one parsed transcript snapshot across pending-input admission gates.
  • Cache runtime scopes per instance without introducing another source of truth.

Summary by CodeRabbit

  • New Features
    • Improved DeepChat runtime lifecycle coordination (status, cancellation, and terminal reporting), including stale-instance detection.
    • Added stronger pending-input queuing/steering with serialized admission and durable settlement handling.
    • Introduced context-budget policies and provider input capability detection (vision/audio).
    • Added pre-stream watchdog logging and refreshed agent registry distribution metadata.
  • Bug Fixes
    • Hardened compaction, permission resolution, abort handling, and queue-drain ownership to prevent conflicting runtime actions.
  • Documentation
    • Expanded DeepChat harness boundary plans, specs, and tasks.
  • Tests
    • Added/updated comprehensive coverage for lifecycle, pending inputs, compaction, watchdogs, capabilities, and runtime hooks.

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

DeepChat runtime ownership is reorganized around lifecycle scopes, centralized status and hook handling, durable pending-input claims, and coordinator-specific responsibilities. Runtime policies, tests, architecture documentation, enforcement scripts, baseline inventories, and ACP registry metadata are also updated.

Changes

DeepChat runtime ownership

Layer / File(s) Summary
Scoped lifecycle and runtime services
src/main/agent/deepchat/instance/*, src/main/agent/deepchat/runtime/runLifecycleCoordinator.ts, src/main/agent/deepchat/runtime/sessionStatusPublisher.ts, src/main/agent/deepchat/runtime/runtimeHookSink.ts
Adds session runtime scopes, stale-instance fencing, lifecycle coordination, status projection, terminal observation, hook dispatch, cancellation, and permission lifecycle handling.
Pending-input execution
src/main/agent/deepchat/runtime/pendingInput*, src/main/session/data/pendingInputs.ts
Adds typed claim/settlement contracts, admission coordination, durable queue pumping, attachment lanes, steering, blocking, rollback, and queue management.
Turn and coordinator integration
src/main/agent/deepchat/runtime/{deepChatRuntimeCoordinator,deepChatLoopRunner,interactionCoordinator,turnCoordinator}.ts, src/main/agent/manager/deepChatAgentBackend.ts
Routes turn execution, hooks, status changes, abort handling, stream cleanup, project settings, and pending-input completion through lifecycle collaborators.
Runtime policies and compaction
src/main/agent/deepchat/runtime/{abortErrors,compactionRuntimeCoordinator,contextBudgetPolicy,preStreamWatchdog,providerInputCapabilities,providerPermissionResolution,sessionSettingsCoordinator}.ts
Adds shared abort helpers, context-budget and provider-capability policies, pre-stream watchdogs, provider permission resolution, and manual compaction lifecycle handling.
Validation and architecture enforcement
test/main/agent/deepchat/runtime/*, test/main/agent/deepchat/instance/*, scripts/agent-cleanup-guard.mjs, scripts/generate-architecture-baseline.mjs, docs/architecture/deepchat-agent-harness-boundaries/*
Adds runtime, lifecycle, pending-input, watchdog, compaction, hook, status, and ownership-guard tests; updates ownership documentation and baseline generation.
Registry metadata
resources/acp-registry/registry.json
Updates existing agent versions, package references, binary archives, commands, and platform checksums.

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

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant DeepChatRuntimeCoordinator
  participant PendingInputAdmissionCoordinator
  participant PendingInputPump
  participant TurnCoordinator
  participant RunLifecycleCoordinator
  Client->>DeepChatRuntimeCoordinator: queue or steer input
  DeepChatRuntimeCoordinator->>PendingInputAdmissionCoordinator: admit pending input
  PendingInputAdmissionCoordinator->>PendingInputPump: schedule or start drain
  PendingInputPump->>TurnCoordinator: start claimed turn
  TurnCoordinator->>RunLifecycleCoordinator: settle run and status
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 matches the PR’s main refactor: establishing explicit runtime ownership boundaries in the DeepChat agent.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/agent-runtime-ownership

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

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
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

🧹 Nitpick comments (9)
src/main/agent/deepchat/runtime/pendingInputAdmissionCoordinator.ts (2)

383-387: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Recovery paths bypass the redacting logger. Both modules standardize on logger + redactRuntimeErrorForLog, but the claim release/restore failure paths fall back to raw console.* with the unredacted error object, which loses structured logging and can leak error details.

  • src/main/agent/deepchat/runtime/pendingInputAdmissionCoordinator.ts#L383-L387: replace console.error with logger.error(..., redactRuntimeErrorForLog(restoreError)); apply the same change to the console.error at Line 278.
  • src/main/agent/deepchat/runtime/pendingInputPump.ts#L456-L467: replace console.warn with logger.warn(..., redactRuntimeErrorForLog(error)).
🤖 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/main/agent/deepchat/runtime/pendingInputAdmissionCoordinator.ts` around
lines 383 - 387, Replace raw recovery-path console logging with the established
structured redacting logger: in pendingInputAdmissionCoordinator.ts at lines
383-387 and 278, use logger.error with redactRuntimeErrorForLog for the restore
and claim-release errors; in pendingInputPump.ts at lines 456-467, use
logger.warn with redactRuntimeErrorForLog(error) instead of console.warn.

197-201: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Duplicated lane-key format. Line 197 hardcodes steer:${sessionId} while acquireAttachmentAcceptanceLane builds ${lane}:${sessionId} at Line 428. Extract a private laneKey(lane, sessionId) helper so the fast-path check can't silently diverge.

🤖 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/main/agent/deepchat/runtime/pendingInputAdmissionCoordinator.ts` around
lines 197 - 201, Extract a private laneKey helper that builds the shared
lane/session key format, and use it in both the hasPriorSteerAcceptance check
and acquireAttachmentAcceptanceLane. Remove the duplicated template literal
while preserving the existing fast-path behavior.
test/main/agent/deepchat/runtime/pendingInputAdmissionCoordinator.test.ts (1)

202-326: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider covering sendQueuedMessage and lane serialization. The suite exercises the steer paths well, but the send-admission path (capacity rejection, needs_user_action short-circuit before queueing, abort after preparation) and the attachmentAcceptanceTails serialization guarantee are untested — both are newly introduced behavior in this coordinator.

🤖 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 `@test/main/agent/deepchat/runtime/pendingInputAdmissionCoordinator.test.ts`
around lines 202 - 326, Extend the PendingInputAdmissionCoordinator suite to
cover sendQueuedMessage admission behavior: capacity rejection, returning the
durable needs_user_action result without queueing, and aborting after attachment
preparation. Add a lane-serialization test that exercises
attachmentAcceptanceTails and verifies overlapping admissions are serialized in
order. Reuse the existing createHarness and mocks, and assert the relevant
queueing, settlement, and result behavior.
test/main/agent/deepchat/runtime/pendingInputPump.test.ts (1)

260-264: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Fixed microtask hops make settlement assertions flaky. launch chains .then.finally(async …).catch, plus awaits inside scheduleNextIfReady, so three hops isn't a guaranteed quiescence point. Prefer vi.waitFor(...) on the actual expectation (as done at Line 548) instead of flushPromises().

🤖 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 `@test/main/agent/deepchat/runtime/pendingInputPump.test.ts` around lines 260 -
264, Replace the fixed three-hop flushPromises() usage in the pending-input
settlement assertions with vi.waitFor(...) around the actual expectation,
matching the established pattern near line 548. Ensure the wait observes the
intended settled state rather than relying on a fixed number of microtask turns,
and remove flushPromises if no longer used.
test/main/agent/deepchat/runtime/deepChatRuntimeCoordinator.test.ts (2)

756-768: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider proxy-based delegation to avoid drift with MemoryRuntimePort.

The hand-written member list must be updated whenever the port gains a method; otherwise the new member silently bypasses the installed override in tests.

♻️ Optional simplification
-function createDelegatingMemoryRuntimePort(getTarget: () => MemoryRuntimePort): MemoryRuntimePort {
-  return {
-    isEnabled: (...args) => getTarget().isEnabled(...args),
-    captureExecutionToken: (...args) => getTarget().captureExecutionToken(...args),
-    canContinueExecution: (...args) => getTarget().canContinueExecution(...args),
-    buildInjection: (...args) => getTarget().buildInjection(...args),
-    recordInjectionAccess: (...args) => getTarget().recordInjectionAccess(...args),
-    extractAndStore: (...args) => getTarget().extractAndStore(...args),
-    maybeReflect: (...args) => getTarget().maybeReflect(...args),
-    maybeEvolvePersona: (...args) => getTarget().maybeEvolvePersona(...args),
-    observeExtractionQueue: (...args) => getTarget().observeExtractionQueue?.(...args)
-  }
-}
+function createDelegatingMemoryRuntimePort(getTarget: () => MemoryRuntimePort): MemoryRuntimePort {
+  return new Proxy({} as MemoryRuntimePort, {
+    get: (_target, key: keyof MemoryRuntimePort) => {
+      const value = getTarget()[key]
+      return typeof value === 'function'
+        ? (...args: unknown[]) => (value as (...a: unknown[]) => unknown).apply(getTarget(), args)
+        : value
+    }
+  })
+}
🤖 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 `@test/main/agent/deepchat/runtime/deepChatRuntimeCoordinator.test.ts` around
lines 756 - 768, Replace the hand-written delegation object in
createDelegatingMemoryRuntimePort with proxy-based delegation that forwards any
MemoryRuntimePort member to getTarget(), while preserving the installed override
behavior and optional-member handling.

7240-7247: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Single microtask flush makes this assertion timing-sensitive.

await Promise.resolve() only drains one microtask; if the fenced-claim admission path awaits more than once, the "still pending / not re-driven" assertions could pass or fail depending on internal await depth. Prefer a bounded settle (e.g. await vi.waitFor(...) on the pending list plus the processStream count) so intent is asserted rather than tick timing.

🤖 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 `@test/main/agent/deepchat/runtime/deepChatRuntimeCoordinator.test.ts` around
lines 7240 - 7247, Update the test around queuePendingInput and the
processStream assertion to use a bounded settle mechanism such as vi.waitFor,
waiting until the pending input remains present and processStream has the
expected invocation count. Remove the single await Promise.resolve() timing
dependency while preserving the assertions that the fenced claim is not
re-driven and the input remains pending.
test/main/agent/deepchat/runtime/runLifecycleCoordinator.test.ts (1)

289-289: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Line exceeds the 100-column limit.

♻️ Proposed change
-    const message = createMessage('message-paused', [createPendingAction('tool-1')], 1, '{"runId":"run-1"}')
+    const message = createMessage(
+      'message-paused',
+      [createPendingAction('tool-1')],
+      1,
+      '{"runId":"run-1"}'
+    )

As per coding guidelines, "Use Oxfmt formatting: single quotes, no semicolons, and a line width of 100."

🤖 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 `@test/main/agent/deepchat/runtime/runLifecycleCoordinator.test.ts` at line
289, Reformat the createMessage call in the affected lifecycle coordinator test
so it stays within the 100-column Oxfmt line width, preserving its single-quote
style and behavior.

Source: Coding guidelines

src/main/agent/deepchat/runtime/runLifecycleCoordinator.ts (1)

426-435: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the shared logger instead of console.warn.

This file already imports logger (Line 1) and uses it at Line 381; console.warn here bypasses the shared logging transport/redaction. Note the assertions in test/main/agent/deepchat/runtime/runLifecycleCoordinator.test.ts (Lines 329, 353, 366-369) would need updating.

♻️ Proposed change
-        console.warn(
+        logger.warn(
           `[DeepChatAgent] Failed to cancel ACP permission request ${permission.requestId}:`,
-          error
+          redactRuntimeErrorForLog(error)
         )
🤖 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/main/agent/deepchat/runtime/runLifecycleCoordinator.ts` around lines 426
- 435, Update cancelProviderPermissions to replace console.warn with the file’s
existing shared logger, preserving the current message and error details; update
the affected runLifecycleCoordinator tests to assert against the shared logger
instead of console.warn.
src/main/agent/deepchat/runtime/sessionSettingsCoordinator.ts (1)

164-206: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate project-dir mutation logic between setProjectDir and resolveProjectDir.

Both methods repeat the same normalize → compute-previous → set → conditionally-invalidate sequence, and use two different invalidation paths (invalidateToolProfile(sessionId) helper vs. direct expectedInstance.invalidateToolProfileCache()). Consider extracting a shared private helper that both call, keeping a single invalidation path.

♻️ Proposed refactor
+  private applyProjectDir(
+    sessionId: string,
+    instance: DeepChatAgentInstance,
+    projectDir?: string | null
+  ): string | null {
+    const normalized = this.normalizeProjectDir(projectDir)
+    const previous = instance.hasProjectDir()
+      ? instance.getProjectDir()
+      : this.resolvePersistedProjectDir(sessionId)
+    instance.setProjectDir(normalized)
+    if (previous !== normalized) {
+      instance.invalidateToolProfileCache()
+    }
+    return normalized
+  }
+
   setProjectDir(sessionId: string, projectDir: string | null): void {
-    const normalized = this.normalizeProjectDir(projectDir)
-    const instance = this.deps.getInstance(sessionId)
-    const previous = instance.hasProjectDir()
-      ? instance.getProjectDir()
-      : this.resolvePersistedProjectDir(sessionId)
-    instance.setProjectDir(normalized)
-    if (previous !== normalized) {
-      this.invalidateToolProfile(sessionId)
-    }
+    this.applyProjectDir(sessionId, this.deps.getInstance(sessionId), projectDir)
   }

   resolveProjectDir(
     sessionId: string,
     incoming?: string | null,
     expectedInstance = this.deps.getInstance(sessionId)
   ): string | null {
     this.deps.assertCurrent(sessionId, expectedInstance)
     if (incoming !== undefined) {
-      const normalized = this.normalizeProjectDir(incoming)
-      const previous = expectedInstance.hasProjectDir()
-        ? expectedInstance.getProjectDir()
-        : this.resolvePersistedProjectDir(sessionId)
-      expectedInstance.setProjectDir(normalized)
-      if (previous !== normalized) {
-        expectedInstance.invalidateToolProfileCache()
-      }
-      return normalized
+      return this.applyProjectDir(sessionId, expectedInstance, incoming)
     }
🤖 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/main/agent/deepchat/runtime/sessionSettingsCoordinator.ts` around lines
164 - 206, Extract the duplicated normalize, previous-value comparison,
project-directory assignment, and conditional invalidation logic from
setProjectDir and resolveProjectDir into one shared private helper. Have both
methods call that helper and use a single invalidation path, preserving the
existing behavior for the session instance and expectedInstance.
🤖 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/main/agent/deepchat/runtime/pendingInputPump.ts`:
- Around line 305-354: The drain guard in drain must use an atomic
check-and-start operation before the first await, replacing the separate
canDrain check and later markPendingQueueDrainStarted call. Update the relevant
runLifecycle/scope instance API so concurrent drains for the same session cannot
both proceed, and ensure every failure or completion path releases the gate only
after its own drain finishes.

In `@src/main/agent/deepchat/runtime/runLifecycleCoordinator.ts`:
- Around line 177-179: Update clearFirstTurnReady in the lifecycle coordinator
to use getHydratedScope instead of getOrCreateScope, matching
clearDeferredToolController and ensuring clearing the flag does not recreate an
evicted or disposed session.
- Around line 280-297: The cancellation path in refreshPendingInteractions
currently terminalizes only pendingInteractions[0].messageId. Before
replacePendingInteractions([]), iterate over each distinct pending interaction
messageId, parse and stamp its metadata as aborted with user_stop, and call
settleAbortedTurn for each message while preserving the existing pending-input
drain behavior.

In `@src/main/agent/deepchat/runtime/turnCoordinator.ts`:
- Around line 748-765: Track whether the pending input turn was rolled back in
the failure path around claimedInput.settle, and after rollback clear both the
user and assistant message identifiers (or otherwise mark the turn as rolled
back). Gate the subsequent terminal-persistence block—setMessageError,
emitMessageRefresh, and chat.stream.failed—so it is skipped for rolled-back
turns while preserving existing behavior for abort, stale-instance, and other
failures.

---

Nitpick comments:
In `@src/main/agent/deepchat/runtime/pendingInputAdmissionCoordinator.ts`:
- Around line 383-387: Replace raw recovery-path console logging with the
established structured redacting logger: in pendingInputAdmissionCoordinator.ts
at lines 383-387 and 278, use logger.error with redactRuntimeErrorForLog for the
restore and claim-release errors; in pendingInputPump.ts at lines 456-467, use
logger.warn with redactRuntimeErrorForLog(error) instead of console.warn.
- Around line 197-201: Extract a private laneKey helper that builds the shared
lane/session key format, and use it in both the hasPriorSteerAcceptance check
and acquireAttachmentAcceptanceLane. Remove the duplicated template literal
while preserving the existing fast-path behavior.

In `@src/main/agent/deepchat/runtime/runLifecycleCoordinator.ts`:
- Around line 426-435: Update cancelProviderPermissions to replace console.warn
with the file’s existing shared logger, preserving the current message and error
details; update the affected runLifecycleCoordinator tests to assert against the
shared logger instead of console.warn.

In `@src/main/agent/deepchat/runtime/sessionSettingsCoordinator.ts`:
- Around line 164-206: Extract the duplicated normalize, previous-value
comparison, project-directory assignment, and conditional invalidation logic
from setProjectDir and resolveProjectDir into one shared private helper. Have
both methods call that helper and use a single invalidation path, preserving the
existing behavior for the session instance and expectedInstance.

In `@test/main/agent/deepchat/runtime/deepChatRuntimeCoordinator.test.ts`:
- Around line 756-768: Replace the hand-written delegation object in
createDelegatingMemoryRuntimePort with proxy-based delegation that forwards any
MemoryRuntimePort member to getTarget(), while preserving the installed override
behavior and optional-member handling.
- Around line 7240-7247: Update the test around queuePendingInput and the
processStream assertion to use a bounded settle mechanism such as vi.waitFor,
waiting until the pending input remains present and processStream has the
expected invocation count. Remove the single await Promise.resolve() timing
dependency while preserving the assertions that the fenced claim is not
re-driven and the input remains pending.

In `@test/main/agent/deepchat/runtime/pendingInputAdmissionCoordinator.test.ts`:
- Around line 202-326: Extend the PendingInputAdmissionCoordinator suite to
cover sendQueuedMessage admission behavior: capacity rejection, returning the
durable needs_user_action result without queueing, and aborting after attachment
preparation. Add a lane-serialization test that exercises
attachmentAcceptanceTails and verifies overlapping admissions are serialized in
order. Reuse the existing createHarness and mocks, and assert the relevant
queueing, settlement, and result behavior.

In `@test/main/agent/deepchat/runtime/pendingInputPump.test.ts`:
- Around line 260-264: Replace the fixed three-hop flushPromises() usage in the
pending-input settlement assertions with vi.waitFor(...) around the actual
expectation, matching the established pattern near line 548. Ensure the wait
observes the intended settled state rather than relying on a fixed number of
microtask turns, and remove flushPromises if no longer used.

In `@test/main/agent/deepchat/runtime/runLifecycleCoordinator.test.ts`:
- Line 289: Reformat the createMessage call in the affected lifecycle
coordinator test so it stays within the 100-column Oxfmt line width, preserving
its single-quote style and behavior.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f58e4f57-e0db-40cf-b037-f711823daa73

📥 Commits

Reviewing files that changed from the base of the PR and between a7565c9 and 162c409.

📒 Files selected for processing (41)
  • docs/architecture/baselines/agent-system-layered-runtime-baseline.json
  • docs/architecture/deepchat-agent-harness-boundaries/plan.md
  • docs/architecture/deepchat-agent-harness-boundaries/spec.md
  • docs/architecture/deepchat-agent-harness-boundaries/tasks.md
  • resources/acp-registry/registry.json
  • scripts/agent-cleanup-guard.mjs
  • scripts/generate-architecture-baseline.mjs
  • src/main/agent/deepchat/instance/deepChatAgentRuntime.ts
  • src/main/agent/deepchat/runtime/abortErrors.ts
  • src/main/agent/deepchat/runtime/compactionRuntimeCoordinator.ts
  • src/main/agent/deepchat/runtime/contextBudgetPolicy.ts
  • src/main/agent/deepchat/runtime/deepChatLoopRunner.ts
  • src/main/agent/deepchat/runtime/deepChatRuntimeCoordinator.ts
  • src/main/agent/deepchat/runtime/interactionCoordinator.ts
  • src/main/agent/deepchat/runtime/pendingInputAdmissionCoordinator.ts
  • src/main/agent/deepchat/runtime/pendingInputContracts.ts
  • src/main/agent/deepchat/runtime/pendingInputPump.ts
  • src/main/agent/deepchat/runtime/preStreamWatchdog.ts
  • src/main/agent/deepchat/runtime/providerInputCapabilities.ts
  • src/main/agent/deepchat/runtime/providerPermissionCoordinator.ts
  • src/main/agent/deepchat/runtime/providerPermissionResolution.ts
  • src/main/agent/deepchat/runtime/runLifecycleCoordinator.ts
  • src/main/agent/deepchat/runtime/runtimeHookSink.ts
  • src/main/agent/deepchat/runtime/sessionSettingsCoordinator.ts
  • src/main/agent/deepchat/runtime/sessionStatusPublisher.ts
  • src/main/agent/deepchat/runtime/turnCoordinator.ts
  • src/main/agent/manager/deepChatAgentBackend.ts
  • src/main/session/data/pendingInputs.ts
  • test/main/agent/deepchat/instance/deepChatAgentRuntime.test.ts
  • test/main/agent/deepchat/runtime/abortErrors.test.ts
  • test/main/agent/deepchat/runtime/compactionRuntimeCoordinator.test.ts
  • test/main/agent/deepchat/runtime/contextBudgetPolicy.test.ts
  • test/main/agent/deepchat/runtime/deepChatRuntimeCoordinator.test.ts
  • test/main/agent/deepchat/runtime/pendingInputAdmissionCoordinator.test.ts
  • test/main/agent/deepchat/runtime/pendingInputPump.test.ts
  • test/main/agent/deepchat/runtime/preStreamWatchdog.test.ts
  • test/main/agent/deepchat/runtime/providerInputCapabilities.test.ts
  • test/main/agent/deepchat/runtime/runLifecycleCoordinator.test.ts
  • test/main/agent/deepchat/runtime/runtimeHookSink.test.ts
  • test/main/agent/deepchat/runtime/sessionStatusPublisher.test.ts
  • test/main/scripts/agentCleanupGuard.test.ts
💤 Files with no reviewable changes (1)
  • src/main/agent/manager/deepChatAgentBackend.ts

Comment thread src/main/agent/deepchat/runtime/pendingInputPump.ts Outdated
Comment thread src/main/agent/deepchat/runtime/runLifecycleCoordinator.ts
Comment thread src/main/agent/deepchat/runtime/runLifecycleCoordinator.ts
Comment thread src/main/agent/deepchat/runtime/turnCoordinator.ts

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

🧹 Nitpick comments (2)
test/main/agent/deepchat/runtime/pendingInputAdmissionCoordinator.test.ts (1)

202-208: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

createDeferred is duplicated verbatim in test/main/agent/deepchat/runtime/pendingInputPump.test.ts.

Consider moving it to a shared test utility so both runtime pump/admission suites use one implementation.

🤖 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 `@test/main/agent/deepchat/runtime/pendingInputAdmissionCoordinator.test.ts`
around lines 202 - 208, Move the duplicated createDeferred helper into a shared
test utility, then update pendingInputAdmissionCoordinator.test.ts and
pendingInputPump.test.ts to import and reuse that single implementation,
removing their local definitions.
src/main/agent/deepchat/runtime/pendingInputPump.ts (1)

278-290: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Distinguish lease contention from claim-adoption failure in the log.

Losing the drain lease race is an expected, recoverable outcome (the claim is released and a drain is rescheduled), yet it is logged at error with stage=adopt-claim, the same signature used for genuine stale-instance failures in drain. This makes the log misleading and noisy under normal concurrency.

♻️ Suggested logging split
     const drainLease = scope?.instance.tryAcquirePendingQueueDrain() ?? null
     if (!scope || !drainLease) {
       const released = this.tryRelease(claim)
-      logger.error(
-        `[DeepChatAgent] pending input start rejected session=${record.sessionId} stage=adopt-claim`
-      )
+      const stage = scope ? 'drain-lease-unavailable' : 'adopt-claim'
+      const log = scope ? logger.info : logger.error
+      log(
+        `[DeepChatAgent] pending input start rejected session=${record.sessionId} stage=${stage} released=${released}`
+      )
       if (released) {
         this.schedule(record.sessionId, 'enqueue')
       }
       return
     }
🤖 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/main/agent/deepchat/runtime/pendingInputPump.ts` around lines 278 - 290,
The pending-input rejection path currently conflates missing scope, drain-lease
contention, and claim-adoption failure. Update the logic around
tryAcquirePendingQueueDrain and tryRelease so expected lease contention uses a
distinct non-error log signature, while genuine missing-scope or stale-instance
adoption failures retain the existing error logging and stage=adopt-claim
context; preserve release and enqueue rescheduling behavior.
🤖 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.

Nitpick comments:
In `@src/main/agent/deepchat/runtime/pendingInputPump.ts`:
- Around line 278-290: The pending-input rejection path currently conflates
missing scope, drain-lease contention, and claim-adoption failure. Update the
logic around tryAcquirePendingQueueDrain and tryRelease so expected lease
contention uses a distinct non-error log signature, while genuine missing-scope
or stale-instance adoption failures retain the existing error logging and
stage=adopt-claim context; preserve release and enqueue rescheduling behavior.

In `@test/main/agent/deepchat/runtime/pendingInputAdmissionCoordinator.test.ts`:
- Around line 202-208: Move the duplicated createDeferred helper into a shared
test utility, then update pendingInputAdmissionCoordinator.test.ts and
pendingInputPump.test.ts to import and reuse that single implementation,
removing their local definitions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e4c68f29-14f3-44cf-b322-8e3ad1f019ff

📥 Commits

Reviewing files that changed from the base of the PR and between 162c409 and 75464cb.

📒 Files selected for processing (17)
  • docs/architecture/baselines/agent-system-layered-runtime-baseline.json
  • docs/architecture/deepchat-agent-harness-boundaries/plan.md
  • docs/architecture/deepchat-agent-harness-boundaries/spec.md
  • docs/architecture/deepchat-agent-harness-boundaries/tasks.md
  • scripts/agent-cleanup-guard.mjs
  • src/main/agent/deepchat/instance/deepChatAgentInstance.ts
  • src/main/agent/deepchat/runtime/pendingInputAdmissionCoordinator.ts
  • src/main/agent/deepchat/runtime/pendingInputPump.ts
  • src/main/agent/deepchat/runtime/runLifecycleCoordinator.ts
  • src/main/agent/deepchat/runtime/sessionSettingsCoordinator.ts
  • src/main/agent/deepchat/runtime/turnCoordinator.ts
  • test/main/agent/deepchat/instance/deepChatAgentRuntime.test.ts
  • test/main/agent/deepchat/runtime/deepChatRuntimeCoordinator.test.ts
  • test/main/agent/deepchat/runtime/pendingInputAdmissionCoordinator.test.ts
  • test/main/agent/deepchat/runtime/pendingInputPump.test.ts
  • test/main/agent/deepchat/runtime/runLifecycleCoordinator.test.ts
  • test/main/scripts/agentCleanupGuard.test.ts
🚧 Files skipped from review as they are similar to previous changes (12)
  • test/main/scripts/agentCleanupGuard.test.ts
  • docs/architecture/deepchat-agent-harness-boundaries/tasks.md
  • test/main/agent/deepchat/runtime/runLifecycleCoordinator.test.ts
  • test/main/agent/deepchat/runtime/pendingInputPump.test.ts
  • docs/architecture/baselines/agent-system-layered-runtime-baseline.json
  • src/main/agent/deepchat/runtime/sessionSettingsCoordinator.ts
  • src/main/agent/deepchat/runtime/runLifecycleCoordinator.ts
  • scripts/agent-cleanup-guard.mjs
  • src/main/agent/deepchat/runtime/pendingInputAdmissionCoordinator.ts
  • docs/architecture/deepchat-agent-harness-boundaries/spec.md
  • src/main/agent/deepchat/runtime/turnCoordinator.ts
  • test/main/agent/deepchat/runtime/deepChatRuntimeCoordinator.test.ts

@yyhhyyyyyy
yyhhyyyyyy merged commit f74a35c into dev Jul 25, 2026
12 checks passed
@zhangmo8
zhangmo8 deleted the refactor/agent-runtime-ownership branch July 27, 2026 02:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant