Commit 9696d10
committed
refactor: scoped state writes preserve untouched entries
## ELI5
**Problem.** Even when you ran a *scoped* push — say
`npm run push -- <env> assistants/foo.md` to update one assistant —
the engine rewrote the **entire** state file. Any pre-existing drift
in unrelated state entries (UUIDs from earlier sessions, untracked
local files, etc.) swept into the focused commit. Reviewers couldn't
tell from the state-file diff "what did this push actually change?"
and the state file became a pile of side effects accumulated across
sessions instead of a precise record of intent.
**What this fix does.** During a push, the engine tracks which
`resourceId`s it actually mutated (a per-section `Set<string>`). At
end-of-run, for **scoped pushes only**, it loads the on-disk state
fresh, replaces only the touched entries with the in-memory version,
and leaves everything else alone. Full pushes (no scope) still write
wholesale (existing behavior). Credentials are always replaced
because bootstrap pull populates them every push regardless.
This depends on Stack F's `ResourceState` because we need per-entry
metadata to distinguish "stale" from "just-not-touched."
**Outcome you'll notice.** A one-file `npm run push` produces a
one-file diff in the state file — same scope as the resource change.
Reviewers can read the state diff and tell "this push updated
assistant `foo`, here's its new hash" cleanly. Pre-existing drift
elsewhere in state stays where it is until you explicitly address it.
---
When push is scoped to specific paths, only update state entries for
the resources actually touched. A surgical push of two files used to
rewrite the entire state file, sweeping in pre-existing drift from
earlier pushes (improvements.md #15) and producing noisy diffs that
hide the actual scope of the change.
Files:
- src/state-merge.ts (NEW): mergeScoped(disk, inMemory, touched).
For each section, replace only touched.X resourceIds with the in-memory
version; leave the rest of disk's section as-is. Credentials are
always replaced wholesale (bootstrap pull populates them on every
push). Pure data, no I/O — safe to test directly.
- src/push.ts: TouchedSets tracker. Each upsertState call site
records the resourceId. End-of-run, partial pushes call
mergeScoped(loadState(), state, touched) before saveState; full
pushes save wholesale (existing behavior).
- tests/state-merge.test.ts: replace-only-touched, leave-untouched,
drift in untouched stays, credentials always replaced.
Closes improvements.md #15.
🤖 Generated with [Claude Code](https://claude.com/claude-code)1 parent e3ca040 commit 9696d10
4 files changed
Lines changed: 273 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
66 | 66 | | |
67 | 67 | | |
68 | 68 | | |
69 | | - | |
| 69 | + | |
70 | 70 | | |
71 | 71 | | |
72 | 72 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
| 18 | + | |
18 | 19 | | |
19 | 20 | | |
20 | 21 | | |
| |||
777 | 778 | | |
778 | 779 | | |
779 | 780 | | |
| 781 | + | |
| 782 | + | |
| 783 | + | |
| 784 | + | |
| 785 | + | |
| 786 | + | |
| 787 | + | |
| 788 | + | |
| 789 | + | |
| 790 | + | |
| 791 | + | |
| 792 | + | |
| 793 | + | |
| 794 | + | |
| 795 | + | |
| 796 | + | |
| 797 | + | |
| 798 | + | |
| 799 | + | |
| 800 | + | |
| 801 | + | |
| 802 | + | |
| 803 | + | |
| 804 | + | |
| 805 | + | |
| 806 | + | |
| 807 | + | |
| 808 | + | |
| 809 | + | |
| 810 | + | |
| 811 | + | |
| 812 | + | |
| 813 | + | |
| 814 | + | |
780 | 815 | | |
781 | 816 | | |
782 | 817 | | |
| |||
966 | 1001 | | |
967 | 1002 | | |
968 | 1003 | | |
| 1004 | + | |
| 1005 | + | |
| 1006 | + | |
| 1007 | + | |
969 | 1008 | | |
970 | 1009 | | |
971 | 1010 | | |
| |||
1203 | 1242 | | |
1204 | 1243 | | |
1205 | 1244 | | |
| 1245 | + | |
1206 | 1246 | | |
1207 | 1247 | | |
1208 | 1248 | | |
| |||
1221 | 1261 | | |
1222 | 1262 | | |
1223 | 1263 | | |
| 1264 | + | |
1224 | 1265 | | |
1225 | 1266 | | |
1226 | 1267 | | |
| |||
1252 | 1293 | | |
1253 | 1294 | | |
1254 | 1295 | | |
| 1296 | + | |
1255 | 1297 | | |
1256 | 1298 | | |
1257 | 1299 | | |
| |||
1277 | 1319 | | |
1278 | 1320 | | |
1279 | 1321 | | |
| 1322 | + | |
1280 | 1323 | | |
1281 | 1324 | | |
1282 | 1325 | | |
| |||
1295 | 1338 | | |
1296 | 1339 | | |
1297 | 1340 | | |
| 1341 | + | |
1298 | 1342 | | |
1299 | 1343 | | |
1300 | 1344 | | |
| |||
1313 | 1357 | | |
1314 | 1358 | | |
1315 | 1359 | | |
| 1360 | + | |
1316 | 1361 | | |
1317 | 1362 | | |
1318 | 1363 | | |
| |||
1331 | 1376 | | |
1332 | 1377 | | |
1333 | 1378 | | |
| 1379 | + | |
1334 | 1380 | | |
1335 | 1381 | | |
1336 | 1382 | | |
| |||
1349 | 1395 | | |
1350 | 1396 | | |
1351 | 1397 | | |
| 1398 | + | |
1352 | 1399 | | |
1353 | 1400 | | |
1354 | 1401 | | |
| |||
1366 | 1413 | | |
1367 | 1414 | | |
1368 | 1415 | | |
| 1416 | + | |
1369 | 1417 | | |
1370 | 1418 | | |
1371 | 1419 | | |
| |||
1455 | 1503 | | |
1456 | 1504 | | |
1457 | 1505 | | |
1458 | | - | |
| 1506 | + | |
| 1507 | + | |
| 1508 | + | |
| 1509 | + | |
| 1510 | + | |
| 1511 | + | |
| 1512 | + | |
| 1513 | + | |
1459 | 1514 | | |
1460 | 1515 | | |
1461 | 1516 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
0 commit comments