Skip to content

fix(resource-loader): track mod.ts-based extension dirs in managed-resources manifest#3481

Open
NilsR0711 wants to merge 1 commit intogsd-build:mainfrom
NilsR0711:fix/remote-questions-manifest-tracking
Open

fix(resource-loader): track mod.ts-based extension dirs in managed-resources manifest#3481
NilsR0711 wants to merge 1 commit intogsd-build:mainfrom
NilsR0711:fix/remote-questions-manifest-tracking

Conversation

@NilsR0711
Copy link
Copy Markdown
Contributor

TL;DR

What: remote-questions was silently excluded from installedExtensionDirs in managed-resources.json.
Why: The entry-point filter only checked for index.js/index.ts; remote-questions uses mod.ts.
How: Extend the filter to also match mod.js/mod.ts.

What

  • src/resource-loader.ts — one-line fix in writeManagedResourceManifest
  • src/tests/resource-loader.test.ts — regression test (test-first, verified red before fix)

Why

writeManagedResourceManifest builds installedExtensionDirs by scanning bundledExtensionsDir and keeping only subdirectories that contain index.js or index.ts. remote-questions uses mod.ts as its entry point and has no index.* file — so it was never recorded in the manifest.

This caused two problems:

  1. Partial copies not detected or repaired. On upgrade, initResources skips the sync when version + content hash match. If a previous sync was interrupted and only 8 of 14 files landed, the missing files (format.js, notify.js, telegram-adapter.js, …) are never restored.
  2. Directory never pruned. If remote-questions is removed from the bundle in a future release, the stale copy in ~/.gsd/agent/extensions/remote-questions/ would remain forever.

Observed crash symptom:

Cannot find module '/home/user/.gsd/agent/extensions/remote-questions/format.js'
imported from /home/user/.gsd/agent/extensions/remote-questions/mod.js

How

Extended the filter from:

existsSync(join(dirPath, 'index.js')) || existsSync(join(dirPath, 'index.ts'))

to also match mod.js/mod.ts — the two entry-point conventions present in the bundled extensions directory.

The regression test runs initResources() against a temp dir and reads the resulting managed-resources.json, asserting that remote-questions appears in installedExtensionDirs.

Closes #2367

Verified with AI.

…sources manifest

remote-questions uses mod.ts as its entry point instead of index.ts.
The installedExtensionDirs filter in writeManagedResourceManifest only
checked for index.js/index.ts, so remote-questions was silently excluded
from the manifest.

This caused two problems on upgrades:
- Partial copies of remote-questions were not detected or repaired
- The directory was never pruned when removed from the bundle

The symptom was a crash at startup:
  Cannot find module '.../remote-questions/format.js'

Fix: extend the entry-point check to also match mod.js/mod.ts.

Adds a regression test that reads managed-resources.json after
initResources() and asserts remote-questions is present in
installedExtensionDirs.

Closes gsd-build#2367

Verified with AI.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 3, 2026

🟠 PR Risk Report — HIGH

Files changed 2
Systems affected 2
Overall risk 🟠 HIGH

Affected Systems

Risk System
🟠 high Extension Registry
🟢 low Loader/Bootstrap
File Breakdown
Risk File Systems
🟠 src/resource-loader.ts Loader/Bootstrap, Extension Registry
src/tests/resource-loader.test.ts (unclassified)

⚠️ High risk — please run full integration tests and verify tool/extension contracts.

Copy link
Copy Markdown
Collaborator

@trek-e trek-e left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Copy Markdown
Collaborator

@trek-e trek-e left a comment

Choose a reason for hiding this comment

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

Verdict: APPROVE | Severity: MINOR

The fix is correct and the regression test validates the stated behavior. A few observations:

What's right:

  • One-line change in writeManagedResourceManifest is minimal and correct. mod.js/mod.ts are the only other entry-point conventions present in the bundled extensions dir per the PR description, so the expansion is bounded.
  • The test drives initResources() against a real temp dir and reads the manifest — not a mock, not a source-scan. This is the right level of validation for a file-system–touching path.
  • t.after cleanup is correct and won't leak on failure.

MINOR — test doesn't actually exercise the fix:
The new test (initResources tracks mod.ts-based extension dirs) calls initResources(fakeAgentDir) against an empty temp dir that has no bundled extensions at all. It then asserts dirs.includes("remote-questions") — but remote-questions is a bundled extension that lives inside the installed package, not the test's temp dir. Unless initResources copies the real bundled extensions into the temp dir during the call (i.e., it runs copyDirRecursive from the real bundledExtensionsDir), this test will pass vacuously if installedExtensionDirs simply doesn't exist yet, or it will fail because remote-questions was never written there. Either outcome means the test is not actually asserting what the comment claims.

The existing passing-CI state may mask this if initResources writes real extension dirs in place during the test run. Confirm the test actually goes red without the one-line fix — the PR description says "verified red before fix", which is the key claim here.

MINOR — existsSync probing has a TOCTOU window:
Not actionable in this PR, but the filter does four existsSync calls per directory. Under concurrent initResources calls (unlikely but possible on first install) a directory could be deleted between the probe and the subsequent copy. Not a blocker for this fix.

No security or logic issues. The fix itself is exactly right.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working High Priority

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: resource-loader: remote-questions not tracked in managed-resources manifest; partial sync leaves missing files

2 participants