Skip to content

Seed registry-owned SystemIO service - #12342

Draft
franknoirot wants to merge 10 commits into
mainfrom
codex/systemio-operation-core
Draft

Seed registry-owned SystemIO service#12342
franknoirot wants to merge 10 commits into
mainfrom
codex/systemio-operation-core

Conversation

@franknoirot

@franknoirot franknoirot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Summary

This rebases the original SystemIO operation-core seed onto current main after #12551 and turns it into the first registry-owned SystemIO service step:

  • moves the SystemIO service contract and implementation under src/lib/systemIO, with src/registry/contracts/systemIO.ts left as a compatibility re-export
  • adds a typed systemIOService.request(...) API, currently starting with projects.refresh
  • adds a dependency-injected operation queue with per-resource FIFO execution, duplicate coalescing, cancellation, and operation status signals
  • wires a core system-io extension that provides systemIOService and reads project directories through the merged project-library scanner plus the registry-provided WASM bundle
  • makes the SystemIO service own the legacy systemIOMachineImpl actor lifecycle, with App.systemIOActor kept as a compatibility getter instead of an App-owned actor instance
  • keeps service projects signals and legacy actor folders context synchronized while callsites migrate
  • migrates Home's default local project entries and create/rename/delete/cloud-sync refreshes from systemIOService.actor to the new service request/signal API
  • removes the temporary App.buildSingletons() bridge that provided the old systemIOActor as systemIOService

Boundary

This is still intentionally a first slice. Legacy actor callsites remain for route loaders, command flows, Zookeeper/file replay, file mutations, watchers, and the remaining XState machine operations. The important ownership change is that App no longer constructs or stops that actor; the registry-owned SystemIO service does. The next steps should move one actor-invoked operation at a time behind this service instead of folding every route/session/UI behavior into one oversized PR.

Validation

  • npm run test:unit -- src/lib/systemIO/operationQueue.test.ts src/lib/systemIO/service.test.ts src/lib/homeProjects.test.ts
  • npm run test:integration -- src/registry/contracts/homeProjects.spec.ts src/registry/contracts/projectLibraries.spec.ts src/lib/app.spec.ts
  • npm run tsc -- --pretty false
  • npm run lint

@vercel

vercel Bot commented Jul 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
modeling-app Ready Ready Preview, Comment Jul 28, 2026 3:02pm

Request Review

The pure-systemIO operation-core refactor left the legacy XState actor no
longer driven into readingFolders, breaking the service<->actor bridge in
both directions and dropping project-list updates.

- systemIOMachine: handle setFolders while idle, where the service now
  permanently sits and pushes refresh results as a legacy bridge.
- service: skip actor snapshots with undefined folders so an unrelated
  idle snapshot can't clobber the freshly-loaded project list; keep the
  stopped actor reference on dispose so late send/subscribe calls are
  absorbed instead of crashing app.systemIOActor consumers.
- operationQueue: only coalesce into a still-queued operation, so a
  refresh triggered after a filesystem mutation re-reads instead of
  reusing an in-flight scan that predates the change.
- homeProjects: clear lastRefreshKey when a refresh fails so a transient
  error no longer permanently blocks re-requests for that directory.

Adds regression tests for each fix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
franknoirot and others added 4 commits July 24, 2026 16:56
The `latestRefreshId` guard reads as a per-directory bug at first glance,
but it is deliberate: `projects`/`projectHandles` model a single current
project directory, and the most recently started refresh must win so a
stale in-flight refresh (e.g. for a directory the user navigated away
from) cannot clobber newer state. Same-directory refreshes are already
serialized by the operation queue, so the guard only arbitrates across
directories. Comment this so it is not mistaken for a concurrency defect.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…jections

Callers that only watch the status signals never read `operation.result`,
so a failed or cancelled operation's rejection could escape as an
unhandled promise rejection. Attach an internal no-op catch to the
queue-owned result promise; observers that await `.result` still receive
the rejection through their own handler.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Previously, folding a request into a still-queued operation returned the
existing operation and silently dropped the new caller's handler and
request payload, so the work enqueued first always won. Track the
request/handler mutably and adopt the newest intent when coalescing, so a
still-queued operation runs the freshest handler/request when it starts.
Behavior is unchanged for identical duplicate refreshes (the current only
caller), and running operations are still never coalesced into.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Operation snapshots were retained forever, so a long-lived session that
refreshes on every mutation/watcher event grew the `operations` array
without bound. Add a writable `recordLimit` signal (default 200) that
evicts oldest settled records once the count exceeds it, while never
dropping in-flight operations. Expose it on the service as
`operationRecordLimit` so a debug/inspection UI or a developer in a pinch
can raise it — set it to `Infinity` to keep every record.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ion-core

# Conflicts:
#	src/registry/extensions/homeProjects/index.ts
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