@try-works/dsh-memory 0.1.22 → 0.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/STATUS.md +22 -16
- package/package.json +1 -1
- package/src/git.js +8 -2
- package/src/host.js +32 -1
package/STATUS.md
CHANGED
|
@@ -62,6 +62,8 @@ Observed in the running host. Each row names the effect that was seen, not a cla
|
|
|
62
62
|
| `memory_recover` is wired | calling it no longer answers "unavailable in this context"; it reaches output validation |
|
|
63
63
|
| Session logs are searchable in principle | zstd, not encrypted; 368 logs, 740MB compressed, ~2.96GB plaintext |
|
|
64
64
|
| `memory/map` events carry `ignorable: true` | the live session log's own event is marked, and both dsh-memory logs pass the harness's `validateStoredEvents` — the exact check that refused session-cc4795a7 with "unknown to this harness and not marked ignorable" |
|
|
65
|
+
| 0.1.21 guard is LOADED | the 2026-10-11 06:01+08 restart booted the installed 0.1.21; `map-publish ok:true` events follow boot, so the guarded publisher runs. The suppression branch itself cannot be observed on this harness — it only fires on one that does not stamp the marker |
|
|
66
|
+
| Boot tasks backup + restore ran live | at boot (2026-10-10T22:03:14Z) the log records `tasks-backup ok:true written:0 refreshed:5` and `tasks-restore ok:true owner:session-6e8d9a10 present:5 added:0 paused:0` — the restore reached the schedule service, found everything present, and duplicated nothing |
|
|
65
67
|
|
|
66
68
|
## WRITTEN but NOT LOADED
|
|
67
69
|
|
|
@@ -73,6 +75,7 @@ Implemented and tested on disk. The running host predates them, so none of this
|
|
|
73
75
|
| `hostExtras(host)` as one source | `index.js` and the wiring test shared a copied list that drifted | the test now derives extras from the host object |
|
|
74
76
|
| `memory_recover` report omits its `entries` field rather than blanking it | the old line added `entries: undefined`, which is NOT valid JSON, so the harness rejected the whole result | the new guard round-trips every tool output; reintroducing it fails with `value.entries is undefined` |
|
|
75
77
|
| Publisher stops when the harness does not stamp `ignorable` (0.1.21) | ≤0.1.19 appended `memory/map` WITHOUT the marker into ten real session logs, so every later load of those sessions failed ("unknown to this harness and not marked ignorable"). The guard checks the RETURN VALUE of `session.append` and suppresses publishing for that session at the first refusal, instead of poisoning the log | 403 tests pass; the new test drives a harness-shaped stub that returns an unstamped event and asserts the next turn appends nothing, even with a changed store and a passed throttle |
|
|
78
|
+
| Git runner resolves the subprocess service lazily (0.1.22) | `makeGitRunner` probed `ctx.get('subprocess')` ONCE at wire time; cordis `get()` only returns services whose providing fiber is ACTIVE (vendor/cordis reflect.ts), so during mount it returns undefined, git stayed null forever, and every git path was dead: zero sync events ever recorded, and `history-check` threw `Cannot read properties of null (reading 'run')` hourly since 2026-10-06. The runner now re-probes at use and reports an honest reason while absent | 405 tests pass; the new test pins no-probe-at-mount, an honest refusal while the service is absent, and working ingest after it appears |
|
|
76
79
|
|
|
77
80
|
How to tell loaded from not: ask the host. `memory_recover` used to reply *"unavailable in this
|
|
78
81
|
context"* and now replies *"returned invalid output: value is not lossless JSON"*. The change of
|
|
@@ -102,12 +105,12 @@ byte-identical.
|
|
|
102
105
|
- No task file leaks into memory search, checked by building the index over a store holding the layer.
|
|
103
106
|
- `SYSTEM.md` names the layer, its path, `memory_tasks`, and all eight states.
|
|
104
107
|
|
|
105
|
-
**
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
**VERIFIED live, 2026-10-11 boot.** `logs/memory.log` at 2026-10-10T22:03:14Z records a real
|
|
109
|
+
`tasks-backup ok:true written:0 refreshed:5` and a real `tasks-restore ok:true present:5 added:0
|
|
110
|
+
paused:0` from the running host — the cadence and the boot restore executed outside the test
|
|
111
|
+
harness for the first time, reached the schedule service, found all five tasks present, and
|
|
112
|
+
duplicated nothing (`added: 0`, which is the de-duplication property the drill cared about). The
|
|
113
|
+
paragraph below is retained as the record of what "unverified" looked like before this boot.
|
|
111
114
|
|
|
112
115
|
Seven defects were found by asking what the REAL path does, and not one by an existing test: the
|
|
113
116
|
empty-string config fallback, the wrong directory, a fixture-coincidence assertion, `orphaned` with
|
|
@@ -147,12 +150,14 @@ the flag on did not change what the user sees.
|
|
|
147
150
|
|
|
148
151
|
### Blocked on a restart
|
|
149
152
|
|
|
150
|
-
- [ ] **Restart to load 0.1.
|
|
151
|
-
|
|
152
|
-
profile
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
153
|
+
- [ ] **Restart to load 0.1.22.** The lazy git-runner fix is published; install is pending the
|
|
154
|
+
registry finishing its staged publish (E409 "previously staged version" on retry). Once the
|
|
155
|
+
profile install lands and the host restarts, the hourly `history-check ok:false null.run`
|
|
156
|
+
should become either a working ingest or an honest `reason` — and git sync gets its first
|
|
157
|
+
real chance to run ever.
|
|
158
|
+
- [x] **Restart to load 0.1.21 — DONE 2026-10-11 06:01+08.** The host booted the installed 0.1.21;
|
|
159
|
+
`map-publish ok:true` events follow boot and the boot tasks backup/restore ran live (see
|
|
160
|
+
LIVE table).
|
|
156
161
|
- [x] **Restart, then check the tasks layer — DONE 2026-10-09.** The restart confirmed it. See the
|
|
157
162
|
tasks-layer section above for the two log entries and the counts. It writes to ANOTHER plugin's storage through
|
|
158
163
|
that plugin's service, so it deserves a real observation rather than a passing suite. After
|
|
@@ -160,13 +165,14 @@ the flag on did not change what the user sees.
|
|
|
160
165
|
`pending.present: 2`. Then check `logs/memory.log` for a `tasks-backup` entry and a
|
|
161
166
|
`tasks-restore` entry: the first should say `refreshed: 2`, the second `present: 2, added: 0`.
|
|
162
167
|
An `added` above zero would mean de-duplication failed and it duplicated a task.
|
|
163
|
-
- [
|
|
164
|
-
|
|
165
|
-
|
|
168
|
+
- [x] **Restart again — DONE 2026-10-11.** After the 06:01+08 restart, `memory_recover({})` answers
|
|
169
|
+
with a report: 75 candidates stopped with missing material, 75 recoverable now, 0 need
|
|
170
|
+
approval. The output-validation fix is live.
|
|
166
171
|
|
|
167
172
|
### Recovery (needs the restart)
|
|
168
173
|
|
|
169
|
-
- [
|
|
174
|
+
- [x] `memory_recover({})` — DONE 2026-10-11: report-only call returned a clean report (75
|
|
175
|
+
recoverable, 0 oversized), no error.
|
|
170
176
|
- [ ] `memory_recover({ limit: 1 })` — **prove it end to end on one** before spending on 76.
|
|
171
177
|
- [ ] Then the rest in batches of ~10. Cost is roughly 76-150 model calls plus consolidation,
|
|
172
178
|
against **52 MB** of logs. All 76 are under the 8 MB approval threshold, so nothing will
|
package/package.json
CHANGED
package/src/git.js
CHANGED
|
@@ -26,12 +26,18 @@ export class GitRunner {
|
|
|
26
26
|
*/
|
|
27
27
|
export function classifyGitResult(result) {
|
|
28
28
|
if (!result) return { ok: false, reason: 'git produced no result' }
|
|
29
|
-
|
|
29
|
+
// BOTH names, because the two halves disagreed and nobody noticed: the doc comment above the
|
|
30
|
+
// GitRunner constructor promises a run() result carrying `code`, so this read `result.code` -
|
|
31
|
+
// while the runner the plugin actually injects returns `exitCode`. Every successful git command
|
|
32
|
+
// was therefore classified as a failure, every pull and push was reported as failed, and sync had
|
|
33
|
+
// never once succeeded. A classifier that rejects success is worse than no classifier.
|
|
34
|
+
const code = result.exitCode ?? result.code
|
|
35
|
+
if (code === 0) return { ok: true }
|
|
30
36
|
const stdout = String(result.stdout ?? '')
|
|
31
37
|
const stderr = String(result.stderr ?? '')
|
|
32
38
|
const combined = stdout + ' ' + stderr
|
|
33
39
|
const conflicted = /conflict|automatic merge failed|needs merge|could not apply/i.test(combined)
|
|
34
|
-
const reason = (stderr.trim() || stdout.trim() || 'exit code ' + String(
|
|
40
|
+
const reason = (stderr.trim() || stdout.trim() || 'exit code ' + String(code)).slice(0, 400)
|
|
35
41
|
return { ok: false, conflicted, reason }
|
|
36
42
|
}
|
|
37
43
|
|
package/src/host.js
CHANGED
|
@@ -28,6 +28,19 @@ const MAP_PUBLISH_MS = 60 * 1000
|
|
|
28
28
|
const TASKS_BACKUP_MS = 30 * 60 * 1000
|
|
29
29
|
const IDLE_TICK_MS = 60_000
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Did the pull fail only because the remote has no such branch yet?
|
|
33
|
+
*
|
|
34
|
+
* The first sync of a store that has been committing to itself - which is every store that has ever
|
|
35
|
+
* been used - meets a remote with no refs at all. git says so in a few ways depending on version and
|
|
36
|
+
* transport, and none of them is a real failure: there is simply nothing to pull and everything to
|
|
37
|
+
* push.
|
|
38
|
+
*/
|
|
39
|
+
function isMissingRemoteBranch(result) {
|
|
40
|
+
const text = String((result && result.stdout) || '') + ' ' + String((result && result.stderr) || '')
|
|
41
|
+
return /couldn't find remote ref|could not find remote ref|no such ref|remote branch .* not found|unable to find remote ref/i.test(text)
|
|
42
|
+
}
|
|
43
|
+
|
|
31
44
|
function makeGitRunner(ctx) {
|
|
32
45
|
// The subprocess service is NOT resolvable during apply. cordis ctx.get() only returns an
|
|
33
46
|
// implementation whose PROVIDING FIBER IS ACTIVE (vendor/cordis reflect.ts _getImpl), and no
|
|
@@ -611,9 +624,27 @@ export function wireHost(ctx, store, cfg) {
|
|
|
611
624
|
catch (error) { ctx.logger.warn('dsh-memory: commit failed: ' + (error && error.message)) }
|
|
612
625
|
}
|
|
613
626
|
if (cfg.gitRemote && (force || scheduler.shouldSync())) {
|
|
627
|
+
// The origin is CONFIGURED, not necessarily INSTALLED. remoteAdd ran only on the fresh-repo
|
|
628
|
+
// path above, so a store that already had a .git - which every store that has ever committed
|
|
629
|
+
// does - went straight to `pull origin` with no origin at all and failed on 'no such remote',
|
|
630
|
+
// leaving memory.json and the profile config both claiming a remote git had never heard of.
|
|
631
|
+
const configured = await git.remoteGet(store.root).catch(() => null)
|
|
632
|
+
const installed = configured && configured.exitCode === 0 ? String(configured.stdout ?? '').trim() : ''
|
|
633
|
+
if (installed !== cfg.gitRemote) {
|
|
634
|
+
const applied = installed === ''
|
|
635
|
+
? await git.remoteAdd(store.root, cfg.gitRemote).catch(() => null)
|
|
636
|
+
: await git.remoteSet(store.root, cfg.gitRemote).catch(() => null)
|
|
637
|
+
if (!applied || applied.exitCode !== 0) {
|
|
638
|
+
return failSync('remote', { ok: false, reason: (applied && (applied.stderr || applied.stdout)) || 'could not set origin to ' + cfg.gitRemote }, committed)
|
|
639
|
+
}
|
|
640
|
+
}
|
|
614
641
|
const pulled = await git.pull(store.root, branch).catch((error) => ({ code: -1, stderr: String((error && error.message) || error) }))
|
|
615
642
|
const pullVerdict = classifyGitResult(pulled)
|
|
616
|
-
|
|
643
|
+
// An EMPTY remote has no branch to pull, and that is not a failure - it is the first push.
|
|
644
|
+
// The store has been committing to itself since it was created, so its very first sync meets a
|
|
645
|
+
// remote with no refs at all; failing there would mean a freshly configured origin could never
|
|
646
|
+
// be bootstrapped by the plugin, only by hand.
|
|
647
|
+
if (!pullVerdict.ok && !isMissingRemoteBranch(pulled)) return failSync('pull', pullVerdict, committed)
|
|
617
648
|
const pushed = await git.push(store.root, branch).catch((error) => ({ code: -1, stderr: String((error && error.message) || error) }))
|
|
618
649
|
const pushVerdict = classifyGitResult(pushed)
|
|
619
650
|
if (!pushVerdict.ok) return failSync('push', pushVerdict, committed)
|