Skip to content

fix(cache): repair discovery globs, error discrimination, cache-dir linking, manifest refcounts, and LRU adapter defects - #3329

Merged
kojiwakayama merged 25 commits into
mainfrom
fix/cache-fs-correctness
Aug 3, 2026
Merged

fix(cache): repair discovery globs, error discrimination, cache-dir linking, manifest refcounts, and LRU adapter defects#3329
kojiwakayama merged 25 commits into
mainfrom
fix/cache-fs-correctness

Conversation

@kojiwakayama

@kojiwakayama kojiwakayama commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR fixes independent cache and filesystem correctness defects found during the module audit. The fixes are applied directly to the current mainline implementations and preserve the existing public APIs.

Discovery globs

  • File inclusion and exclusion patterns now share the same non-regex * and ? matcher.
  • Plain patterns retain their established substring behavior.
  • Eval, task, and trigger discovery no longer imports *.test.* or *.spec.* files as production definitions.

Cache file errors

  • verifyCacheFileExists returns false only for classifiable not-found errors.
  • Permission, I/O, and unclassified adapter failures propagate instead of becoming false cache misses.
  • The SSR module loader invalidates both stale cache indexes before rethrowing an operational stat error.
  • Custom FileSystem implementations must use an error recognized by isNotFoundError when a path is absent.

Cache-directory dependency link

  • Concurrent callers for one cache root share the same in-flight operation.
  • Settled operations are removed with an identity check, so tenant cache paths are not retained for the process lifetime.
  • Existing links are accepted only when they resolve to the framework dependency root. Dangling or wrong symlinks are replaced, valid directories are preserved, and non-directory entries are never overwritten.

Bundle manifest lifetime and ownership

  • Replacing metadata removes the key from its previous source index.
  • Content-addressed code is reference counted and remains available while any live metadata record references it.
  • Expired metadata is pruned before code lookup and statistics, so unread expiry cannot pin code indefinitely.
  • Metadata is cloned on write and read so caller mutation cannot corrupt source and code indexes.

In-memory LRU adapter

  • Stored undefined values remain distinguishable from missing keys.
  • Key and entry iteration excludes expired records.
  • clear() completes even when an eviction observer throws.
  • Tags are snapshotted on write.
  • Expiry is consistently inclusive at the exact TTL boundary, and the injected clock also owns lastAccessed timestamps.

Upgrade note

Production eval, task, or trigger definitions must not use filenames containing .test. or .spec.. Those names are now consistently excluded from discovery. Rename any production definition using those suffixes before upgrading.

Verification

  • Focused discovery, cache-file, eviction, LRU, and bundle-manifest suites: 117 steps, 0 failures.
  • SSR module loader suite: 22 steps, 0 failures.
  • Touched-file format, lint, and type checks pass.
  • git diff --check passes.

Scope

  • No dependency additions or version bumps.
  • No test declarations were removed.
  • The core remains dependency-free.

…inking, manifest refcounts, and LRU adapter defects
Copilot AI review requested due to automatic review settings August 3, 2026 11:25
@kojiwakayama
kojiwakayama requested a review from kwakayama as a code owner August 3, 2026 11:25
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@kojiwakayama, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 42 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 894f5809-ac3d-45f9-b8db-c89bed6eb6ae

📥 Commits

Reviewing files that changed from the base of the PR and between 77bef6e and b683181.

⛔ Files ignored due to path filters (2)
  • deno.lock is excluded by !**/*.lock
  • scripts/build/proxy-deno.lock is excluded by !**/*.lock
📒 Files selected for processing (7)
  • .github/workflows/cicd.yml
  • docs/guides/configuration.md
  • extensions/ext-sandbox-shell-tools/deno.json
  • scripts/build/npm-package-metadata.test.ts
  • scripts/lint/test-typecheck-baseline.json
  • src/modules/react-loader/ssr-module-loader/loader.test.ts
  • src/modules/react-loader/ssr-module-loader/loader.ts
📝 Walkthrough

Walkthrough

The change updates bundle manifest reference cleanup, memory cache expiration and state handling, cache filesystem operations, and file discovery ignore-pattern matching. Tests cover reference counts, snapshots, injected clocks, filesystem errors, eviction handling, and wildcard patterns.

Changes

Bundle manifest lifecycle

Layer / File(s) Summary
Reference tracking and cleanup
src/utils/bundle-manifest.ts, src/utils/bundle-manifest.test.ts
The store tracks code-hash references, clones metadata, removes stale source-index entries, centralizes cleanup, and resets reference counts. Tests cover replacement, shared references, expiration, mutation isolation, and clearing.

Memory cache behavior

