|
| 1 | +# Exploration: gentle-engram-cloud-autotick |
| 2 | + |
| 3 | +## Status |
| 4 | + |
| 5 | +- SDD mode: auto |
| 6 | +- Artifact store: OpenSpec |
| 7 | +- Worktree: `../engram-gentle-engram-cloud-autotick` |
| 8 | + |
| 9 | +## Question |
| 10 | + |
| 11 | +Pi `gentle-engram` currently connects Pi sessions to Engram memory, but Cloud autosync only starts when the underlying Engram process sees `ENGRAM_CLOUD_AUTOSYNC=1`. The user wants Pi package parity with Engram Cloud's 30-second autosync loop. |
| 12 | + |
| 13 | +## Evidence |
| 14 | + |
| 15 | +### Pi plugin behavior |
| 16 | + |
| 17 | +- `plugin/pi/index.ts` registers Pi-native `mem_*` tools and lifecycle handlers. |
| 18 | +- `initOnce()` starts `engram serve` when `/health` is not reachable and `ENGRAM_URL` is not set. |
| 19 | +- The spawned `engram serve` process inherits the parent environment, but the plugin does not derive or inject `ENGRAM_CLOUD_AUTOSYNC=1` from Cloud configuration. |
| 20 | +- `initOnce()` runs one `engram sync --import` for git-synced chunks when `.engram/manifest.json` exists. This is not Cloud autosync. |
| 21 | + |
| 22 | +### Engram core autosync behavior |
| 23 | + |
| 24 | +- `internal/cloud/autosync/manager.go` owns durable background sync behavior. |
| 25 | +- `DefaultConfig()` sets `PollInterval: 30 * time.Second`. |
| 26 | +- `Run()` uses `time.NewTicker(m.cfg.PollInterval)` and runs `safeRun()` on each poll tick. |
| 27 | +- This manager also supports dirty notification debounce, push/pull, lease, backoff, and status. |
| 28 | + |
| 29 | +### Core process startup gates |
| 30 | + |
| 31 | +- `cmd/engram/main.go::tryStartAutosync()` starts autosync only when `ENGRAM_CLOUD_AUTOSYNC` is exactly `"1"`. |
| 32 | +- It also requires a Cloud server URL and `ENGRAM_CLOUD_TOKEN`. |
| 33 | +- The function is intentionally non-fatal: missing config logs and leaves Engram running without autosync. |
| 34 | +- `engram serve` and `engram mcp` call `tryStartAutosync()`. |
| 35 | + |
| 36 | +### Pi MCP setup behavior |
| 37 | + |
| 38 | +- `plugin/pi/cli.js` writes an MCP server launcher that runs `engram mcp --tools=agent`. |
| 39 | +- The launcher currently inherits the Pi process environment but does not infer `ENGRAM_CLOUD_AUTOSYNC=1` from Cloud token/server configuration. |
| 40 | + |
| 41 | +### Docs behavior |
| 42 | + |
| 43 | +- `docs/AGENT-SETUP.md` documents the current core toggle: users must export `ENGRAM_CLOUD_AUTOSYNC=1`, `ENGRAM_CLOUD_TOKEN`, and `ENGRAM_CLOUD_SERVER`. |
| 44 | +- `plugin/pi/README.md` says Cloud is opt-in and project-scoped, but does not document Pi package autosync parity. |
| 45 | + |
| 46 | +## Findings |
| 47 | + |
| 48 | +### What is missing in Pi parity today? |
| 49 | + |
| 50 | +Pi does not automatically enable the existing Engram core autosync loop for Engram processes it launches/configures. Users who run Pi with Cloud token/server configured still need to know and export `ENGRAM_CLOUD_AUTOSYNC=1`; otherwise the 30-second core ticker never starts. |
| 51 | + |
| 52 | +### Where should the 30-second tick live? |
| 53 | + |
| 54 | +The tick must stay in `internal/cloud/autosync`. The Pi plugin should not implement its own polling or Cloud sync loop. `gentle-engram` should only provide thin process environment wiring so the existing Engram core process starts its own autosync manager. |
| 55 | + |
| 56 | +### What gates preserve opt-in cloud sync? |
| 57 | + |
| 58 | +Recommended plugin-side auto-enable gate: |
| 59 | + |
| 60 | +1. Never override an explicit `ENGRAM_CLOUD_AUTOSYNC` value. |
| 61 | +2. Require `ENGRAM_CLOUD_TOKEN` to be non-empty. |
| 62 | +3. Require either `ENGRAM_CLOUD_SERVER` or persisted local `cloud.json.server_url`. |
| 63 | +4. Let Engram core continue enforcing project enrollment, server policy, pause controls, lease, retry, and status. |
| 64 | + |
| 65 | +This keeps local-first behavior because no token means no Cloud replication; configured token+server is a strong Cloud opt-in signal. |
| 66 | + |
| 67 | +### Tests that should fail first |
| 68 | + |
| 69 | +1. `plugin/pi/test/cloud-autosync-env.test.mjs` |
| 70 | + - auto-enables when token + `ENGRAM_CLOUD_SERVER` are present; |
| 71 | + - auto-enables when token + persisted `cloud.json.server_url` are present; |
| 72 | + - does not enable without token; |
| 73 | + - does not override explicit `ENGRAM_CLOUD_AUTOSYNC=0` or `1`. |
| 74 | +2. `plugin/pi/test/index-source.test.mjs` |
| 75 | + - verifies `spawnDetached` uses the cloud-autosync env helper when starting Engram processes. |
| 76 | +3. `plugin/pi/test/cli-source.test.mjs` or CLI helper tests |
| 77 | + - verifies generated MCP launcher includes equivalent auto-enable env logic for `engram mcp`. |
| 78 | + |
| 79 | +### Docs to update |
| 80 | + |
| 81 | +- `plugin/pi/README.md`: add Pi Cloud autosync parity section. |
| 82 | +- `docs/AGENT-SETUP.md`: clarify that Pi `gentle-engram` can auto-enable autosync for its launched/configured Engram processes when token+server are present, while raw `engram serve`/`engram mcp` still support the explicit env toggle. |
| 83 | + |
| 84 | +## Risks |
| 85 | + |
| 86 | +| Risk | Mitigation | |
| 87 | +|---|---| |
| 88 | +| Plugin violates thin-adapter boundary | Only set child process environment; do not implement sync/polling in plugin. | |
| 89 | +| Surprise Cloud sync | Require token + server and preserve explicit `ENGRAM_CLOUD_AUTOSYNC` overrides. Project enrollment remains authoritative. | |
| 90 | +| Duplicate autosync workers | Existing SQLite lease in core manager prevents duplicate workers from syncing concurrently. | |
| 91 | +| Noisy logs when Cloud partially configured | Gate on token+server before injecting autosync. | |
| 92 | + |
| 93 | +## Recommendation |
| 94 | + |
| 95 | +Implement a small Pi helper that builds child-process env for Engram processes. Use it for `engram serve`, generated `engram mcp` launcher, and any plugin-spawned one-shot sync command where harmless. The helper should only set `ENGRAM_CLOUD_AUTOSYNC=1` when the user has Cloud token + server configured and did not set an explicit autosync value. |
0 commit comments