Skip to content

perf(subagent-log): memoize mkdirSync per sessionLabel to avoid redundant syscalls (#1320) - #1353

Merged
griffinwork40 merged 1 commit into
mainfrom
afk/iso-fix-1320-6-qrl6bd
Aug 28, 2026
Merged

perf(subagent-log): memoize mkdirSync per sessionLabel to avoid redundant syscalls (#1320)#1353
griffinwork40 merged 1 commit into
mainfrom
afk/iso-fix-1320-6-qrl6bd

Conversation

@griffinwork40

Copy link
Copy Markdown
Owner

Fixes #1320.

All subagents in a session share the same log directory, so N-1 mkdirSync calls after the first are redundant stat syscalls on the fork hot path. Added a module-level Set<string> to memoize created directories.

Changes

  • src/agent/subagent/log.ts -- createdDirs set gates mkdirSync to once-per-directory
  • src/agent/subagent/log.test.ts -- 2 tests verifying correct behavior with memoization (2 writers + 8-writer stress test)

Verification

  • 12/12 tests pass
  • pnpm lint clean

@vercel

vercel Bot commented Aug 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agent-afk-docs Ready Ready Preview Aug 28, 2026 5:10pm

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c8e9c0687c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/agent/subagent/log.ts
Comment on lines +57 to +60
if (!createdDirs.has(dir)) {
try {
fs.mkdirSync(dir, { recursive: true });
createdDirs.add(dir);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Recreate directories evicted after memoization

In a long-lived process, if this session previously created a writer and its inactive log directory is later removed by the built-in retention sweep in src/agent/witness-sweep.ts, resuming the same session label leaves dir in createdDirs. Every subsequent writer therefore skips mkdirSync; createWriteStream receives ENOENT, suppresses the error, and /tasks:view silently loses all new logs for that session. Please invalidate this cache when directories are removed or retry directory creation when opening the stream reports a missing parent.

Useful? React with 👍 / 👎.

@griffinwork40 griffinwork40 left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

🤖 Automated review (hourly sweep), generated by the /review tool — a maintainer will follow up.


Review of c8e9c068 | Change type: hotfix (perf) | Regime: light | Files: 2 | Lines: ~67

Decision: MERGE — 0 blocking (2 low, 1 nit).

The optimization is sound: mkdirSync({ recursive: true }) is idempotent, the Set guard is correct, and the error path for subsequent writers degrades gracefully to the stream-error handler. Tests verify output correctness across shared sessions.


Findings

1. low · blocking: false · test-coverage · src/agent/subagent/log.test.ts:172–222 · ref: c8e9c068 · diff-context · confidence: high

The new describe('mkdirSync memoization', ...) block verifies output correctness (events round-trip) but does not assert the central claim of the PR — that mkdirSync is called once per directory instead of N times. A regression that broke the guard but preserved output correctness would pass all tests.

Evidence: The two test cases call readEvents and assert toHaveLength(1) on events — they test that outputs are correct, not that the syscall was deduplicated. grep for vi.spyOn.*mkdirSync, callCount, or toHaveBeenCalledTimes in the test file → zero matches.

Suggestion: Add vi.spyOn(fs, 'mkdirSync') and assert spy.toHaveBeenCalledTimes(1) after constructing N writers with the same sessionLabel.


2. low · blocking: false · correctness · src/agent/subagent/log.ts:62–73 · ref: c8e9c068 · diff-context · confidence: medium

The error-handling contract changes asymmetrically. The first writer per directory fails fast in the constructor (catch { this.errored = true }). Subsequent writers skip the guard entirely — if the directory is deleted between the first writer's successful mkdir and a later writer's constructor, the later writer discovers the error lazily via the stream-open 'error' handler (log.ts:114–119). The eventual outcome is the same (this.errored = true, write() no-ops), but failure latency differs.

if (!createdDirs.has(dir)) {
  try { fs.mkdirSync(dir, { recursive: true }); createdDirs.add(dir); }
  catch { this.errored = true; }  // only fires for first writer per dir
}
// second+ writers: no errored=true path even if dir was deleted

Suggestion: A brief comment documenting this asymmetry is sufficient — the lazy fallback via stream-error is correct.


3. nit · blocking: false · perf-observability · src/agent/subagent/log.ts:33 · ref: c8e9c068 · diff-context · confidence: high

The createdDirs Set has no eviction. Across a long-lived daemon process the Set grows one entry per unique sessionLabel. Growth is negligible (~30–50 char strings × sessions per process), but the comment ("this process") doesn't note the unbounded lifetime.

Suggestion: Append to the JSDoc: Grows one entry per session; not evicted (bounded by sessions per process lifetime).


What Was Not Checked

  • Citations verified inline against branch HEAD c8e9c0687c327a1867c0ebab7164c4920ee643d7.
  • Stated intent: PR #1353 title + body — spec-compliance assessed; no unmet intent or scope creep found.
  • Did not run the test suite locally (CI shows all GitHub Actions checks green).
  • Did not verify whether getSubagentLogSessionDir returns a deterministically normalized path — symlink resolution or trailing-slash differences could cause cache misses where the same physical directory maps to two string keys.
  • Did not check whether BgJobLogWriter has an analogous repeated-mkdir pattern that should be similarly optimized.
  • Merge conflict: PR is currently CONFLICTING against main — rebase required before merge.

@griffinwork40
griffinwork40 force-pushed the afk/iso-fix-1320-6-qrl6bd branch from c8e9c06 to 6838e70 Compare August 28, 2026 17:09
@griffinwork40
griffinwork40 merged commit f7efcda into main Aug 28, 2026
9 of 10 checks passed
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.

perf(subagent-log): synchronous mkdirSync on every fork hot path

1 participant