Skip to content

🤖 refactor: plugin discovery, journals and registry read through HostFiles and HostPaths - #7322

Merged
ThomasK33 merged 4 commits into
mainfrom
phase5/b-plugin-discovery
Oct 23, 2026
Merged

ThomasK33 merged 4 commits into
mainfrom
phase5/b-plugin-discovery

Conversation

@ThomasK33

Copy link
Copy Markdown
Member

Summary

5b PR 6 of phase 5 (Loop host I/O) of the agent-loop refactor. Plugin discovery, its journals and its registry now read host files through HostFiles and the user home through HostPaths (#7319). The three files (agentPlugins/discovery.ts, journals.ts, registry.ts) import no node:fs, node:fs/promises or node:os any more. Behavior does not change: each call goes to the Node layers, which read the node:fs/promises and node:os namespaces at call time, so the spies of the existing tests still apply. The frozen pairs of local/no-host-imports stay at 44 in 26 files.

Order

The plan order of stack 5b is PR 1 to PR 9. 5b PRs 2 to 5 wait for phase 4 and phase 2 PRs that have not merged. This PR waits only for 5a PR 5 (#7319, merged), and it uses no code of 5b PRs 2 to 5. So it lands now, with base main, as the bottom of the new stack 5b.

Implementation

  • hostFiles/hostFiles.ts: HostFiles.promises also gives open and constants. readPluginFileWithinRootCapped reads a plugin file through one handle opened with O_NONBLOCK, so a FIFO cannot hang it and the bytes come from the inode it checked. Both layers already have the two members (the Node layer is the namespace itself, the memory layer is the memfs promises).
  • hostFiles.contract.testHarness.ts: one new case that both layers pass. open with O_RDONLY | O_NONBLOCK reads through the handle, its bigint stat names the inode of lstat, and open of a missing file fails with ENOENT.
  • discovery.ts: nodePluginFs and readPluginFileWithinRootCapped use nodeHostFiles.promises, and computeAgentPluginContainers takes ~/.agents/plugins from nodeHostPaths.userHome().
  • journals.ts, registry.ts: nodeHostFiles.promises in place of the node:fs/promises import.

The plan estimated about 450 lines for this PR. It adds 64 lines and removes 28, because no caller passes its own HostFiles or HostPaths yet, so this PR adds no parameter for one. A whole discovery run on a memory host also needs ensurePathContained (tools/skillFileUtils.ts) on HostFiles. That file belongs to 5a PR 6, and the services that would pass a memory host belong to 5b PR 8.

Validation

  • The plan's "no host import" check, an import scan with the module list of local/no-host-imports, run on the three files: before, discovery.ts had fs/promises, fs and os, and journals.ts and registry.ts had fs/promises. After, the three files have no pair.
  • bun test ./src/node/services/agentPlugins/: 826 pass, 0 fail.
  • bun test ./src/node/services/hostFiles/: 30 pass (the new case on both layers).
  • make static-check, make lint-eslint.
  • make test (host suite lock): 27683 pass, 7 skip, 3 fail. The fails were timeouts under the suite's load, and each file passes alone: backup/gitRepo.test.ts (2 BackupRepoCache cases) passes 61 of 61, and bash_output.test.ts (a known flake) passes 14 of 14. Neither file imports the agentPlugins or hostFiles code.
  • The ui line (scripts/ tests and the others of CI's line): 725 pass, 1 fail. The fail was a timeout in scripts/perf/desktopColdStart.test.ts while another test process ran on the host. Alone it passes 2 of 2.
  • Loop-test tripwire, 3 runs against origin/main 9c1eb89: 0 trips.

Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: high • Cost: $71.07

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-23T09:25:01.667028Z def97b7 Manual request
🔒 Security Review ✅ Completed 2026-10-23T09:25:35.405783Z def97b7 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

Reviewed commit: def97b739f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: def97b739f

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@github-actions

Copy link
Copy Markdown
Contributor

🟢 e2e stories (Haiku): 14 passed

27 agent steps · 7 replayed from cache · 31 model calls · 218.1k tokens (70% cached)
Spend: $0.10 of the $1.00 cap (proxy ledger)

All 14 tests in 9 files
Test Agent Time
🟢 chatRestart.e2e.ts · 1 passed 2 steps · 4 calls 23.9s
🟢 a chat survives a restart of the server 2 steps · 4 calls 23.9s
🟢 compaction.e2e.ts · 1 passed 3 steps · 7 calls 42.4s
🟢 /compact replaces the old chat with a summary 3 steps · 7 calls 42.4s
🟢 pluginTrustRestart.e2e.ts · 1 passed 4 steps · 2 calls 9.5s
🟢 a trusted plugin hook stays trusted after the server restarts 4 steps · 2 calls 9.5s
🟢 settingsRoundTrip.e2e.ts · 1 passed 3 steps · 5 calls 29.4s
🟢 a chat survives trips through Settings 3 steps · 5 calls 29.4s
🟢 sidebarPerWorkspace.e2e.ts · 1 passed 4 steps · 1 call 4.2s
🟢 a new workspace starts with its right sidebar collapsed while another keeps it open 4 steps · 1 call 4.2s
🟢 smoke.e2e.ts · 1 passed 2 steps · 1 call 2.7s
🟢 the agent opens the playground workspace 2 steps · 1 call 2.7s
🟢 streamErrors.e2e.ts · 1 passed 1 step · 1 call 7.0s
🟢 stream errors show in the chat, and the chat recovers 1 step · 1 call 7.0s
🟢 todoNudge.e2e.ts · 1 passed 2 steps · 4 calls 24.5s
🟢 the agent finishes open TODO items after the turn-end nudge 2 steps · 4 calls 24.5s
🟢 touchAlignment.e2e.ts · 6 passed 6 steps · 6 calls 27.5s
🟢 the Settings close button is level with the title on a touch screen 390px wide 1 step · 1 call 4.0s
🟢 the Settings close button is level with the title on a touch screen 768px wide 1 step · 1 call 5.4s
🟢 Backup settings checkboxes are small and level with their labels on a touch phone 1 step · 1 call 4.4s
🟢 the heartbeat checkbox is small and level with its label on a touch phone 1 step · 1 call 3.3s
🟢 a reply's model and time stay on one line on a touch phone 1 step · 1 call 3.8s
🟢 the workspace actions menu stays on the title row on a touch phone 1 step · 1 call 6.5s

e2e 0.20.0 · 2m 54s · web · run artifacts

🟢 e2e Electron stories (Haiku): 3 passed

3 agent steps · 3 model calls · 8.0k tokens (21% cached)
Spend: $0.02 of the $1.00 cap (proxy ledger)

All 3 tests in 3 files
Test Agent Time
🟢 electron/deepLink.e2e.ts · 1 passed 1 step · 1 call 5.5s
🟢 a deep link of a second launch starts a chat in the running app 1 step · 1 call 5.5s
🟢 electron/menuBar.e2e.ts · 1 passed 1 step · 1 call 6.0s
🟢 the workspace menu bar is the desktop title bar 1 step · 1 call 6.0s
🟢 electron/smoke.e2e.ts · 1 passed 1 step · 1 call 5.1s
🟢 Xum Electron opens the playground workspace 1 step · 1 call 5.1s

e2e 0.20.0 · 18.7s · electron · run artifacts

🟢 e2e agent repros (Haiku): no tests selected

Spend: $0.00 of the $1.00 cap (proxy ledger)

e2e 0.20.0 · 5.3s · 2 targets (web, phone) · run artifacts

@ThomasK33
ThomasK33 added this pull request to the merge queue Oct 23, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 23, 2026
@ThomasK33
ThomasK33 added this pull request to the merge queue Oct 23, 2026
Merged via the queue into main with commit 7df88f0 Oct 23, 2026
37 of 38 checks passed
@ThomasK33
ThomasK33 deleted the phase5/b-plugin-discovery branch October 23, 2026 10:19
azaj01 pushed a commit to azaj01/mux that referenced this pull request Oct 25, 2026
…through HostFiles, HostPaths and HostClock (coder#7371)

## Summary

5b PR 7 of phase 5 (Loop host I/O) of the agent-loop refactor. The
instruction readers and the home-clutter reminder reach the host through
the phase 5 and phase 3 services:

- `instructionFiles.ts` and `homeClutterWatch.ts` read files through
`HostFiles` (coder#7319).
- `systemMessage.ts` and `tools.ts` take the user home and the Xum home
from `HostPaths` (coder#7319).
- `HomeClutterReminderSource` arms its scan timeout on the session's
`HostClock` (phase 3).

The five files import no `fs`, `fs/promises` or `os` any more, and
`systemMessage.ts` no longer imports `getXumHome`. Behavior does not
change: the Node layers read the same `fs/promises` and `os` namespaces
and call `getXumHome` at each call, so the `os.homedir` spies of
`systemMessage.test.ts` and `instructionsService.test.ts` still apply.
None of the five files is in the `local/no-host-imports` file set, so
the frozen pairs do not change.

## Order

Plan order: stack 5b is PR 1 to PR 9, and this PR comes after 5b PR 6
(coder#7322, merged).

Landing order: this PR lands ahead of 5b PRs 2 to 5, by rule 2 of plan
note 1. Those PRs wait for phase 4 and phase 2 PRs that have not merged.
This PR waits only for 5a PR 5 (coder#7319, merged) and phase 3 PR 1 (coder#7270,
merged), and it uses no code of 5b PRs 2 to 5. So it opens on its own,
with base main. No open phase 2 to 5 PR changes these five files.

## Implementation

- `utils/main/instructionFiles.ts`: the local file reader calls
`nodeHostFiles.promises.readFile`.
- `runtime/homeClutterWatch.ts`: the default `readDir` calls
`nodeHostFiles.promises.readdir`.
- `services/systemMessage.ts`: the Claude-compat global dir comes from
`nodeHostPaths.userHome()`. The global instruction set, the settings
`config.json` path and its directory come from
`nodeHostPaths.xumHome()`.
- `agentNotifications/sources/HomeClutterReminderSource.ts`: the
constructor takes an optional `clock` (absent: `nodeClock`, as for tool
configs that tests build by hand). The scan timeout calls
`clock.setTimer`, and the scan start times come from `clock.nowMs()`.
- `common/utils/tools/tools.ts`: the reminder gets
`nodeHostPaths.userHome()` and `config.clock`.

The plan estimated about 320 lines for this PR. It adds 71 lines and
removes 21, because no caller passes its own `HostFiles` or `HostPaths`
yet (the same reason as in 5b PR 6).

## Validation

- `HomeClutterReminderSource.test.ts`: a new case runs a poll on a stuck
scan with the TestClock. The poll is still open at 249 ms of TestClock
time and ends at 250 ms, with no real wait. With the timer pointed back
at `nodeClock`, the case fails.
- The plan's tests (`instructionFiles`, `systemMessage`,
`instructionsService`, `homeClutterWatch`, `HomeClutterReminderSource`)
plus `tools.test.ts`: 135 pass, 0 fail.
- `make static-check`, `make lint-eslint`, `make dry-check`, `make
crap-check`.
- `make test` (host suite lock) on head c9af576: `bun test src` had
27679 pass, 7 skip and 1 fail. The fail is `renderer first load > the
entry's static imports do not reach the tool definitions module`, which
fails the same way on main 2e2a2b7 (I ran it there alone). `make
test` stops at the first failing run, so I ran its other steps by hand:
the shared scripts/storybook/vscode run (729 pass, 0 fail) and each of
the 18 isolated files (0 fail).
- The ui line on that head: 732 pass, 0 fail.
- Loop-test tripwire, 3 runs against origin/main 2e2a2b7: 0 trips.
- Round 2 (77a0664) merges main 2633118 (coder#7369, the fix for the
renderer first load test). On it: `make static-check`, and the plan's
tests plus `tools.test.ts` and `rendererFirstLoad.test.ts`: 138 pass, 0
fail.

---

_Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking:
`high` • Cost: `$112.49`_

<!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high
costs=112.49 -->
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.

1 participant