fix(cli): upgrade patches repo-local node_modules; doctor verifies two-layer commit hooks#1463
Conversation
…s in doctor The postinstall ESM patcher returned at the first search root that already had a patched copy, so on global installs the consumer repo's node_modules was never reached — the cwd entry added to SEARCH_ROOTS in 2d9f0b4 was effectively dead code. The patcher now patches every root it finds, and squad upgrade re-runs it against the repo's own node_modules via runEnsureChecks, covering both the normal and already-current paths. squad doctor's hook check now includes pre-commit/post-commit (the hooks that guard and flush two-layer state) for two-layer/orphan backends, matching the full set install-hooks.ts installs, instead of only the four sync hooks. Part of bradygaster#1190 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
🟡 Impact Analysis — PR #1463Risk tier: 🟡 MEDIUM 📊 Summary
🎯 Risk Factors
📦 Modules Affectedroot (1 file)
squad-cli (3 files)
tests (2 files)
This report is generated automatically for every PR. See #733 for details. |
🛫 PR Readiness Check
PR Scope: 📦🔧 Mixed (product + infrastructure)
|
| Status | Check | Details |
|---|---|---|
| ✅ | Single commit | 1 commit — clean history |
| ✅ | Not in draft | Ready for review |
| ✅ | Branch up to date | Up to date with dev |
| ❌ | Copilot review | No Copilot review yet — it may still be processing |
| ✅ | Changeset present | Changeset file found |
| ✅ | Scope clean | No .squad/ or docs/proposals/ files |
| ✅ | No merge conflicts | No merge conflicts |
| ✅ | Copilot threads resolved | No Copilot review threads |
| ❌ | CI passing | 7 check(s) still running |
Files Changed (6 files, +273 −22)
| File | +/− |
|---|---|
.changeset/fix-1190-esm-patch-doctor-commit-hooks.md |
+5 −0 |
packages/squad-cli/scripts/patch-esm-imports.mjs |
+26 −19 |
packages/squad-cli/src/cli/commands/doctor.ts |
+3 −1 |
packages/squad-cli/src/cli/core/upgrade.ts |
+51 −0 |
test/cli/doctor.test.ts |
+26 −2 |
test/cli/patch-esm-imports.test.ts |
+162 −0 |
Total: +273 −22
This check runs automatically on every push. Fix any ❌ items and push again.
See CONTRIBUTING.md and PR Requirements for details.
🏗️ Architectural Review
Automated architectural review — informational only. |
tamirdresher
left a comment
There was a problem hiding this comment.
✅ Reviewed by Squad (Data). Two real bugs, two targeted fixes. The early-return → continue fix for the ESM patcher is obviously correct, and extending REQUIRED_SYNC_HOOKS to include pre-commit/post-commit closes the doctor gap for two-layer state. 6+ regression tests cover exact failure scenarios from #1190.
What
Findings 1 and 2 from #1190:
squad upgradenow applies the ESM import patch to the repo-localnode_modules, andsquad doctorfails when thepre-commit/post-commithooks required by the two-layer/orphan backends are missing.Why
Part of #1190 — finding 3 (teamRoot portability) lands separately; asked on the issue whether
peers.<machineId>.teamRootis the intended convention before building it.Two gaps left a repo subtly broken after upgrading:
postinstallwith cwd inside the installed package dir, so a globalnpm install -gonly ever patched the global copy of vscode-jsonrpc / copilot-sdk — the consumer repo'snode_modulesstayed unpatched andsquad doctorkept failing its squad-cli fails with ERR_MODULE_NOT_FOUND on Node 22 (vscode-jsonrpc ESM import) #449 checks while the patcher reported "already patched — skipping". Theprocess.cwd()entry added toSEARCH_ROOTSin 2d9f0b4 couldn't help: both patch functions returned at the first root that already had a patched copy, which on a global install is always the global one, so the cwd entry was effectively dead code.doctor's hook check only covered the four sync hooks (pre-push/post-merge/post-rewrite/post-checkout). A two-layer repo missingpre-commit/post-commit— the hooks that actually guard and flush state to thesquad-statebranch — passed the check while the state branch sat dormant, which is the exact post-squad upgrade --self --insider: misplaced templates, Rai not installed, --state-backend ignored #1185 condition described in the issue.How
scripts/patch-esm-imports.mjs: both patch functions now iterate every search root instead of returning at the first hit, log which path they patched or skipped, and are exported. A direct-invocation guard keepsnode scripts/patch-esm-imports.mjs(postinstall) behavior unchanged.core/upgrade.ts: newensureEsmImportsPatched(dest)locates the script by walking up from the compiled file (same approach asgetTemplatesDir()), imports it, and points it at<dest>/node_modules. Called fromrunEnsureChecks(), so it covers both the normal and already-current upgrade paths — rerunningsquad upgradeon an up-to-date repo still repairs the patch.commands/doctor.ts:REQUIRED_SYNC_HOOKSnow includespre-commit/post-commit, matching the full setinstall-hooks.tsinstalls andensureHooksForBackendverifies. The existing fail message already names each missing hook and points atsquad install-hooks.Testing
test/cli/patch-esm-imports.test.ts(6 tests): the all-roots regression for both layers (an already-patched earlier root no longer masks a later unpatched one), idempotency, andensureEsmImportsPatchedwiring against a temp project's node_modules.test/cli/doctor.test.ts: new regression test for the exact bug: upgrade pipeline gaps — postinstall misses repo-local node_modules, two-layer hooks not installed, teamRoot not portable #1190 state (four sync hooks present, commit hooks missing → fail, message contains both hook names and the remediation); the two existing PASS fixtures updated to install all six hooks.npm run build— passes.npm run lint(tsc) — passes.npm run lint:eslint— 0 errors (the new helper carries the same pre-existing n/no-sync warning class the rest ofcore/upgrade.tsalready has).npx vitest run test/cli/patch-esm-imports.test.ts test/cli/doctor.test.ts— 42 passed.npm teston this Windows box: 59 failed / 6962 passed with these changes vs 64 failed / 6950 passed on a cleanupstream/devcheckout using the same build+run procedure. The failing sets shift run to run and are the known local timing/locale flakes (5s timeouts in journey/e2e/shell tests, a unicode-vs-ascii rendering assertion); every test that failed only in the with-changes run passes standalone against the same build, and 50 of the failures are common to both runs..changeset/fix-1190-esm-patch-doctor-commit-hooks.md(patch bump,@bradygaster/squad-clionly — no SDK or governed template paths touched)PR Readiness Checklist
Branch & Commit
devdevgit diff --cached --stat, plus agit diff -wpass for whitespace-only hunks — none)Build & Test
npm run buildpassesnpm testpasses (see testing notes above for pre-existing unrelated local failures)npm run lintpasses (type check clean)npm run lint:eslintpasses (0 errors)Changeset
@bradygaster/squad-cli)Docs
Exports
ensureEsmImportsPatchedis exported fromcore/upgrade, which already has a subpath exportBreaking Changes
None.
Waivers
None.