Skip to content

refactor: break import cycles; module-boundary law + design doc (isolation 1/7)#116

Merged
Exelord merged 1 commit into
mainfrom
claude/module-isolation-1
Jun 10, 2026
Merged

refactor: break import cycles; module-boundary law + design doc (isolation 1/7)#116
Exelord merged 1 commit into
mainfrom
claude/module-isolation-1

Conversation

@Exelord

@Exelord Exelord commented Jun 10, 2026

Copy link
Copy Markdown
Member

First PR of the module-isolation series — full design + audit in docs/design/module-isolation-2026-06.md (distilled from an exhaustive import-graph audit; adopts PR #108's modules-first principles in place, no rebuild).

Changes

  • src/version.ts extractedorchestrator.ts/cli.ts imported VERSION from index.ts while index.ts imported run() back from the orchestrator. The cycle survived only via ESM hoisted-const live bindings.
  • orchestrator/options.tsRunOptions/RunSummary declarations move out of the module entry so prepare.ts no longer imports upward (type-only cycle).
  • tests/module-boundaries.test.ts — the dependency matrix is now law under the normal test gate. Notably it needed zero seeded exceptions: the layering already held everywhere except the two cycles fixed here. Second rule (contracted modules importable only via their index.ts) is a ratchet that grows as the contract PRs land.
  • oxlint import/no-cycle enabled — verified it actually fires on a deliberate cycle first.

Zero behavior change; public API byte-identical. Remaining series: relocations (nested-dirs/fingerprint→workspace, plan-format→cli, ProjectEntry→workspace), task-hash split, per-module contracts, entry moves, docs refresh.

Test plan

  • Full CI gate green (3/3 tasks; 520+ tests incl. the new boundary tests)

🤖 Generated with Claude Code

First PR of the module-isolation series (see
docs/design/module-isolation-2026-06.md, distilled from a full
import-graph audit):

- src/version.ts extracted — orchestrator.ts and cli.ts imported
  VERSION from index.ts while index.ts imported run() back from the
  orchestrator. The cycle only worked because a hoisted const
  survives ESM live-binding order.
- RunOptions/RunSummary declarations moved to orchestrator/options.ts
  so prepare.ts no longer imports upward from its own module entry.
- tests/module-boundaries.test.ts — the dependency matrix is now law,
  enforced by the standard test gate. Both rules active: matrix
  membership (zero exceptions needed — the layering already held
  everywhere except the two cycles) and contracted-module index-only
  imports (ratchet; grows as contract PRs land).
- oxlint import/no-cycle enabled as a secondary check (verified it
  fires on a deliberate cycle before trusting it).

Zero behavior change; public API byte-identical.
@Exelord Exelord merged commit 17ad3f2 into main Jun 10, 2026
1 check passed
@Exelord Exelord deleted the claude/module-isolation-1 branch June 10, 2026 18:41
Exelord added a commit that referenced this pull request Jun 10, 2026
A SIGTERM to the vx process alone (CI cancellation, kill <pid>) left
running children orphaned — including persistent dev servers — with
no orderly shutdown. Terminal Ctrl-C only worked via process-group
propagation.

run() now keeps a run-scoped liveChildren registry maintained by the
spawn sites (runCommand, runPersistent, runSandboxed); SIGINT/SIGTERM
handlers installed for exactly the run's duration forward SIGTERM to
every live child, close the cache, and exit 128+signo. Handlers are
removed in a finally so repeated runs never stack listeners.

New RunOptions.handleSignals (default true): the watch loop passes
false — it owns signal disposition for its lifetime, and an
in-flight cycle must never exit the process out from under it (the
naive version killed the test suite via watch's in-process SIGINT
simulation).

Known limit (documented): only direct children are signalled — a
double-forking task can still orphan grandchildren.

Squash-merge of claude/run-signal-handling rebased onto the #115/#116
state: the branch's own signalExitCode duplicate was dropped in favor
of #115's os.constants-based helper, and handleSignals lives in
orchestrator/options.ts (its post-#116 home).
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