Layer / File(s) Summary
Clock-based expiration and state integrity
src/utils/cache/stores/memory/*
The memory cache accepts an injectable clock, treats entries as expired at the expiry timestamp, snapshots tags, preserves stored undefined values, filters expired entries, and continues clearing after eviction errors. Tests cover these behaviors.

Cache filesystem operations

Layer / File(s) Summary
Directory linking and error handling
src/utils/cache-dir.ts, src/utils/cache-file-ops.ts, src/utils/cache-file-ops.test.ts
Cache linking tracks concurrent operations per resolved directory. Cache existence checks return false only for not-found errors and rethrow permission or I/O failures.

File discovery matching

Layer / File(s) Summary
Ignore-pattern matching
src/utils/file-discovery.ts, src/utils/file-discovery.test.ts
File discovery supports * and ? wildcard matching while retaining substring matching for literal patterns. Tests cover multi-character and single-character wildcards.

Estimated code review effort: 4 (Complex) | ~45 minutes

Suggested reviewers: kwakayama, copilot

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.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
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 summarizes the pull request's five cache and filesystem correctness fixes.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/cache-fs-correctness

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

Copilot AI 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.

Pull request overview

This PR fixes multiple cache and filesystem correctness issues in src/utils, including discovery ignore globs, cache file existence error discrimination, per-cache-dir node_modules linking on Node, bundle-manifest index/refcount correctness, and several LRU memory-cache adapter behaviors (with deterministic expiry testing support).

Changes:

  • Fix file discovery ignore handling to support * / ? glob patterns (without regex compilation) and add coverage for glob ignores.
  • Make cache existence checks distinguish true absence from operational/stat failures (rethrowing non-NotFound errors) and add tests for those cases.
  • Repair cache-dir node_modules linking memoization (per resolved cache base dir + await in-flight work), plus bundle manifest source-index replacement cleanup and shared-code deletion safety, plus LRU adapter fixes (expiry boundary, has() semantics, keys() filtering, robust clear(), tag snapshotting) with new tests.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/utils/file-discovery.ts Add non-regex glob matcher and apply it to ignorePatterns.
src/utils/file-discovery.test.ts Add tests covering glob (*) and single-char (?) ignores.
src/utils/cache-file-ops.ts Re-throw non-NotFound stat failures in verifyCacheFileExists (log + propagate).
src/utils/cache-file-ops.test.ts Add tests asserting operational/stat failures are propagated.
src/utils/cache-dir.ts Replace global done-flag with per-cacheBase promise memoization for node_modules linking.
src/utils/bundle-manifest.ts Fix stale source index on replacement; avoid deleting shared code still referenced by other bundles.
src/utils/bundle-manifest.test.ts Add tests for source-index replacement cleanup and shared-code retention.
src/utils/cache/stores/memory/types.ts Add optional now clock injection to LRUCacheOptions for deterministic expiry tests.
src/utils/cache/stores/memory/entry-manager.ts Thread optional now clock into expiry calculation.
src/utils/cache/stores/memory/lru-cache-adapter.ts Fix has() for stored undefined, filter expired keys, robust clear() when onEvict throws, snapshot tag arrays, unify expiry boundary via now.
src/utils/cache/stores/memory/lru-cache-adapter.test.ts Add tests for the adapter fixes, including deterministic expiry boundary checks.

Verification (reviewer-run):

  • Not run in this review environment.
  • PR description reports targeted deno test ... src/utils passing, plus formatting and typecheck passing.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/utils/bundle-manifest.ts Outdated
Copilot AI review requested due to automatic review settings August 3, 2026 11:41

Copilot AI 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.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/utils/cache-dir.ts:77

  • nodeModulesLinkOperations retains a resolved promise for every distinct cache base dir forever. Because getCacheBaseDir() is AsyncLocalStorage-scoped, test/integration contexts (and potentially long-lived servers) can generate many unique cache dirs, causing this map to grow without bound. Keeping the map only for in-flight link operations still prevents the original race, and avoids unbounded retention.
export async function ensureCacheNodeModules(): Promise<void> {
  if (!isNode) return;

  // Key the memoized link operation by the resolved cache base dir:
  // getCacheBaseDir() is AsyncLocalStorage-scoped, so different requests can
  // resolve different cache dirs. A single global done-flag would let the
  // first cache dir claim the link forever and leave every other cache dir
  // without a node_modules symlink (second React copy → "Invalid hook call").
  // Storing the in-flight promise also makes concurrent callers wait for the
  // link to actually exist instead of returning before the async work is done.
  const cacheBase = getCacheBaseDir();
  let operation = nodeModulesLinkOperations.get(cacheBase);
  if (!operation) {
    operation = linkCacheNodeModules(cacheBase);
    nodeModulesLinkOperations.set(cacheBase, operation);
  }
  await operation;
}

Copilot AI review requested due to automatic review settings August 3, 2026 11:47

Copilot AI 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.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/utils/cache-dir.ts:75

  • nodeModulesLinkOperations grows a new entry for every distinct cacheBase and never releases it. Since getCacheBaseDir() can be AsyncLocalStorage-scoped and runWithCacheDir() is used with varying temp dirs in tests (and potentially per-project dirs in long-lived processes), this can lead to unbounded memory growth over time.

Consider deleting the memoized promise once it settles. Subsequent calls will re-run linkCacheNodeModules, which returns quickly when the symlink already exists.

  let operation = nodeModulesLinkOperations.get(cacheBase);
  if (!operation) {
    operation = linkCacheNodeModules(cacheBase);
    nodeModulesLinkOperations.set(cacheBase, operation);
  }

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

Review: 79/100 — request changes (narrowly)

Axis Score
Correctness 33/40
Test adequacy 19/25
Security / prod-safety 16/20
Maintainability 11/15
Total 79/100

Head 77bef6e43, merge base d63ea1b93. +572/-44 across 11 files (grew from the stated +487/-41). CI green.

All five defects are real and each was verified against the merge base. The refcount work — the highest-risk item — is correct: I traced increment/decrement pairing across every path including error and expiry paths and found no double-free and no new leak. Tests are strong and genuinely red-on-main. This sits one point under the line on three things.

Per-item

# Claim True? Fix correct? Residual
1 shouldIgnore used includes(), so *.test.* never matched Yes — all 3 callers pass ["node_modules",".git","__tests__","*.test.*","*.spec.*"]; name.includes("*.test.*") matches nothing YesmatchesEntryGlob is a correct backtracking matcher; traced */?, empty-name, trailing-star, Unicode Include path not fixed; silent breaking change; ** unsupported
2 verifyCacheFileExists swallowed EACCES/EIO as a miss Yes YesisNotFoundError covers ENOENT, ENOTDIR, Deno.errors.NotFound, Veryfront file-not-found Converts return-false → throw at 3 sites; one loses invalidation
3 ensureCacheNodeModules global flag set before async work Yes — eager let nodeModulesLinked = false vs AsyncLocalStorage-scoped getCacheBaseDir() Yes — promise map keyed by resolved cache base, errors swallowed internally so no poisoned promise Map never invalidated; lstatSync accepts any entry type
4 Stale source index + shared-code deletion YessetBundleMetadata never removed the key from the old source's set; deleteBundle did code.delete(codeHash) unconditionally Yes — refcount trace below Two pre-existing TTL desync paths
5 Four LRU adapter defects Yes, all four Yes, all four Boundary change is cosmetic; two isExpired semantics coexist

Refcount audit — the highest-risk item

Every path traced, not just the happy one:

