diff --git a/src/main/agent-runtime/artifact-projector.ts b/src/main/agent-runtime/artifact-projector.ts index ab282007..7cb574bc 100644 --- a/src/main/agent-runtime/artifact-projector.ts +++ b/src/main/agent-runtime/artifact-projector.ts @@ -110,17 +110,24 @@ const BASELINE_SKIP_DIR_NAMES: ReadonlySet = new Set([ ]); /** - * Walk `workspacePath` and record the mtime of every non-directory - * entry. Directories matching `BASELINE_SKIP_DIR_NAMES` are pruned at - * walk time — the same categories the registrability filter rejects — - * so the baseline stays bounded on real project trees. Stops early at + * Capture a run-start mtime baseline only for workspaces that need + * one. Git workspaces return null here because `git status` already + * provides the authoritative diff — paying for an O(tree) walk on + * every run just to throw the result away is a hot-path tax the + * scheduler should not be paying. + * + * For non-git workspaces, walk the workspace and record the mtime of + * every non-directory entry. Directories matching + * `BASELINE_SKIP_DIR_NAMES` are pruned at walk time — the same + * categories the registrability filter rejects — so the baseline + * stays bounded on real project trees. The walker stops early at * `MAX_BASELINE_ENTRIES` and flags `truncated`; the partial baseline - * still lets the diff detect every file it saw, while the operator is - * told via the skip reason that an oversize workspace fell off the - * tail. + * still covers every path that was captured while the operator sees + * the cap signal. */ -export function captureWorkspaceBaseline(workspacePath: string): WorkspaceBaseline | null { +export function captureBaselineIfRequired(workspacePath: string | null): WorkspaceBaseline | null { if (!workspacePath) return null; + if (isGitRepo(workspacePath)) return null; try { if (!statSync(workspacePath).isDirectory()) return null; } catch { diff --git a/src/main/agent-runtime/runtime-bridge.ts b/src/main/agent-runtime/runtime-bridge.ts index b64ea94c..08ec78f3 100644 --- a/src/main/agent-runtime/runtime-bridge.ts +++ b/src/main/agent-runtime/runtime-bridge.ts @@ -11,7 +11,7 @@ import type { CredentialResolver } from "@main/credentials"; import { getConnectorDefinition } from "@main/connectors"; import { projectInboxMessageToPi } from "./inbox-projection"; import { projectAgentEventToLogChunk, extractTextBody } from "./agent-event-projection"; -import { captureWorkspaceBaseline, reconcileWorkspaceArtifacts } from "./artifact-projector"; +import { captureBaselineIfRequired, reconcileWorkspaceArtifacts } from "./artifact-projector"; import { handleBeforeToolCall, handleAfterToolCall, @@ -501,13 +501,13 @@ export class RuntimeBridge { workspaceId: string | null; workspacePath: string | null; }; - // Capture an mtime baseline for non-git workspaces so `bash` and - // the end-of-run reconciliation can still diff changed files - // against a known-start reference. Git workspaces leave the - // baseline as null — `git status` is the source of truth there. - const baseline = payload.workspacePath - ? captureWorkspaceBaseline(payload.workspacePath) - : null; + // Capture an mtime baseline only for non-git workspaces so `bash` + // and the end-of-run reconciliation can still diff changed files + // against a known-start reference. Git workspaces skip the walk — + // `git status` is the source of truth there, and paying for an + // O(tree) scan on every run just to throw the result away is a + // hot-path tax the scheduler should not be paying. + const baseline = captureBaselineIfRequired(payload.workspacePath); this.registerActiveRun({ runId, agentId: payload.agentId,