Skip to content

refactor(accounts): split account-service.ts into 12 focused modules (N2)#30

Merged
NagyVikt merged 2 commits into
mainfrom
agent/n2-account-service-split
May 17, 2026
Merged

refactor(accounts): split account-service.ts into 12 focused modules (N2)#30
NagyVikt merged 2 commits into
mainfrom
agent/n2-account-service-split

Conversation

@NagyVikt
Copy link
Copy Markdown
Collaborator

Implements Theme N2 from the 0–4 weeks Now horizon. Splits the 1,675-LOC src/lib/accounts/account-service.ts god-file into 12 focused modules per docs/future/01-ARCHITECTURE.md §2.1. Branches off the wave-1 head (N1 #29, N3 #28, N4 #27 already merged).

Exit criteria (verbatim from 17-ROADMAP.md)

  • At least eight of the ten target modules listed in 01-ARCHITECTURE.md §2.1 exist. All 12 land (table has 12 rows; floor was 8).
  • account-service.ts is below 400 LOC. 1,675 → 164 LOC (close to the ~150 orchestrator goal).
  • No public method signature on AccountService changes; the singleton in src/lib/accounts/index.ts still works. All 21 public methods preserved; verified by node -e "..." probe and by BaseCommand.accounts continuing to compile.
  • Each extracted module has at least one unit test. 31 new test cases across 3 files cover every cluster.
  • CI passes on Linux and macOS (Windows matrix included).

Extracted modules (post-split LOC)

Module LOC Cluster
sync/external-sync.ts 216 external-auth sync orchestrator
read/listing.ts 211 listings + getCurrentAccountName + find
write/save.ts 159 saveAccount + safety guard + name inference
write/use.ts 128 useAccount + activateSnapshot + resolveUsableAccountName
write/remove.ts 105 remove one / by-query / all
config/auto-switch-config.ts 82 status + threshold setters + api-usage toggle
auto-switch/policy.ts 141 runAutoSwitchOnce + runDaemon + candidate scoring
usage/adapter.ts 192 refreshAccountUsage + refreshListUsageIfNeeded + proxy shim
safety/snapshot-vault.ts 125 backup vault + clobber recovery
session/pin.ts 243 sessions.json I/O + Linux PPID heuristic
identity/equality.ts 86 snapshot identity comparisons (pure, no I/O)
naming.ts 40 normalizeAccountName + accountFilePath + percent guard

Shared helpers under _internal/ (not part of the public surface):

  • _internal/fs-helpers.ts (64) — pathExists / filesMatch / auth-state read
  • _internal/auth-state.ts (70) — symlink materialize + current-name file I/O
  • _internal/registry-ops.ts (50) — persistRegistry + metadata hydration
  • _internal/name-resolution.ts (196) — login/default/inferred name resolution

Orchestrator account-service.ts: 164 LOC. Every public method delegates to a free function in one of the cluster modules.

Behavior guarantees

  • Byte-identical behavior. All 98 pre-N2 tests still pass. 31 new tests added — 129 total, 0 failing.
  • N1 contract honored. Every write still routes through persistRegistryAtomic / secureWriteFile.
  • N3 contract honored. Errors still throw AuthmuxError subclasses with the same codes/severity/hints.
  • N4 contract honored. Path resolution stays per-call via resolveCodexDir() / resolveAccountsDir() / etc; no module-import-time path resolution introduced.
  • Public consumers unchanged. commands/check.ts, commands/forecast.ts, commands/auto-switch.ts, and the two test files that import AccountService directly compile without edits.

Verification

npm run build               → clean (tsc -p tsconfig.json, Node16)
npm test                    → tests 129, pass 129, fail 0
wc -l account-service.ts    → 164 (< 400 ceiling)
find … | wc -l (total)      → 3842 across all accounts/*.ts
singleton probe             → all 21 methods are functions
node dist/index.js --help   → CLI boots, all 26 commands listed

Notes for reviewers

  • Diff looks large because the entire 1,675-LOC file is deleted and re-emitted as the orchestrator. Most "additions" are the 16 new module files. Original behavior survives line-for-line — comments preserved where they explained crash-safety rationale.
  • read/listing.ts and write/use.ts take a getCurrentAccountName thunk to break a potential cycle through sync/external-sync.ts. The orchestrator wires them with () => this.getCurrentAccountName().
  • The usage/ directory deliberately sits next to the existing usage.ts (660 LOC). That file is the next refactor target (X2 in the roadmap); this PR does not touch it.
  • git mv was not used because every new module also has different content; preserving blame would require keeping the old file in place. The original was deleted and the orchestrator rewritten — historical blame is reachable via git log --follow on the deleted lines.

🤖 Generated with Claude Code

NagyVikt and others added 2 commits May 17, 2026 17:46
…(N2)

Reduce the 1,675-LOC god-file at src/lib/accounts/account-service.ts to a
164-LOC orchestrator that delegates every public method to a focused
free-function module. No public API change: every signature on
`AccountService` is preserved and the `accountService` singleton in
`src/lib/accounts/index.ts` continues to work for `BaseCommand` and the
existing direct importers (`commands/check.ts`, `commands/forecast.ts`,
`commands/auto-switch.ts`, two test files).

Target modules (per `docs/future/01-ARCHITECTURE.md` §2.1):

- sync/external-sync.ts         (216 LOC) — syncExternalAuthSnapshot...
- read/listing.ts               (211 LOC) — list/find + getCurrentAccountName
- write/save.ts                 (159 LOC) — saveAccount + safety guard
- write/use.ts                  (128 LOC) — useAccount + activateSnapshot
- write/remove.ts               (105 LOC) — remove one / by query / all
- config/auto-switch-config.ts  ( 82 LOC) — status + threshold setters
- auto-switch/policy.ts         (141 LOC) — runAutoSwitchOnce + daemon
- usage/adapter.ts              (192 LOC) — usage refresh + proxy shim
- safety/snapshot-vault.ts      (125 LOC) — backup + clobber recovery
- session/pin.ts                (243 LOC) — sessions.json + Linux PPID
- identity/equality.ts          ( 86 LOC) — snapshot identity comparisons
- naming.ts                     ( 40 LOC) — normalizeAccountName + path

Shared internal helpers under `_internal/`:

- _internal/fs-helpers.ts       — pathExists / filesMatch / auth-state read
- _internal/auth-state.ts       — symlink materialize, current-name file I/O
- _internal/registry-ops.ts     — persistRegistry + metadata hydration
- _internal/name-resolution.ts  — login/inferred name resolution

Behavior is byte-identical: every existing test in the pre-N2 suite
still passes. Wave-1 contracts honored — atomic writes via N1's
`persistRegistryAtomic`/`secureWriteFile`, errors thrown via N3's
`AuthmuxError` subclasses, paths resolved per-call via N4's `resolve*`
getters.

New tests (31 cases across 3 files):

- src/tests/identity-equality.test.ts
- src/tests/naming.test.ts
- src/tests/accounts-modules.test.ts (covers listing, session/pin,
  snapshot-vault, auto-switch-config, auto-switch-policy, usage-adapter,
  write/use, write/remove, external-sync)

Verification:

- npm run build: clean
- npm test: 129 passing, 0 failing (was 98 pre-extraction)
- wc -l src/lib/accounts/account-service.ts → 164 (< 400 ceiling, ~150 goal)
- singleton: all 21 public methods still resolve as functions
- node dist/index.js --help: CLI boots and lists every command

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The accountFilePath test hardcoded "/tmp/authmux-test-accounts" and
asserted equality against a forward-slash POSIX path. On Windows the
resolved path comes back as "D:\tmp\authmux-test-accounts\..." which
fails the strict-equal check across all 3 Node versions in CI.

Build the expected path through os.tmpdir() + path.join() so both
sides go through the same platform-aware joining and the test passes
on Ubuntu, macOS, and Windows.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@NagyVikt NagyVikt merged commit 262ac9e into main May 17, 2026
12 checks passed
NagyVikt added a commit that referenced this pull request May 18, 2026
Bumps package.json + package-lock.json from 0.1.24 to 0.1.25 and
extends releases/v0.1.25.md to cover every theme that landed since
v0.1.24:

- N1 durability (#29): atomic writes, registry lock, fsync-before-rename
- N2 account-service split (#30): 1675 LOC -> 164 LOC orchestrator + 12 modules
- N3 error taxonomy + --json (#28): AuthmuxError base, stable codes, JSON envelope
- N4 lazy path resolvers (#27): bare constants deprecated for v0.2.0
- P0 wave (#26): secure perms, [y/N] update prompt, kiro ENOENT, CI matrix,
  registry proxy-source round-trip, 18k-line improvement docs

Notes follow the canonical 7-section shape from
docs/future/17-ROADMAP.md (Added, Changed, Fixed, Deprecated, Removed,
Security, Migration), with the prior Durability section folded into a
"Theme deep-dives" appendix so the canonical shape is intact.

No code changes. No npm publish.

Verification:
- npm run build: clean
- npm test: 129/129 pass
- node -e "require('./package.json').version" -> 0.1.25
- node -e "require('./package-lock.json').version" -> 0.1.25

Co-authored-by: NagyVikt <nagy.viktordp@gmail.com>
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