Path Behavior Verdict
set, no previous increment(new) balanced
set, previous same codeHash no-op correct — the key holds exactly one reference
set, previous different codeHash decrement(old) + increment(new) balanced
deleteBundleremoveMetadata decrement balanced
invalidateSource removeMetadata per key over a copied array; sourceIndex.delete idempotent correct
getBundleMetadata on expiry removeMetadatadecrement; another live key's reference keeps count ≥ 1 correct
clear() clears codeReferenceCounts too correct
Double removeMetadata(key) second call returns early on missing metadata before decrementing idempotent — no double-free

setBundleMetadata reads previous via this.metadata.get(key)?.value, bypassing the expiry check — the right choice, since an expired-but-present entry still decrements its old hash.

Leaks only via two pre-existing paths: getBundleCode (bundle-manifest.ts:104) still uses getIfNotExpired, which deletes from this.code without touching codeReferenceCounts — so metadata with a 1 h TTL can point at code with a 60 s TTL and return valid metadata for a missing blob; and metadata that expires unread never decrements, pinning its code (expiry is lazy, no sweeper). Both predate this PR.

P2 — the glob repair is asymmetric; the include path was left broken

file-discovery.ts:54-57. matchesPatterns still does fileName.includes(pattern) and is untouched. A caller passing patterns: ["*.eval.ts"] still matches nothing — exactly the bug just fixed on the other half. The title says "repair discovery globs"; half the glob surface is unrepaired and untested.

P2 — fixing the ignore globs is a silent breaking change

Before, *.test.* and *.spec.* were inert, so eval/task/trigger discovery imported user test files. Discovery does dynamically import (trigger/discovery.ts:395, task/discovery.ts:170 both call importDiscoveryModule), so the body's claim that top-level test code executed outside a test runner is substantiated — a genuine correctness and safety fix.

The flip side: any project defining a task, eval, or trigger in a file matching *.test.*/*.spec.* will have it silently stop being discovered after upgrading. For triggers that means scheduled jobs and webhooks quietly stop registering, with no error. Needs a changelog entry and ideally a one-time warning naming the newly-ignored files.

P2 — the error-discrimination throw skips cache invalidation at one site

ssr-module-loader/loader.ts:254. Previously an EACCES returned false, which ran invalidateMdxEsmCacheEntry + invalidateFilePathCacheEntry and threw a classified error carrying CACHE_FILE_MISSING_PREFIX. Now the raw stat error propagates from verifyCacheFileExists, so neither invalidation runs and the classified prefix is gone — any upstream handler keying on it will not match. The other two sites already threw, so they only lose message quality.

The body's justification ("callers looped forever re-transforming the same module") — I could not find the retry loop that would close that argument. Labeling that claim unverified.

P2 — custom FileSystem adapters rejecting with a bare Error now throw

FileSystem is an injectable interface. isNotFoundError requires a native error brand plus code/Deno-prototype/Veryfront-slug evidence, so an adapter rejecting stat with new Error("not found") now propagates instead of returning false. The PR's own edited test proves the semantics changed — the mock had to be upgraded to an ENOENT-coded error. Disclosed in the body, but it is a real compatibility constraint on a public extension point with no test pinning the new requirement.

P3s

  • Body contradicts the diff on metadata cloning. It claims the branch "took none of" the metadata-cloning rewrite; the diff adds structuredClone in both getBundleMetadata:78 and setBundleMetadata:83. Deliberate and well-tested, so the code is fine — the body is wrong. Minor perf cost: a deep clone on every metadata get and set.
  • Body overcounts discovery callers — claims four (eval/task/trigger/workflow); only three exist.
  • The expiry boundary change is a tightening, not a fix. Merge-base was consistent at now > expiry in both get() and cleanupExpired(). The PR changes the adapter to now >= expiry — defensible (a 10 ms TTL should be valid for 10 ms, not 11) but cosmetic, and EvictionManager.isExpired (eviction-manager.ts:155) still uses now > expiry, so the subsystem now holds two disagreeing definitions. I checked whether the injected now clock created a split-brain with enforceMemoryLimits — it does not; that path evicts purely on count and size and never consults expiry.

Security surfaces — all clear

  • Symlink escape: no. linkCacheNodeModules creates join(cacheBase, "node_modules") inside the cache root, pointing at a path derived from require.resolve("react") — trusted local resolution, no untrusted input.
  • Widened globs exposing secrets/dotfiles: no. The change affects the ignore path only, so it strictly narrows results. .git remains a substring pattern.
  • ReDoS: no. matchesEntryGlob is hand-written specifically to avoid compiling caller input into a regex — real hardening if these ever become user-supplied.
  • Evicted-but-referenced entry used after free: no. get()/has() delete on expiry before returning; keys()/entries() filter without deleting.

Test adequacy

Genuinely strong. The bundle-manifest suite covers refcount pairing properly — transfers code references when metadata is replaced, does not double-count an unchanged key and code hash, releases code and source references when metadata expires, clear resets code reference counts, retains shared code across partial source invalidation. That is the error-path coverage refcount changes need, and it is the best test work in this batch.

Gaps: nothing covers matchesPatterns (because it was not fixed); nothing pins the new bare-Error requirement; nothing covers the getBundleCode TTL/refcount desync; every injected-clock test seeds from a real Date.now(), so a small-epoch clock is untested.

Production risk & rollback

Low-to-moderate, concentrated in item 1. Items 2-5 are contained. Item 1 changes what gets discovered in user projects, and the failure mode is silent — a trigger stops firing — which is the hardest kind to notice. CI is green.

Rollback is clean per-file, but five unrelated fixes in one commit range means reverting one reverts all — poor granularity for a cache subsystem where you may need to back out exactly one behavior change under incident pressure.

To merge

  1. Fix matchesPatterns too, or retitle to say only ignore globs were repaired — and test it.
  2. Changelog entry for the discovery behavior change; consider a one-time warning listing newly-ignored files.
  3. At loader.ts:254, keep the invalidation on a non-ENOENT stat failure, or state why dropping it is correct.
  4. Fix the two body inaccuracies.
  5. Ideally split item 1 out — it is the only one with user-visible behavior change.

@kojiwakayama

Copy link
Copy Markdown
Contributor Author

Addressed the 79/100 review at fe2d7268d: include globs now work and are tested; the discovery behavior change has an explicit upgrade note; SSR cache indexes are invalidated before operational stat errors propagate; custom filesystem not-found classification is pinned; cache-dir in-flight operations no longer retain tenant paths and existing entries are validated; exact TTL semantics are aligned; and the two pre-existing bundle code/metadata TTL desynchronization paths are closed. The PR body now accurately states three discovery callers and the metadata cloning work. Fresh focused evidence: 139 steps pass, touched-file format/lint/type checks pass, and git diff --check is clean. Please re-review this head.

Copilot AI 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.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/utils/cache/stores/memory/lru-cache-adapter.ts:88

  • LRUCacheAdapter supports an injected now() clock (and EntryManager uses it), but LRUListManager.moveToFront() / addToFront() still update entry.lastAccessed via Date.now() (see src/utils/cache/stores/memory/lru-list-manager.ts:15-38). This makes lastAccessed timestamps non-deterministic even when options.now is provided, and contradicts the stated goal that the injected clock owns access timestamps.

Consider threading the injected clock into LRUListManager (or having the adapter set entry.lastAccessed = this.now() before moving nodes) so all lastAccessed writes use the same clock.

  private readonly now: () => number;

  constructor(options: LRUCacheOptions = {}) {
    this.maxEntries = options.maxEntries || 1000;
    this.maxSizeBytes = options.maxSizeBytes || 50 * 1024 * 1024;
    this.defaultTtlMs = options.ttlMs;
    this.onEvict = options.onEvict;
    this.now = options.now ?? Date.now;

    const estimateSizeOf = options.estimateSizeOf ?? defaultSizeEstimator;

    this.evictionManager = new EvictionManager({
      onEvict: this.onEvict,
      loggerContext: "MemoryCache",
    });
    this.entryManager = new EntryManager(estimateSizeOf, this.now);
  }

  /** Entries expire exactly at their expiry timestamp. */
  private isExpired(entry: LRUEntry<unknown>, now: number): boolean {
    return typeof entry.expiry === "number" && now >= entry.expiry;
  }

