os.homedir() reads USERPROFILE on Windows and never HOME, so the
isolation setup and every test that moved home were still resolving the
developer's real home directory. Fixtures came back empty, real sessions
leaked into counts, and the code under test wrote plans, receipts and a
sync ledger into the real ~/.config/codeburn during a run.
USERPROFILE now joins the redirected variables and a shared setHome()
helper moves HOME and USERPROFILE together; every spawn env carries both.
The remaining Windows-only failures were POSIX-shaped fixtures: chmod
tricks that only toggle a read-only attribute on NTFS, pid 1 as an
always-alive lock holder, splitting paths on '/', bare tar, mode-bit
expectations, and per-file timeouts too short for the cost of a tsx spawn.
Each gets a platform-aware equivalent so nothing is skipped on Windows and
the POSIX branches stay byte-for-byte what they were.
Full run on Windows: 267 files passed, 2 skipped, 3592 tests passed.
The rebase spliced HEAD's damaged-record tests and the desktop branch's --json status tests into one block; rebuilt as the full HEAD suite plus the desktop 'sync auto status --json' describe block. 30 tests, no duplicate titles.
Part 1: Add --json output flags to three CLI commands for machine-readable output
- codeburn plugin list --json: array of plugin objects with status and capabilities
- codeburn plugin info <name> --json: manifest with dir and onDiskSections
- codeburn sync auto status --json: status object with configured, accepted, killed, receipts
Part 2: Implement Electron bridge with read and mutation handlers
- Read handlers: pluginList, pluginInfo, syncAutoStatus (return parsed JSON)
- Mutation handlers: pluginAdd, pluginRemove, pluginVerify, syncAutoEnable, syncAutoDisable
- All handlers follow existing Envelope pattern with proper error handling
- Special case: syncAutoEnable captures disclosure text when accept=false
Part 3: Renderer UI components (minimal implementation D1-D3 shell)
- Add Plugins section to nav rail at bottom (quiet entry, plugin icon)
- Implement PluginsSection component with list rendering for loaded/rejected plugins
- Update Section type and routing to include plugins navigation
- Add test coverage for Plugins panel row states
- Implemented as dormant Team tab registry placeholder for future plugin sections
Tests: All CLI tests (3839 passed), app tests (613 passed), E2E gates all passing
- CLI: tsc clean, vitest passes with 26 pre-existing jsdom errors
- App: typecheck clean, npm test green, npm run build succeeds
Two fixes from a deep review of the consent scheduler.
The LaunchAgent plist was being written with process.argv[1] as the sole
ProgramArguments entry, which is a non-executable .ts/.js path under
plain node and a stale copy under packaged Electron. buildLaunchAgentPlist
now takes both runtimePath and scriptPath, emits them as separate
<string> entries, and sets ELECTRON_RUN_AS_NODE so the packaged Electron
binary behaves as a node runtime. Both paths are XML-escaped before being
dropped into the plist so a stray & or < in a path cannot break it. The
non-darwin crontab hint now prints the same two-argument form. Caller in
src/sync/cli.ts passes process.execPath and process.argv[1].
When config.auto.accepted is present but missing fingerprint or
acceptedAt, sync auto status used to interpolate the missing field and
print the literal 'undefined'. It now detects the malformed record, prints
a single line telling the user to re-run sync auto enable, and exits non
zero. The run command already fails closed on this case and is unchanged.
Reused menubar-installer pattern for schedule installer. On non-darwin, prints crontab line to add manually. Auto block is optional in sync config; add-only and never migrates existing installs.