- Creates pulse.service in ~/.config/systemd/user/
- Restart=always with 5s delay, rate limited 10/60s
- Enables service automatically (user starts manually)
- Linux only, graceful fallback on other platforms
Bug #65: Unify JSONata bindings between validateExpression and foldProjection
- Modified defs.ts validateExpression to use bindings (, , )
- Updated all test expressions to use bindings syntax for consistency
- Both validation and execution now use the same bindings approach
Bug #66: Include sources in projection hash calculation
- Extended calculateProjectionHash to include sources array
- Prevents hash collisions when only expression changes
- Ensures projection_def_sources foreign key constraints work correctly
Bug #69: Export new modules from index.ts
- Added exports for defs.js and projection-engine.js
- External packages can now import definitions and projection engine
All tests pass (225/225) with added test coverage for the fixes.
- Add three append-only definition tables: object_defs, event_defs, projection_defs
- Support content-addressed versioning with SHA-256 hashes
- Implement JSONata expression validation for projections
- Add projection_def_sources table with foreign key constraints
- Support (name, code_rev) unique constraints for versioning
- Add comprehensive test suite covering all 9 scenarios from Issue #60closes#60
- Replace VitalRecord with type alias to EventRecord (kind='vital')
- Remove vitals-specific methods from PulseStore interface
- Add generic archiveEvents/downsampleEvents to PulseStore
- Update rebuildSnapshot with overloaded signature for system/vitals stores
- Migrate legacy vitals.db data to _vitals scope as kind='vital' events
- Remove openVitalsStore, vitalsDbFile config, PULSE_VITALS_DB env var
- Update gc, deploy, server, init, watcher to use events API
- Update all tests (200 pulse + 76 upulse pass)
Made-with: Cursor
Co-authored-by: 小橘 <xiaoju@shazhou.work>
Add ScopedStore interface and createScopedStore() factory that partitions
events into per-scope SQLite files while sharing a single CAS objects/
directory. Scope names are validated ([a-z0-9_-], max 64 chars) and
databases are lazily opened and cached. Existing createStore API remains
fully backward compatible. Includes 20 new tests.
Made-with: Cursor
Co-authored-by: 小橘 <xiaoju@shazhou.work>
- Server<undefined> type param for Bun.serve (TS2314)
- Remove stale @ts-expect-error in deploy.ts (TS2578)
- Pre-push hook now runs tsc --noEmit for all packages
- Pre-push hook now tests all packages (was missing upulse, pulse-cursor, pulse-openclaw)
- Use subshells + absolute ROOT path instead of fragile cd chains
Fixes CI failure on PR #52.
— 小糯 🐱
- Remove symlink: engine/ and staging/ have independent node_modules
- Init: bun install in engine before staging worktree (bun.lock committed)
- Init: symlink migration detection (auto-replace legacy symlinks)
- Promote: graceful 'bun test' when staging has no test files
- Promote: frozen-lockfile with fallback to regular bun install
- Promote: migrate discovers sense keys from vitals (post-#47 compat)
- Rollback: frozen-lockfile with fallback to regular bun install
- Fix createRule template to use correct 3-param Rule<S,E> signature
- Update E2E T5 to seed vitals data (tick no longer writes collect events)
- Update E2E T2/T4 test patterns for new Rule<S,E> signature
Add a local WebUI dashboard for monitoring Pulse runtime state.
Run `upulse ui` to start at http://localhost:3140.
Features:
- Dashboard: daemon status, last tick, code rev, recent events timeline
- Vitals: sense key selection, canvas line charts, raw data view
- Rules: engine rule files in onion order
- Deploy: promote/rollback history with active version badge
- Event detail: click any event to view CAS object data
Technical:
- Zero dependencies: Bun HTTP server + single-file embedded HTML
- Linear-inspired dark theme, monospace accents
- 7 API endpoints reading from PulseStore
- Auto-polling (5s) on dashboard
- localhost-only, read-only, no auth needed
Files:
- commands/ui.ts: CLI command registration
- ui/server.ts: Bun HTTP server + API routes
- ui/dashboard.ts: embedded HTML/CSS/JS (874 lines)
- ui/server.test.ts: 8 API tests
Closes#50
tick.ts was manually calling collectSystem() and writing events,
bypassing the watcher framework. This was legacy from the old design
where tick handled both collection and rule execution.
New design: tick is a pure read → decide → execute operation:
- Rebuild snapshot from existing vitals/events (rebuildSnapshot)
- Discover senseKeys from existing collect events in store
- Use findEffectiveEpoch for version-aware snapshots
- Run rules to produce effects
- Log effects (collect effects are skipped — daemon handles collection)
Changes:
- Remove collectSystem import and all manual collect() calls
- Remove bootstrap logic (empty store → empty snapshot, rules decide)
- Use prev = curr for single-tick (no meaningful time delta)
- Add queryByKind to store type for senseKey discovery
- Update E2E test: check for tick events instead of collect events
Closes#47
Co-authored-by: 鹿鸣 <luming@shazhou.work>
* chore: add pre-push hook for lint + tests (closes#29)
- scripts/install-hooks.mjs: zero-dep hook installer, runs on postinstall
- Pre-push runs: biome lint + pulse tests + pulse-hermes tests
- CI: add pulse-hermes type check + tests, use bun run lint
- CONTRIBUTING.md: document hook setup
Uses pure Node.js APIs (no Bun dependency) so postinstall works with any runtime.
Users can delete .git/hooks/pre-push to opt out.
* fix: TS2322 in findEffectiveEpoch — cast meta.to to string
meta is Record<string, unknown>, so meta.to is unknown.
Explicit cast to (string | undefined) satisfies codeRev: string param.
---------
Co-authored-by: 鹿鸣 <luming@shazhou.work>
- Add pnpm-workspace.yaml for monorepo workspace
- Add root package.json with packageManager field
- Add .npmrc with shamefully-hoist=true
- Replace bun.lock with pnpm-lock.yaml
- Use workspace:* protocol for @uncaged/pulse dependency
- Update init.ts scaffold to use pnpm install
- Update CLI commands (tick/dev/deploy) to use pnpm/npx
- Update E2E test comments to reference pnpm
- Keep bun as TS runtime (bun:sqlite, bun:test still needed)
closes#19
- Fix processWatchdog to read Record<string, boolean> from process-alive watcher
- Fix resourceGuard to read flat {diskPct, memoryPct} from system-resource watcher
- Fix networkWatchdog to check {dnsOk, httpOk} from network watcher
- Add proper SurvivalSnapshot interface replacing Rule<any, SurvivalEffect>
- Fix rebuildHealth panicCount to count rollback-config events from DB
- Update rollback-code executor to use upulse rollback instead of git checkout
- Update tests with correct mock data structures
All survival rules now access correct data fields, preventing silent failures.
- Add packages/pulse/src/survival/ with 5 core files
- Implement 7 survival rules in onion order (panicRollback, autoRollback, processWatchdog, resourceGuard, llmWatchdog, networkWatchdog, errorEscalate)
- Add SurvivalEffect executors for restart-service, archive-sessions, gc-vitals, clear-cache, rollback-code, rollback-config, notify-owner
- Add rebuildHealth function to rebuild health snapshot from events
- Add comprehensive test coverage (33 tests) for rules and executors
- Export survival layer from main index.ts
- All tests pass, biome checks clean
- biome.json: 2-space indent, single quotes, recommended rules
- All 28 source files formatted
- CI: lint runs before type check and tests
- Root package.json with lint/lint:fix scripts
- Fix noNonNullAssertedOptionalChain in store.test.ts
82 unit tests still green.
小橘 🍊(NEKO Team)