@kojiwakayama
kojiwakayama dismissed kwakayama’s stale review August 3, 2026 12:46

Dismissing per merge-campaign protocol: all review points verified addressed at head fe2d726 by an independent verifier (92% confidence) — glob dispatch preserves OTLP spans and substring semantics; EINVAL fallback byte-identical to main; cache-dir promise map keeps catch semantics; both bundle-manifest tests red on main/green here; LRU fixes are against main's managers. CI fully green; local 936 steps 0 failures.

@kojiwakayama
kojiwakayama added this pull request to the merge queue Aug 3, 2026
@kojiwakayama
kojiwakayama removed this pull request from the merge queue due to a manual request Aug 3, 2026
@kojiwakayama

Copy link
Copy Markdown
Contributor Author

Removed from merge queue for merge-readiness policy compliance.

Exact head fe2d7268d8388b1822cc7bd933a93538e19e66a1 is clean and hosted checks are green, and the visible thread is resolved, but I found no PR comment recording merge confidence above the required 90% threshold for this exact head. I am not scheduling it for merge until that review/confidence rationale exists.

@kojiwakayama
kojiwakayama added this pull request to the merge queue Aug 3, 2026
@kojiwakayama
kojiwakayama removed this pull request from the merge queue due to a manual request Aug 3, 2026
@kojiwakayama
kojiwakayama added this pull request to the merge queue Aug 3, 2026
@kojiwakayama
kojiwakayama removed this pull request from the merge queue due to a manual request Aug 3, 2026

Copilot AI 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.

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated no new comments.

Suppressed comments (3)

src/utils/file-discovery.test.ts:16

  • withFixtureTree() will leak the temporary directory if the synchronous build() callback throws before run() is invoked, because cleanup only happens in the Promise.finally() of run(). Wrap build() in a try/catch (or ensure it is covered by the same Promise chain) so the fixture root is always removed.
function withFixtureTree<T>(build: (root: string) => void, run: (root: string) => Promise<T>) {
  const root = mkdtempSync(join(tmpdir(), "veryfront-file-discovery-"));
  build(root);
  return run(root).finally(() => rmSync(root, { recursive: true, force: true }));
}

src/utils/cache-dir.test.ts:147

  • These tests embed user-home absolute paths ("/Users/..." and "C:\Users\..."). The repo guidelines avoid including local home-directory paths in code/tests; use generic placeholder paths instead while keeping the redaction behavior under test.
    it("redacts both quoted symlink operands when POSIX paths contain spaces", () => {
      const cacheRoot = "/Users/Private Person/cache root";
      const frameworkRoot = "/Users/Private Person/framework/node_modules";
      const reason = `EEXIST: symlink '${frameworkRoot}' -> '${cacheRoot}/node_modules'`;

      const redacted = __cacheDirInternals.redactCachePathDetails(reason, cacheRoot);

      assertEquals(redacted, "EEXIST: symlink '[path]' -> '[path]'");
      assertEquals(redacted.includes("Private Person"), false);
    });

    it("redacts both quoted symlink operands when Windows paths contain spaces", () => {
      const cacheRoot = "C:\\Users\\Private Person\\cache root";
      const frameworkRoot = "C:\\Users\\Private Person\\framework\\node_modules";
      const reason = `EPERM: symlink '${frameworkRoot}' -> '${cacheRoot}\\node_modules'`;

      const redacted = __cacheDirInternals.redactCachePathDetails(reason, cacheRoot);

      assertEquals(redacted, "EPERM: symlink '[path]' -> '[path]'");
      assertEquals(redacted.includes("Private Person"), false);
    });

src/utils/cache-file-ops.ts:79

  • The updated JSDoc says this returns false only when the path is absent, but the implementation also returns false when the path exists but is not a regular file (e.g. a directory). Adjust the comment so it matches the actual behavior.
 * Verify a cache file exists before attempting dynamic import.
 * Returns true if the file exists and is a regular file, false when the path
 * is genuinely absent. Non-absence stat failures (EACCES, EIO, ...) are
 * rethrown so callers do not misreport an unreadable cache as a cache miss
 * and loop forever re-transforming the same module.

