Skip to content

bug: upgrade pipeline gaps — postinstall misses repo-local node_modules, two-layer hooks not installed, teamRoot not portable #1190

Open

Description

@tamirdresher

Summary

After upgrading to 0.9.6-insider.3 with stateBackend=two-layer, three related gaps in the upgrade pipeline leave a repo in a subtly broken state that squad doctor only partially catches.

Environment

  • squad-cli: 0.9.6-insider.3 (global), 0.9.1 (repo-local)
  • Windows 11, PowerShell, Node v23.5.0
  • stateBackend: "two-layer" in .squad/config.json

Finding 1 — postinstall ESM patch misses repo-local node_modules (references #449)

squad doctor reports two failures after a global install of 0.9.6-insider.3:

❌  vscode-jsonrpc exports field — missing exports field — run postinstall or reinstall (see #449)
❌  copilot-sdk session.js ESM patch — session.js has extensionless vscode-jsonrpc/node import

Running npm run postinstall from the global squad-cli package dir reports "already patched — skipping" yet doctor still fails. Root cause: patch-esm-imports.mjs uses hardcoded SEARCH_ROOTS relative to __dirname of the global install:

const SEARCH_ROOTS = [
  join(__dirname, '..', 'node_modules'),              // squad-cli local
  join(__dirname, '..', '..', '..', 'node_modules'),  // workspace root
  join(__dirname, '..', '..'),                         // global install (sibling)
];

None of these resolve to <repo>/node_modules. The repo-local node_modules (where doctor actually checks) is never patched.

Fix: Add join(process.cwd(), 'node_modules') to SEARCH_ROOTS. Additionally, squad upgrade should invoke the patch script from the repo root (not the global package dir) after install completes.


Finding 2 — pre-commit / post-commit hooks missing after two-layer upgrade (references #1185)

Because --state-backend migration was ignored during upgrade (#1185), the pre-commit and post-commit hooks required by two-layer were never installed. The squad-state orphan branch exists but has only its init commit — nothing has ever written to it.

Installed hooks after upgrade:

Hook Status
pre-push ✅
post-checkout ✅
post-merge ✅
post-rewrite ✅
pre-commit ❌ Missing
post-commit ❌ Missing

squad doctor does not flag this even though stateBackend=two-layer is configured. The state branch is silently dormant.

Fix: squad doctor should cross-check: if stateBackend=two-layer, verify pre-commit and post-commit hooks are present and warn/error if not.


Finding 3 — teamRoot absolute path not portable; squad upgrade / squad init should default to "." (references #1163)

squad upgrade writes teamRoot as an absolute path (e.g. C:\Users\...\tamresearch1). When the same repo is cloned to a different path or machine, squad doctor fails:

❌  team root resolves — directory not found

teamRoot: "." is a portable relative default. Machine-specific absolute paths should live in peers.<machineId>.teamRoot, not the top-level field.

Additionally, config.json had a duplicate stateBackend key — written once at the top and appended again at the end by the upgrade path. Non-breaking (JSON last-value wins) but indicates the upgrade config-write path appends rather than merges.

Fix:

  • squad init / squad upgrade should write teamRoot: "." by default.
  • Config write should merge/replace keys, not append.

Workaround (applied on our end)

  1. Manually patched node_modules/vscode-jsonrpc/package.json with exports field and @github/copilot-sdk/dist/session.js to use .js extension.
  2. Changed teamRoot to ".", updated peers.<machineId>.teamRoot to the correct absolute path.
  3. Removed duplicate stateBackend key.

After these manual fixes: squad doctor → 13 passed, 0 failed, 0 warnings ✅


Related

Activity

  1. omercangumus commented on Jul 10, 2026

    @omercangumus
    Contributor

    taking findings 1 and 2 — turns out the SEARCH_ROOTS cwd entry already landed in 2d9f0b4 but it was dead code, the patcher returned at the first already-patched root so repo-local node_modules never got reached. fixing that plus the missing pre-commit/post-commit doctor check, PR shortly. one thing before I touch finding 3 — is peers..teamRoot the convention you had in mind for the machine-specific absolute paths, or should it live somewhere else?

  2. bradygaster commented on Aug 23, 2026

    @bradygaster
    Collaborator

    Triage note — partially shipped, keeping open for the remainder.

    #1463 (fix(sdk): resolve casting registry findings 1 & 2 — display-name lookup + duplicate cast guard) closed findings 1 and 2. Its own description scopes out the rest:

    finding 3 (teamRoot portability) lands separately.

    So the open remainder here is finding 3 only — teamRoot portability. Retitling/rescoping this to just that would make it pickup-ready; as written the issue reads as fully outstanding, which is why it was nearly closed as done.

    Related open work on the same surface: #1629, #1163.

    Verified against origin/dev on 2026-08-23.

  3. added
    wave:1-nextWave 1 — immediate next sprint
    wave1:P2Wave 1 PR group P2
    and removed
    wave:2-soonWave 2 — next 2-4 weeks
    on Nov 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    triagedIssue has been triagedtype:bugSomething brokenwave1:P2Wave 1 PR group P2wave:1-nextWave 1 — immediate next sprint

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions