fix(pulse): stop setup.ts clobbering an existing PULSE.toml - #1642
Open
elhoim wants to merge 1 commit into
Open
Conversation
setup.ts wrote its three-job worker template over PULSE.toml with no existence check and no backup, so `bun run setup.ts` on a configured install destroyed the daemon's config of record in one call — modules, ports, notification routing, blocked-skills lists, the DA block and every cron job. Most installs are not git repos, so the loss was unrecoverable. The .env write twenty lines later already checked before touching the file; the function contradicted itself. All config writes now go through writeConfigPreserving: create when absent, preserve when present, replace only under an explicit --force, which announces the replacement and copies the file to <file>.backup-<timestamp> first. On a preserved run the generated worker block is printed so it can be merged by hand. Same fix applied to the launch agent write, which also overwrote the installed plist unconditionally and would discard local edits. The other writes in this path (.env append, /etc/hosts append, mkcert certs) were already guarded and are unchanged. main() is now behind an import.meta.main guard so the config-write helpers are importable without provisioning.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
PULSE/setup.tsunconditionally overwritesPULSE.tomlwith a three-job template. There is no existence check and no backup, so running the documentedbun run setup.tson an already-configured install destroys the daemon's config of record in a single call.What goes:
[voice],[telegram],[imessage],[observability]including the server port, every[notifications.*]routing rule and threshold,[hooks] blocked_skills,[work],[bunker],[syslog],[local_intelligence],[telos], the whole[da]block (heartbeat, diary and growth schedules, cost ceiling), and every[[job]]cron entry — replaced by[worker]plus three samples.Most personal installs are not git repositories, so this is unrecoverable.
The fix is suggested by the surrounding code: the
.envwrite about twenty lines below already checks for existence and appends rather than clobbering. The two branches of the same function disagreed with each other.The fix
Every config write now goes through one
writeConfigPreservinghelper:--force→ replaced, but only after announcing it and copying the existing file aside as<file>.backup-<timestamp>Create-only is the right default here rather than a merge: a partial merge of a TOML the operator has hand-tuned produces a config that is neither theirs nor the template's, and silently reintroducing sample jobs into a live scheduler is its own failure. Preserve-and-tell leaves the operator in control, and
--forceplus a backup covers the genuine reset case.The launch agent write is guarded the same way, for the same reason.
Tests
test/pulse/setup.test.ts, 9 cases: a fresh path is created, an existing config survives byte-identical,--forcereplaces it, the backup holds the original contents, and the announcement fires before anything on disk changes.Separately verified against the exported helper with an independent case set, including one the unit tests do not cover: repeated
--forceruns keep distinct backups rather than overwriting the previous one.Notes
No behaviour change for a fresh install. The only way to lose config now is to pass
--force, which prints the backup path first.