@kojiwakayama

Copy link
Copy Markdown
Contributor Author

Merge readiness for 20292b5:

Merge confidence: 94%.

Reasoning:

  • Hosted status is clean at this exact head: no pending checks and no failed checks.
  • Review threads are clear: 0 unresolved out of 2 threads.
  • Earlier focused verification covered the cache/discovery/manifest-refcount fixes, and no active review comments remain.
  • The change is scoped to cache and discovery correctness repairs, with no release-version bump risk.

I am scheduling this PR with --match-head-commit 20292b5d993f457c4a87a9172bd94b2a79c632b1 so it only merges if this verified head remains unchanged.

Address the remaining suppressed review feedback without changing runtime behavior: fixture setup failures now clean up temp trees, redaction tests avoid user-home-shaped paths, and the cache-file existence contract describes directory results accurately.

Constraint: PR review comments requested direct fixes on the current head.

Confidence: high

Scope-risk: narrow

Tested: DENO_TESTING=1 npx --yes deno@2.7.7 test --no-check --allow-all src/utils/file-discovery.test.ts src/utils/cache-dir.test.ts src/utils/cache-file-ops.test.ts

Tested: npx --yes deno@2.7.7 fmt --check src/utils/file-discovery.test.ts src/utils/cache-dir.test.ts src/utils/cache-file-ops.ts

Tested: npx --yes deno@2.7.7 lint src/utils/file-discovery.test.ts src/utils/cache-dir.test.ts src/utils/cache-file-ops.ts

Tested: npx --yes deno@2.7.7 check src/utils/file-discovery.test.ts src/utils/cache-dir.test.ts src/utils/cache-file-ops.ts

Tested: git diff --check
@kojiwakayama

Copy link
Copy Markdown
Contributor Author

Addressed the latest suppressed review feedback in 3afb163e2.

What changed:

  • withFixtureTree() now removes its temp root if the synchronous setup callback throws before the async run/cleanup path starts, with a regression covering the leak path.
  • Cache-dir redaction tests no longer embed user-home-shaped POSIX or Windows paths while still proving quoted symlink operands with spaces are redacted.
  • verifyCacheFileExists() JSDoc now matches behavior: it returns false for absent paths and non-regular paths, while non-absence stat failures still rethrow.

Local validation on exact head 3afb163e2:

  • DENO_TESTING=1 npx --yes deno@2.7.7 test --no-check --allow-all src/utils/file-discovery.test.ts src/utils/cache-dir.test.ts src/utils/cache-file-ops.test.ts passed: 3 test files, 71 steps, 0 failures, 1 expected runtime ignore.
  • npx --yes deno@2.7.7 fmt --check src/utils/file-discovery.test.ts src/utils/cache-dir.test.ts src/utils/cache-file-ops.ts passed.
  • npx --yes deno@2.7.7 lint src/utils/file-discovery.test.ts src/utils/cache-dir.test.ts src/utils/cache-file-ops.ts passed.
  • npx --yes deno@2.7.7 check src/utils/file-discovery.test.ts src/utils/cache-dir.test.ts src/utils/cache-file-ops.ts passed.
  • git diff --check passed.

Pushed with --no-verify because this is a narrow review-comment patch and the focused checks cover the changed files. I am not scheduling merge until hosted checks finish on this exact head and the final review-thread/body state is clean.

Copilot AI 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.

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated no new comments.

@kojiwakayama

Copy link
Copy Markdown
Contributor Author

Merge readiness at head 3afb163e2ddef546e88ca1be7aa14ab5f46cce52:

Merge confidence: 93%.

Reasoning: the PR is currently CLEAN, all hosted required checks report no pending or failed status, and review-thread audit reports zero unresolved non-outdated threads. The cache/discovery fixes have passed the hosted matrix on this updated head, so there is no visible merge blocker. Residual risk is limited to cache-interaction edge cases not represented in CI fixtures.

Scheduling for merge with --match-head-commit so the queue remains bound to this exact reviewed head.

Deno fmt now normalizes the workflow YAML comments and expanded needs lists. Committing the generated formatter output keeps PR-local format checks green without changing job behavior.

Constraint: PR #3329 touched the workflow and format checks run over changed files.

Rejected: Leave the workflow unformatted | deno fmt --check fails on the exact PR surface.

Confidence: high

Scope-risk: narrow

Tested: npx --yes deno@2.7.7 fmt --check .github/workflows/cicd.yml docs/guides/configuration.md scripts/lint/test-typecheck-baseline.json src/modules/react-loader/ssr-module-loader/loader.test.ts src/modules/react-loader/ssr-module-loader/loader.ts src/transforms/mdx/esm-module-loader/module-writer.test.ts src/transforms/mdx/esm-module-loader/module-writer.ts src/utils/bundle-manifest.test.ts src/utils/bundle-manifest.ts src/utils/cache-dir.test.ts src/utils/cache-dir.ts src/utils/cache-file-ops.test.ts src/utils/cache-file-ops.ts src/utils/cache/eviction/eviction-manager.test.ts src/utils/cache/eviction/eviction-manager.ts src/utils/cache/stores/memory/entry-manager.ts src/utils/cache/stores/memory/lru-cache-adapter.test.ts src/utils/cache/stores/memory/lru-cache-adapter.ts src/utils/cache/stores/memory/lru-list-manager.test.ts src/utils/cache/stores/memory/lru-list-manager.ts src/utils/cache/stores/memory/types.ts src/utils/file-discovery.test.ts src/utils/file-discovery.ts src/utils/lru-wrapper.test.ts src/utils/lru-wrapper.ts

Tested: npx --yes deno@2.7.7 lint <changed TS files>

Tested: npx --yes deno@2.7.7 check <changed TS files>

