You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
tooling(slm-frontend): the shared SLM node_modules was emptied and now holds a link to itself — every worktree's SLM vue-tsc and vitest are dead #18406
Related: #16912 (worktrees symlink the main checkout's node_modules), #17573 (an earlier break of the same shared install: mode 644, not traversable).
Observed 2026-10-12 (read-only)
The main checkout's autobot-slm-frontend/node_modules has 1 entry and no .bin, so vue-tsc and vitest are missing. The directory inode is 12288 bytes, so it previously held many entries.
That single entry is node_modules -> <main checkout>/autobot-slm-frontend/node_modules, a link to its own parent, created 11:23:53. The directory mtime is the same second.
autobot-frontend/node_modules is intact (721 entries, 85 bins).
The self-link:ln -s <main>/…/node_modules <dir>/node_modules creates exactly this link when <dir> resolves to the main checkout instead of a worktree, because ln places the link inside an existing directory. So the worktree link step most likely ran with the main checkout as its target.
The emptying: NOT determined. Three agents of the session that reported this say they ran no package-manager command today; the other sessions have not been asked.
Acceptance criteria
The worktree link step refuses to run when its target resolves to the main checkout, and refuses to create a link inside an existing real directory. There is a test for both.
Nothing a worktree does through the link can delete the shared install. Either package-manager commands refuse to run when node_modules is a link, or the hook detects it and stops with instructions. There is a test for this.
Pre-push reports "SLM frontend deps missing" as its own failure, not as a vue-tsc timeout.
The cause of the emptying is found or explicitly recorded as unknown, with the evidence that was checked.
Who ran package-manager commands: none, by their own account. The master session's agents (5), the helper session and the frontend session all report no npm ci, npm install or package-manager run today. The only npx use was npx --no-install openapi-typescript at about 10:42 and 10:55, which only executes a binary.
Worktree links near 11:23: none. The SLM node_modules link timestamps of every live worktree are all ≤ 11:14:40, so no worktree was created or re-linked at 11:23:53.
Nearest event: at 11:22:08, git checkout -b ran in a frontend worktree, so its post-checkout hook ran. A subagent working in that worktree from 11:22 to 11:27 already found the SLM vitest and vue-tsc missing. So the emptying may PREDATE the 11:23:53 link.
The current post-checkout link step (section 7) cannot create this link as written:
it skips when the main root equals the target root;
it links only when the target ! -e, so it never creates a link inside an existing directory;
with GIT_DIR set, GIT_ROOT resolves to .git/worktrees, and [ -d $GIT_ROOT/$_proj ] fails.
So the self-link came from something other than this hook, or from an older copy of it in some worktree's .git/hooks. Not determined.
Earlier main-checkout event: on 2026-10-11 at 23:47:58, a checkout ran in the main checkout by mistake and was restored at 23:48:13.
Status: the cause of both the emptying and the link is unknown. The owner is restoring the install by hand. The ACs stand, especially the one that makes the hook refuse a target inside an existing real directory, and the one that detects an emptied shared install.
A second effect of this, in the pre-push hook: it misclassifies the dead install as a test failure, so AUTOBOT_PREPUSH_ALLOW_UNRUNNABLE=1 does not apply and the push is blocked outright.
Observed on a merge-train push, verbatim:
[pre-push WARN] vue-tsc (autobot-slm-frontend) COULD NOT RUN on this interpreter — allowed by AUTOBOT_PREPUSH_ALLOW_UNRUNNABLE=1
[pre-push FAIL] vitest failures (autobot-slm-frontend, #5142):
vitest.config.ts (8:16) [UNRESOLVED_IMPORT] Could not resolve '@vitejs/plugin-vue' in vitest.config.ts
failed to load config from …/autobot-slm-frontend/vitest.config.ts
⎯⎯⎯⎯⎯⎯⎯ Startup Error ⎯⎯⎯⎯⎯⎯⎯⎯
Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'vitest' imported from …/autobot-slm-frontend/node_modules/.vite-temp/vitest.config.ts.timestamp-…mjs
[pre-push FAIL] Push BLOCKED.
The same run classified vue-tsc as COULD NOT RUN, but not vitest. A vitest run that dies with failed to load config / Startup Error / ERR_MODULE_NOT_FOUND before collecting a single test ran nothing, and should be classified COULD NOT RUN like vue-tsc, not as a failure.
The shared SLM node_modules currently holds only .vite-temp (no vitest).
Acceptance criterion to add:
A vitest startup error with zero tests collected is classified as COULD NOT RUN, with a contrast test.
A real defect is already slipping through. Because SLM vue-tsc can't run locally, no pre-push catches SLM type errors. Review just found one: refactor(forks): resolve the slm batch of symbol-fork clusters, 16 of 20 names (#18449) #18469 renames generated schemas that autobot-slm-frontend/src/types/slm.ts:698,720 index by key. Local tooling should have caught that before review.
What unblocks it: either option works.
Restore the SLM frontend install, which agents may not do themselves (no installs into the checkout).
Approve fixing the hook so that a vitest startup error with zero tests collected is classified COULD NOT RUN, like vue-tsc already is. (That is this issue's added AC.)
Removed the self-link autobot-slm-frontend/node_modules/node_modules (→ itself, created 11:23:53).
Ran npm ci --no-audit --no-fund in the main checkout: added 543 packages, rc=0.
node_modules/.bin/vue-tsc and .bin/vitest are present. npm run type-check rc=0, with 0 error TS.
Root cause: still not established. I read the post-checkout link step (section 7). By its guards ([ -L dst ] && [ ! -e dst ] then rm, and [ ! -e dst ] then ln -s src dst), it should never write into an existing directory.
Two hypotheses fit the evidence, and neither is verified:
An install run inside a worktree.npm ci or npm install in a worktree, whose node_modules is a symlink to the main checkout's, would wipe the shared install through the link. That would explain the emptied directory.
Two paths for one directory. If GIT_ROOT and MAIN_ROOT resolve to the same directory under different strings, _dst and _src are one directory. A run that happens while it's briefly missing could then link it to itself. That would explain the self-link.
Suggested hardening:
In the hook: skip when realpath "$_dst" equals realpath "$_src".
For worktree installs: a guard that refuses npm ci or npm install when node_modules is a symlink, for example a preinstall check in both apps' package.json.
npm ci's cleanup ran through the symlink, and the shared install in the main checkout was emptied.
Timing fits:
SLM vitest ran fine in another worktree at ~08:37 Riga;
the main install was found already empty at 11:23:22 (the S0 subagent's ls …/AutoBot-AI/autobot-slm-frontend/node_modules/.bin/vitest: No such file);
no other package-manager command appears in any transcript in that window.
Not directly proven: that npm ci deleted the target's contents through the link, rather than only the link. Everything else is recorded.
The self-link, 11:23:50 Riga.
The frontend session's S0 subagent ran ln -s $R/autobot-slm-frontend/node_modules autobot-slm-frontend/node_modules inside refined-ui-shell. There the destination already was a symlink to that directory, so ln created node_modules/node_modules inside the target.
That was mine. It was harmless to the contents, but it is the self-link that was found.
Fix, so it can't recur:
Guard in both apps' package.json: a preinstall check that refuses npm ci or npm install when node_modules is a symlink, with the message: "run make frontend-setup in the main checkout".
Hook: the post-checkout link step skips when realpath of the destination equals the source.
Agent rule: never install in a worktree. The standard procedure is make frontend-setup from the main checkout (owner, 2026-10-12).
Activity
mrveiss commentedon Oct 12, 2026
What was checked (2026-10-12):
Who ran package-manager commands: none, by their own account. The master session's agents (5), the helper session and the frontend session all report no
npm ci,npm installor package-manager run today. The only npx use wasnpx --no-install openapi-typescriptat about 10:42 and 10:55, which only executes a binary.Worktree links near 11:23: none. The SLM
node_moduleslink timestamps of every live worktree are all ≤ 11:14:40, so no worktree was created or re-linked at 11:23:53.Nearest event: at 11:22:08,
git checkout -bran in a frontend worktree, so its post-checkout hook ran. A subagent working in that worktree from 11:22 to 11:27 already found the SLMvitestandvue-tscmissing. So the emptying may PREDATE the 11:23:53 link.The current post-checkout link step (section 7) cannot create this link as written:
! -e, so it never creates a link inside an existing directory;GIT_DIRset,GIT_ROOTresolves to.git/worktrees, and[ -d $GIT_ROOT/$_proj ]fails.So the self-link came from something other than this hook, or from an older copy of it in some worktree's
.git/hooks. Not determined.Earlier main-checkout event: on 2026-10-11 at 23:47:58, a checkout ran in the main checkout by mistake and was restored at 23:48:13.
Status: the cause of both the emptying and the link is unknown. The owner is restoring the install by hand. The ACs stand, especially the one that makes the hook refuse a target inside an existing real directory, and the one that detects an emptied shared install.
mrveiss commentedon Oct 12, 2026
A second effect of this, in the pre-push hook: it misclassifies the dead install as a test failure, so
AUTOBOT_PREPUSH_ALLOW_UNRUNNABLE=1does not apply and the push is blocked outright.Observed on a merge-train push, verbatim:
The same run classified
vue-tscas COULD NOT RUN, but not vitest. A vitest run that dies withfailed to load config/Startup Error/ERR_MODULE_NOT_FOUNDbefore collecting a single test ran nothing, and should be classified COULD NOT RUN like vue-tsc, not as a failure.The shared SLM
node_modulescurrently holds only.vite-temp(novitest).Acceptance criterion to add:
mrveiss commentedon Oct 12, 2026
Owner: please prioritise this. It is now the single blocker on the merge pipeline.
Current state:
autobot-slm-frontend/node_modulesstill holds 1 entry, with novitestand novue-tsc.What it is blocking:
viteststartup error as test failures rather than COULD NOT RUN (see my earlier comment here), so the sanctionedAUTOBOT_PREPUSH_ALLOW_UNRUNNABLEflag doesn't apply. It is built and green locally and waiting only on this. It carries refactor(audit): audit_logs is the one system of record, Redis its projection — three stores retired, one write path, one retention table (#17225) #18343, fix(sandbox): execution and pooled containers run fully hardened, and no caller can widen their reach (#17267) #18232, refactor(infra): classify 43 more infra scripts by runtime, repair 3 validators (#18289) #18380, fix(codebase-analytics): honest scan outcomes, duplication detectors wired, and forks shown from the one CI detector (#18013, #18067, #17982) #18362, security(api): RUM split into admin, user and bounded-open routes, and Prometheus metrics behind an SLM-issued scrape token (#16375) #18372, fix(guards): mypy resolves autobot_shared so enum aliases stay types; the embedding-literal guard covers namespaced ids and the infra builders (#18345) #18393, fix(slm-auth): SSO callbacks redirect with a one-time code, not the session JWT (#18394) #18399, feat(llm): needs_reauth registry skip + typed error, header headroom harvest, pre-dispatch headroom routing (#15022, #15026, #15028) #18403, refactor(api): the main app's raw fetch tail goes through fetchWithAuth, and ApiClient gains an opt-in circuit breaker for polled dashboards (#12363) #18398 and feat(guard): a kit component cannot be forked in either app, and kit stories render in both Storybooks (#14776) #18409.vue-tsccan't run locally, no pre-push catches SLM type errors. Review just found one: refactor(forks): resolve the slm batch of symbol-fork clusters, 16 of 20 names (#18449) #18469 renames generated schemas thatautobot-slm-frontend/src/types/slm.ts:698,720index by key. Local tooling should have caught that before review.What unblocks it: either option works.
vue-tscalready is. (That is this issue's added AC.)fix(hooks): a FAIL line counts as having run, so a crash after a fail…
Merge pull request #18474 from mrveiss/issue-18406-vitest-startup-cou…
mrveiss commentedon Oct 12, 2026
Restored on 2026-10-12 at the owner's request.
autobot-slm-frontend/node_modules/node_modules(→ itself, created 11:23:53).npm ci --no-audit --no-fundin the main checkout:added 543 packages, rc=0.node_modules/.bin/vue-tscand.bin/vitestare present.npm run type-checkrc=0, with 0error TS.Root cause: still not established. I read the post-checkout link step (section 7). By its guards (
[ -L dst ] && [ ! -e dst ]thenrm, and[ ! -e dst ]thenln -s src dst), it should never write into an existing directory.Two hypotheses fit the evidence, and neither is verified:
npm ciornpm installin a worktree, whosenode_modulesis a symlink to the main checkout's, would wipe the shared install through the link. That would explain the emptied directory.GIT_ROOTandMAIN_ROOTresolve to the same directory under different strings,_dstand_srcare one directory. A run that happens while it's briefly missing could then link it to itself. That would explain the self-link.Suggested hardening:
realpath "$_dst"equalsrealpath "$_src".npm ciornpm installwhennode_modulesis a symlink, for example apreinstallcheck in both apps'package.json.mrveiss commentedon Oct 12, 2026
Cause found from the session transcripts, 2026-10-12.
wt-18394, the security(sso): SSO callbacks redirect with the session JWT in the query string — now live after #18322 #18394 subagent ran this inautobot-slm-frontend:npm ci --no-audit --no-fund && npx --no-install openapi-typescript openapi.json -o src/types/generated/api.ts --additional-properties.node_moduleswas the tech-debt(frontend): a frontend change in a fresh worktree has no local verification at all — not vitest, not vue-tsc #16912 symlink to the main checkout's install. npm printednpm warn reify Removing non-directory …/wt-18394/autobot-slm-frontend/node_modules, thenadded 543 packagesinto the worktree.npm ci's cleanup ran through the symlink, and the shared install in the main checkout was emptied.ls …/AutoBot-AI/autobot-slm-frontend/node_modules/.bin/vitest: No such file);npm cideleted the target's contents through the link, rather than only the link. Everything else is recorded.ln -s $R/autobot-slm-frontend/node_modules autobot-slm-frontend/node_modulesinsiderefined-ui-shell. There the destination already was a symlink to that directory, solncreatednode_modules/node_modulesinside the target.Fix, so it can't recur:
package.json: apreinstallcheck that refusesnpm ciornpm installwhennode_modulesis a symlink, with the message: "runmake frontend-setupin the main checkout".realpathof the destination equals the source.make frontend-setupfrom the main checkout (owner, 2026-10-12).