docs(FN-4907): finalize stale-settings audit report

Fusion-Task-Id: FN-4907
Fusion-Task-Lineage: e84d5357-0719-4cc7-ab3f-f665ac83b218
This commit is contained in:
Fusion (runfusion.ai)
2026-05-17 10:16:02 -07:00
committed by gsxdsm
parent a2bd876eb4
commit 500a55ff37

View File

@@ -31,15 +31,29 @@
| `DaemonTokenManager.rotateToken*` (`packages/core/src/daemon-token.ts`) | other `GlobalSettingsStore` instances reading `daemonToken` | Writes via its private `GlobalSettingsStore` only; no cross-instance invalidation |
| `FirstRunExperience.completeSetup()` (`packages/core/src/first-run.ts`) | other global settings store instances reading `setupComplete` | Writes via private `GlobalSettingsStore`; no cross-instance invalidation |
## Suspect call-site audit (Step 2)
## Findings (severity)
| Suspect | Status | Evidence |
|---|---|---|
| `register-custom-provider-routes.ts` writes global settings but does not fan out invalidation | **Bug** | Route writes at lines 461/499/530 with no `invalidateAllGlobalSettingsCaches()` call, unlike `/settings/global` and auth toggles.
| `POST /settings/sync-receive` + `CentralCore.applyRemoteSettings()` handling | **Bug (global sync path)** + **Not-a-bug (project TaskStore event path)** | Inbound route only calls `central.applyRemoteSettings(payload)` (line 65), and `applyRemoteSettings()` explicitly does not apply global settings (central-core lines 3546-3552). Project settings in `CentralCore` are central-registry sync snapshots, not per-project `TaskStore` config state; bypassing `TaskStore.updateSettings()` here is expected.
| `PeerExchangeService.updateGlobalSettings()` has no wiring | **Bug** | Method exists and clears cached sync payload (peer-exchange-service lines 102-107), but dashboard/daemon startup paths construct/start `PeerExchangeService` without subscribing to settings changes (dashboard line 1549, daemon setup path has no bridge).
| `GlobalSettingsStore.cachedSettings` per-instance cache topology | **Already-handled with caveat** | Cross-store invalidation is done in selected routes via `invalidateAllGlobalSettingsCaches()`; gaps are route-specific (e.g., custom-provider routes) not store-implementation bugs.
| Long-lived executor/heartbeat/merger snapshots | **Not a bug** | Settings are repeatedly fetched (`await store.getSettings()`) in-run; no single startup snapshot reused for model/budget/workflow decisions.
| `getSettingsFast()` / `getSettingsByScopeFast()` missing workflow-steps | **Not a bug (contracted behavior)** | Store docs explicitly state fast path skips workflow-step hydration (store line 2340+ and 2406+ comments).
| `daemon-token.ts` direct `GlobalSettingsStore.updateSettings` | **Not a bug (documented caveat)** | Token reads/writes use one manager instance; cross-instance staleness only exists until explicit invalidation/read-through in other instances.
| `first-run.ts` direct `GlobalSettingsStore.updateSettings` | **Not a bug (documented caveat)** | `setupComplete` is written at end of setup flow and subsequently read in new flows; no observed long-lived stale consumer in same call chain.
| Suspect | Severity | Status | Evidence |
|---|---:|---|---|
| `register-custom-provider-routes.ts` writes global settings but does not fan out invalidation | Medium | **Bug fixed** | Route writes at lines 461/499/530 with no `invalidateAllGlobalSettingsCaches()` call, unlike `/settings/global` and auth toggles.
| `POST /settings/sync-receive` + `CentralCore.applyRemoteSettings()` handling | High | **Bug fixed (global sync path)** + **Not-a-bug (project TaskStore event path)** | Inbound route only calls `central.applyRemoteSettings(payload)` (line 65), and `applyRemoteSettings()` explicitly does not apply global settings (central-core lines 3546-3552). Project settings in `CentralCore` are central-registry sync snapshots, not per-project `TaskStore` config state; bypassing `TaskStore.updateSettings()` here is expected.
| `PeerExchangeService.updateGlobalSettings()` has no wiring | Medium | **Bug fixed** | Method exists and clears cached sync payload (peer-exchange-service lines 102-107), but dashboard/daemon startup paths constructed the service without live refresh wiring.
| `GlobalSettingsStore.cachedSettings` per-instance cache topology | Low | Already-handled with caveat | Cross-store invalidation is done in selected routes via `invalidateAllGlobalSettingsCaches()`; gaps were route-specific.
| Long-lived executor/heartbeat/merger snapshots | Low | Not a bug | Settings are repeatedly fetched (`await store.getSettings()`) in-run; no single startup snapshot reused for model/budget/workflow decisions.
| `getSettingsFast()` / `getSettingsByScopeFast()` missing workflow-steps | Low | Not a bug (contracted behavior) | Store docs explicitly state fast path skips workflow-step hydration (store line 2340+ and 2406+ comments).
| `daemon-token.ts` direct `GlobalSettingsStore.updateSettings` | Low | Not a bug (documented caveat) | Token reads/writes use one manager instance; cross-instance staleness only exists until explicit invalidation/read-through in other instances.
| `first-run.ts` direct `GlobalSettingsStore.updateSettings` | Low | Not a bug (documented caveat) | `setupComplete` is written at end of setup flow and subsequently read in new flows.
## Fixes shipped in FN-4907
1. Added `invalidateAllGlobalSettingsCaches()` after custom-provider create/update/delete writes.
2. Inbound `/api/settings/sync-receive` now applies global fields (when local value is unset) via `store.updateGlobalSettings(...)` and invalidates caches.
3. Dashboard/daemon `PeerExchangeService` now receives initial global settings plus refreshes on `settings:updated` events so cached sync payloads are invalidated.
4. Regression tests added for each fix:
- `custom-provider-routes.test.ts`
- `routes-nodes-sync.test.ts`
- `dashboard.test.ts` mesh lifecycle assertion
- `daemon.test.ts` compatibility for new wiring
## Follow-ups
- None required; all identified stale-snapshot bugs in this audit were fixed in-task.