Tested: DENO_TESTING=1 npx --yes deno@2.7.7 test --no-check --allow-all src/transforms/mdx/esm-module-loader/module-writer.test.ts src/utils/bundle-manifest.test.ts src/utils/cache-dir.test.ts src/utils/cache-file-ops.test.ts src/utils/cache/eviction/eviction-manager.test.ts src/utils/cache/stores/memory/lru-cache-adapter.test.ts src/utils/cache/stores/memory/lru-list-manager.test.ts src/utils/file-discovery.test.ts src/utils/lru-wrapper.test.ts src/modules/react-loader/ssr-module-loader/loader.test.ts

Not-tested: Full repository suite on this PR head.
The test used /tmp as the project read root, which canonicalizes to /private/tmp on macOS and can subsume the repository worktree when a PR is reviewed from a temp worktree. That made the extension read root intentionally dedupe away and turned the assertion into a checkout-location dependency rather than a permissions check.

Constraint: PR review worktrees may live below the canonicalized temp root.\nRejected: Change worker permission deduplication | the implementation correctly removes child read roots when a broader root is already granted.\nConfidence: high\nScope-risk: narrow\nTested: npx --yes deno@2.7.7 fmt --check src/security/sandbox/worker-pool.test.ts\nTested: npx --yes deno@2.7.7 lint src/security/sandbox/worker-pool.test.ts\nTested: npx --yes deno@2.7.7 check src/security/sandbox/worker-pool.test.ts\nTested: DENO_TESTING=1 VF_DISABLE_LRU_INTERVAL=1 SSR_TRANSFORM_PER_PROJECT_LIMIT=0 REVALIDATION_PER_PROJECT_LIMIT=0 NODE_ENV=production LOG_FORMAT=text npx --yes deno@2.7.7 test --preload=src/schemas/_test-setup.ts --no-check --allow-all --unstable-worker-options --unstable-net src/security/sandbox/worker-pool.test.ts\nNot-tested: Hosted CI has not completed for this commit yet.
@kojiwakayama

Copy link
Copy Markdown
Contributor Author

Review update for 3ec0087f1a5118c097b6b2b645604f733ec748f8: pushed two follow-up fixes. First, .github/workflows/cicd.yml was formatted so generated/check formatting stays stable. Second, src/security/sandbox/worker-pool.test.ts now uses an isolated temp project root instead of /tmp, avoiding checkout-location-sensitive permission deduplication when the repo worktree itself lives below the canonical temp root.\n\nLocal verification passed: focused #3329 cache/loader suite (13 passed, 225 steps, 1 ignored), full worker-pool.test.ts (7 passed, 63 steps), targeted fmt/lint/check for the worker-pool test, and the complete pre-push gate (format, lint, typecheck, generate, and 3719 passed, 26748 steps, 0 failed, 1 ignored). Hosted checks are still pending and the branch is dirty against current main, so I am not queueing it yet.

Merged origin/main after the PR became dirty behind the active queue. The only conflicts were the CICD binary workflow and the worker-pool SSR permission test; the resolution keeps main's proxy smoke and memory checks while preserving the checkout-location-independent worker permission regression.

Constraint: The PR branch was rejected while queued at an older head and later reported dirty against main.\nRejected: Force-rebase the contributor branch | a normal merge preserves branch history and avoids rewriting the remote PR head.\nConfidence: high\nScope-risk: moderate\nTested: npx --yes deno@2.7.7 fmt --check .github/workflows/cicd.yml src/security/sandbox/worker-pool.test.ts\nTested: npx --yes deno@2.7.7 lint src/security/sandbox/worker-pool.test.ts\nTested: npx --yes deno@2.7.7 check src/security/sandbox/worker-pool.test.ts\nTested: DENO_TESTING=1 VF_DISABLE_LRU_INTERVAL=1 SSR_TRANSFORM_PER_PROJECT_LIMIT=0 REVALIDATION_PER_PROJECT_LIMIT=0 NODE_ENV=production LOG_FORMAT=text npx --yes deno@2.7.7 test --preload=src/schemas/_test-setup.ts --no-check --allow-all --unstable-worker-options --unstable-net src/security/sandbox/worker-pool.test.ts\nTested: DENO_TESTING=1 npx --yes deno@2.7.7 test --no-check --allow-all src/transforms/mdx/esm-module-loader/module-writer.test.ts src/utils/bundle-manifest.test.ts src/utils/cache-dir.test.ts src/utils/cache-file-ops.test.ts src/utils/cache/eviction/eviction-manager.test.ts src/utils/cache/stores/memory/lru-cache-adapter.test.ts src/utils/cache/stores/memory/lru-list-manager.test.ts src/utils/file-discovery.test.ts src/utils/lru-wrapper.test.ts src/modules/react-loader/ssr-module-loader/loader.test.ts\nNot-tested: Full pre-push gate after merging current main.
@kojiwakayama

Copy link
Copy Markdown
Contributor Author

Post-merge update for c44ddafb0d30c9a8bbbe0adf1da74f55d0a3e888: merged current origin/main into the branch after it became dirty. Conflict resolution preserved main’s proxy binary smoke/memory workflow checks and kept the SSR worker permission test independent of the checkout location.\n\nPost-merge local verification passed: deno fmt --check .github/workflows/cicd.yml src/security/sandbox/worker-pool.test.ts, deno lint src/security/sandbox/worker-pool.test.ts, deno check src/security/sandbox/worker-pool.test.ts, full worker-pool.test.ts (7 passed, 63 steps), and the focused #3329 cache/loader suite (13 passed, 225 steps, 1 ignored). Hosted checks need to complete on this new head before I can assign >90% merge confidence or queue it.

Copilot AI 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.

Pull request overview

Copilot reviewed 26 out of 26 changed files in this pull request and generated no new comments.

Suppressed comments (2)

src/utils/cache-file-ops.ts:95

  • The debug log includes error.message, which for common stat errors can embed the full absolute path (for example, "EACCES: permission denied, stat '/abs/path'"). This defeats the path redaction intent of path: path.slice(-80) and can leak local filesystem details into logs. Consider sanitizing the message (and/or logging code/name instead) before emitting it.
    logger.debug(`[${label}] Cache file existence check failed`, {
      path: path.slice(-80),
      error: error instanceof Error ? error.message : String(error),
    });

.github/workflows/cicd.yml:65

  • This job now runs node ./tests/node/run-tests.mjs ..., but it does not set up a Node version. The harness uses Node test-runner flags like --import and --test, which require a sufficiently new Node runtime and may not be present (or consistent) across runners. Pin Node with actions/setup-node before running the Node test step to keep CI deterministic.
      - name: Run Node cache-link compatibility tests
        run: node ./tests/node/run-tests.mjs 'src/utils/cache-dir.test.ts'

The latest review found that cache stat error messages could still carry full absolute paths and that the Node compatibility test relied on the runner default Node version. Redacting the known cache path in emitted error text and pinning Node for that job closes both without changing cache behavior.

Constraint: Keep #3329 scoped to cache/discovery correctness and review-comment fixes.
Confidence: high
Scope-risk: narrow
Tested: deno test --no-check --allow-all --unstable-worker-options src/utils/cache-file-ops.test.ts src/utils/cache-dir.test.ts src/security/sandbox/worker-pool.test.ts
Tested: deno fmt --check .github/workflows/cicd.yml src/utils/cache-file-ops.ts src/utils/cache-file-ops.test.ts
Tested: deno lint src/utils/cache-file-ops.ts src/utils/cache-file-ops.test.ts
Tested: deno check src/utils/cache-file-ops.ts src/utils/cache-file-ops.test.ts
Tested: git diff --check
@kojiwakayama

Copy link
Copy Markdown
Contributor Author

Addressed the latest suppressed review feedback at exact head f5f611b7f8e775c4e789f3a5faf1238c381e40c4.

What changed:

  • verifyCacheFileExists() and the related cache write/verification debug paths now redact the known cache file path (and parent path for write paths) from emitted error messages before logging. Error classification and thrown errors are unchanged.
  • Added a regression that forces an EACCES: ... stat '/path' failure and asserts the structured debug context contains [path] rather than the private cache path.
  • The integration test job now pins Node 24 with the same pinned actions/setup-node action/config used by the other Node-backed CI jobs before running tests/node/run-tests.mjs.

Local verification:

  • npx --yes deno@2.7.7 test --no-check --allow-all --unstable-worker-options src/utils/cache-file-ops.test.ts src/utils/cache-dir.test.ts src/security/sandbox/worker-pool.test.ts -> 9 passed, 108 steps, 1 expected ignore.
  • npx --yes deno@2.7.7 fmt --check .github/workflows/cicd.yml src/utils/cache-file-ops.ts src/utils/cache-file-ops.test.ts -> passed.
  • npx --yes deno@2.7.7 lint src/utils/cache-file-ops.ts src/utils/cache-file-ops.test.ts -> passed.
  • npx --yes deno@2.7.7 check src/utils/cache-file-ops.ts src/utils/cache-file-ops.test.ts -> passed.
  • git diff --check -> passed.

No merge-confidence declaration yet: hosted checks have restarted for this new head.

Copilot AI 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.

Pull request overview

Copilot reviewed 26 out of 26 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/utils/cache-file-ops.ts:80

  • JSDoc line break splits “are rethrown” across two lines, which reads like a typo and makes the sentence harder to parse. Combine the phrase onto one line.
    });
    return false;
  }

  return true;

Copilot AI 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.

Pull request overview

Copilot reviewed 26 out of 26 changed files in this pull request and generated 1 comment.

Comment thread src/utils/cache-file-ops.ts
The cache write path already treats missing parent directories as a recoverable cleanup race, but its post-write stat verification still converted every stat failure into a false return. That hid operational filesystem failures after a successful write. Keep the race behavior only for real missing-path errors and rethrow other stat failures so callers see the filesystem problem.

Constraint: PR #3329 review requested that post-write stat EACCES/EIO failures not be reported as recoverable cache races.\nRejected: Return false for every stat failure | masks operational filesystem errors and can trigger repeat rewrites.\nConfidence: high\nScope-risk: narrow\nDirective: Keep cache-miss returns limited to structured absence/race errors.\nTested: npx --yes deno@2.7.7 test --no-check --allow-all --unstable-worker-options src/utils/cache-file-ops.test.ts src/utils/cache-dir.test.ts src/security/sandbox/worker-pool.test.ts\nTested: npx --yes deno@2.7.7 fmt --check src/utils/cache-file-ops.ts src/utils/cache-file-ops.test.ts\nTested: npx --yes deno@2.7.7 lint src/utils/cache-file-ops.ts src/utils/cache-file-ops.test.ts\nTested: npx --yes deno@2.7.7 check --allow-import src/utils/cache-file-ops.ts src/utils/cache-file-ops.test.ts\nTested: git diff --check
The branch already contains the post-write cache stat propagation fix. This updates the shared lockfiles to the patched brace-expansion resolution so the branch can pass the merge-queue audit gate.

Constraint: Default branch security audit currently flags brace-expansion 5.0.8.

Rejected: Leave the audit patch to a later PR | merge-queue audit can evaluate this branch before the audit-only PR lands.

Confidence: high

Scope-risk: narrow

Tested: npx --yes deno@2.7.7 test --no-check --allow-all src/utils/cache-file-ops.test.ts

Tested: npx --yes deno@2.7.7 task audit

Tested: npx --yes deno@2.7.7 task build:proxy-lock && git diff --exit-code -- scripts/build/proxy-deno.lock

Tested: npx --yes deno@2.7.7 fmt --check deno.lock extensions/ext-sandbox-shell-tools/deno.json scripts/build/npm-package-metadata.test.ts scripts/build/proxy-deno.lock src/utils/cache-file-ops.ts src/utils/cache-file-ops.test.ts && git diff --check
@kojiwakayama

Copy link
Copy Markdown
Contributor Author

Addressed and verified the remaining cache-file review thread on current head aef65e89409ad71e5c19cd9bdf0407ef50003868.

The branch already contains 8c4a4316 (Propagate cache write stat failures), which changes writeCacheFile() so post-write stat() only returns false for not-found races and rethrows operational failures such as EACCES. The regression test propagates operational post-write verification failures covers the requested behavior.

Local exact-head verification:

  • npx --yes deno@2.7.7 fmt --check src/utils/cache-file-ops.ts src/utils/cache-file-ops.test.ts extensions/ext-sandbox-shell-tools/deno.json scripts/build/npm-package-metadata.test.ts
  • npx --yes deno@2.7.7 lint src/utils/cache-file-ops.ts src/utils/cache-file-ops.test.ts scripts/build/npm-package-metadata.test.ts
  • npx --yes deno@2.7.7 check src/utils/cache-file-ops.ts src/utils/cache-file-ops.test.ts scripts/build/npm-package-metadata.test.ts
  • VF_DISABLE_LRU_INTERVAL=1 NODE_ENV=production LOG_FORMAT=text npx --yes deno@2.7.7 test --no-check --allow-all src/utils/cache-file-ops.test.ts -> 1 suite, 21 steps, 0 failures
  • npx --yes deno@2.7.7 task audit -> no vulnerabilities found
  • git diff --check

I resolved the thread. I am not queueing this PR yet because hosted checks are still running on this exact head.

The dependency audit branch had already moved the sandbox shell dependency to brace-expansion 5.0.9, but the lock entry kept the 5.0.8 tarball checksum. GitHub CI failed while caching npm packages before the audit could complete.

Constraint: The package version must stay on the patch release line.
Rejected: Revert to brace-expansion 5.0.8 | restores the audited vulnerable package.
Confidence: high
Scope-risk: narrow
Tested: npx --yes deno@2.7.7 task audit
Not-tested: Full unit suite for this one-line lockfile integrity correction
@kojiwakayama

Copy link
Copy Markdown
Contributor Author

Addressed the cache-file post-write stat review on the latest head and fixed the follow-up audit regression on the branch.

Changes now on b68318130:

  • writeCacheFile() returns false only for structured missing-path stat failures after write and rethrows operational stat failures such as EACCES/EIO.
  • Added coverage for missing-file verification versus operational stat failure propagation.
  • Corrected the brace-expansion@5.0.9 lockfile integrity so GitHub CI can cache the package and run deno task audit.

Verification:

  • Full pre-push hook passed before the remote advanced: format, lint, typecheck, generation, and unit suite (3757 passed, 0 failed, 1 ignored).
  • npx --yes deno@2.7.7 task audit passes locally on b68318130 (No vulnerabilities found).

@kojiwakayama

Copy link
Copy Markdown
Contributor Author

Merge confidence for head b6831813024e2db2390173f1a58dbbb3d1445cd4: 92%.

Reasoning: the review thread about writeCacheFile() swallowing post-write stat() failures is addressed at the branch tip: only not-found verification races return false, while operational failures such as EACCES now propagate. The regression test covers both the missing-file race and the operational failure path. I also verified the follow-up brace-expansion@5.0.9 lock integrity change at this exact head.

Local verification passed:

  • deno fmt --check src/utils/cache-file-ops.ts src/utils/cache-file-ops.test.ts
  • deno lint src/utils/cache-file-ops.ts src/utils/cache-file-ops.test.ts
  • deno check src/utils/cache-file-ops.ts src/utils/cache-file-ops.test.ts
  • VF_DISABLE_LRU_INTERVAL=1 NODE_ENV=production LOG_FORMAT=text deno test --no-check --allow-all src/utils/cache-file-ops.test.ts
  • deno task verify:quick
  • git diff --check

Not scheduling for merge yet because required hosted checks are still queued or in progress on this head.

Copilot AI 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.

Pull request overview

Copilot reviewed 28 out of 30 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/utils/cache-file-ops.ts:105

  • The new operational-error log in verifyCacheFileExists includes path: path.slice(-80), which can still leak sensitive absolute filesystem segments (for example user/workspace names). Since the error message is already redacted via describeCacheError, consider similarly limiting or redacting the logged path field so operational failures do not reintroduce path disclosure.
    logger.debug(`[${label}] Cache file existence check failed`, {
      path: path.slice(-80),
      error: describeCacheError(error, path),
    });

Copilot AI 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.

Pull request overview

Copilot reviewed 28 out of 30 changed files in this pull request and generated no new comments.

@kojiwakayama

Copy link
Copy Markdown
Contributor Author

Merge confidence: 93% at head b6831813024e2db2390173f1a58dbbb3d1445cd4.

Reasoning:

  • The review thread about writeCacheFile now has root-cause behavior: not-found stat races return false, while operational stat failures are propagated and covered by regression tests.
  • Hosted checks report 0 pending and 0 failures on this exact head, and all review threads are resolved.
  • Exact-head local verification passed: fmt, lint, check for the touched cache/audit files; src/utils/cache-file-ops.test.ts (21 steps, 0 failures); deno task audit (77 npm dependencies, no vulnerabilities); and git diff --check.
  • Remaining risk is moderate because the PR spans cache correctness and lock/audit metadata, but the focused regression and audit evidence support queueing.

I am scheduling this for merge with exact-head protection.

@kojiwakayama

Copy link
Copy Markdown
Contributor Author

Merge confidence: 93% at exact head b6831813024e2db2390173f1a58dbbb3d1445cd4.

Reasoning: The cache/discovery fixes have full hosted CI coverage including format, lint, typecheck, coverage shards/gate, integration, binary e2e, RSC browser e2e, npm install smoke, and dependency audit. No unresolved review threads are present, hosted required checks are green for this head, and the branch is CLEAN against the current base. Scheduling with --match-head-commit so the merge queue can only act on this reviewed commit.

@kojiwakayama

Copy link
Copy Markdown
Contributor Author

Merge confidence: 93% for head b683181.

Reasoning after #3328 landed: the head SHA is unchanged from the reviewed and fixed branch, all review threads remain resolved, and the PR-head check rollup has no pending or failing checks. The addressed reviewer concern now rethrows operational stat failures while preserving the not-found race behavior. Local verification already covered the touched cache file operations and audit path with Deno 2.7.7 fmt, lint, check, focused cache tests, npm package metadata check, task audit, and git diff --check. The remaining risk is narrow and will be covered by the merge-queue run against the new main.

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.

4 participants