@try-works/dsh-memory 0.1.21 → 0.1.23
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 -15
- package/package.json +1 -1
- package/src/host.js +41 -6
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,11 +150,14 @@ the flag on did not change what the user sees.
|
|
|
147
150
|
|
|
148
151
|
### Blocked on a restart
|
|
149
152
|
|
|
150
|
-
- [ ] **
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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).
|
|
155
161
|
- [x] **Restart, then check the tasks layer — DONE 2026-10-09.** The restart confirmed it. See the
|
|
156
162
|
tasks-layer section above for the two log entries and the counts. It writes to ANOTHER plugin's storage through
|
|
157
163
|
that plugin's service, so it deserves a real observation rather than a passing suite. After
|
|
@@ -159,13 +165,14 @@ the flag on did not change what the user sees.
|
|
|
159
165
|
`pending.present: 2`. Then check `logs/memory.log` for a `tasks-backup` entry and a
|
|
160
166
|
`tasks-restore` entry: the first should say `refreshed: 2`, the second `present: 2, added: 0`.
|
|
161
167
|
An `added` above zero would mean de-duplication failed and it duplicated a task.
|
|
162
|
-
- [
|
|
163
|
-
|
|
164
|
-
|
|
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.
|
|
165
171
|
|
|
166
172
|
### Recovery (needs the restart)
|
|
167
173
|
|
|
168
|
-
- [
|
|
174
|
+
- [x] `memory_recover({})` — DONE 2026-10-11: report-only call returned a clean report (75
|
|
175
|
+
recoverable, 0 oversized), no error.
|
|
169
176
|
- [ ] `memory_recover({ limit: 1 })` — **prove it end to end on one** before spending on 76.
|
|
170
177
|
- [ ] Then the rest in batches of ~10. Cost is roughly 76-150 model calls plus consolidation,
|
|
171
178
|
against **52 MB** of logs. All 76 are under the 8 MB approval threshold, so nothing will
|
package/package.json
CHANGED
package/src/host.js
CHANGED
|
@@ -29,15 +29,28 @@ const TASKS_BACKUP_MS = 30 * 60 * 1000
|
|
|
29
29
|
const IDLE_TICK_MS = 60_000
|
|
30
30
|
|
|
31
31
|
function makeGitRunner(ctx) {
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
// The subprocess service is NOT resolvable during apply. cordis ctx.get() only returns an
|
|
33
|
+
// implementation whose PROVIDING FIBER IS ACTIVE (vendor/cordis reflect.ts _getImpl), and no
|
|
34
|
+
// fiber is active while the composition is still mounting. The first version probed here,
|
|
35
|
+
// once, at wire time - it cached undefined, returned null, and every git path was dead
|
|
36
|
+
// forever: zero sync events ever recorded, and the history hook threw "Cannot read
|
|
37
|
+
// properties of null (reading 'run')" every hour. The service exists by first USE (an agent
|
|
38
|
+
// pre-step, minutes after boot), so resolve lazily and keep re-probing until it appears.
|
|
39
|
+
let subprocess
|
|
40
|
+
const probe = () => {
|
|
41
|
+
const svc = ctx.get('subprocess')
|
|
42
|
+
if (svc && typeof svc.resolveExecutable === 'function' && typeof svc.spawn === 'function') subprocess = svc
|
|
43
|
+
return subprocess
|
|
44
|
+
}
|
|
34
45
|
let executablePromise
|
|
35
46
|
const run = async (argv, opts = {}) => {
|
|
36
|
-
|
|
47
|
+
const svc = probe()
|
|
48
|
+
if (!svc) return { exitCode: 127, stdout: '', stderr: 'subprocess service unavailable' }
|
|
49
|
+
if (!executablePromise) executablePromise = svc.resolveExecutable('git', undefined, undefined).catch(() => null)
|
|
37
50
|
const executable = await executablePromise
|
|
38
51
|
if (!executable) return { exitCode: 127, stdout: '', stderr: 'git unavailable' }
|
|
39
52
|
try {
|
|
40
|
-
const handle =
|
|
53
|
+
const handle = svc.spawn({
|
|
41
54
|
argv: [executable, ...argv],
|
|
42
55
|
cwd: opts.cwd,
|
|
43
56
|
stdio: { stdin: 'ignore', stdout: { maxBytes: 4 * 1024 * 1024 }, stderr: { maxBytes: 16 * 1024 } },
|
|
@@ -52,7 +65,9 @@ function makeGitRunner(ctx) {
|
|
|
52
65
|
return { exitCode: 1, stdout: '', stderr: String(error) }
|
|
53
66
|
}
|
|
54
67
|
}
|
|
55
|
-
|
|
68
|
+
const runner = new GitRunner(run)
|
|
69
|
+
runner.available = () => Boolean(probe())
|
|
70
|
+
return runner
|
|
56
71
|
}
|
|
57
72
|
|
|
58
73
|
function resolveTarget(agent) {
|
|
@@ -235,6 +250,9 @@ export function wireHost(ctx, store, cfg) {
|
|
|
235
250
|
if (!workspace) return { ok: false, reason: 'no workspace for this session', added: 0 }
|
|
236
251
|
const path = readAssociations(store.root)[workspace]?.path
|
|
237
252
|
if (!path) return { ok: false, reason: 'no local folder associated with ' + workspace, added: 0 }
|
|
253
|
+
// Report the honest reason instead of throwing: a hook that throws hourly because a service
|
|
254
|
+
// was probed too early must never look like a repository with no commits.
|
|
255
|
+
if (!git.available()) return { ok: false, reason: 'subprocess service unavailable', added: 0 }
|
|
238
256
|
return ingestHistory(store.root, workspace, path, (argv, opts) => git.run(argv, opts), { limit })
|
|
239
257
|
}
|
|
240
258
|
|
|
@@ -570,6 +588,9 @@ export function wireHost(ctx, store, cfg) {
|
|
|
570
588
|
}
|
|
571
589
|
|
|
572
590
|
async function runSync(force = false) {
|
|
591
|
+
// Not a git failure: no CONFLICT marker, no status record - the capability simply is not
|
|
592
|
+
// there yet. available() re-probes, so this is true only while the host is still mounting.
|
|
593
|
+
if (!git.available()) return { initialized: false, committed: false, synced: false, error: 'subprocess service unavailable' }
|
|
573
594
|
const branch = cfg.gitBranch || 'main'
|
|
574
595
|
const hasGit = existsSync(join(store.root, '.git'))
|
|
575
596
|
if (!hasGit) {
|
|
@@ -590,6 +611,20 @@ export function wireHost(ctx, store, cfg) {
|
|
|
590
611
|
catch (error) { ctx.logger.warn('dsh-memory: commit failed: ' + (error && error.message)) }
|
|
591
612
|
}
|
|
592
613
|
if (cfg.gitRemote && (force || scheduler.shouldSync())) {
|
|
614
|
+
// The origin is CONFIGURED, not necessarily INSTALLED. remoteAdd ran only on the fresh-repo
|
|
615
|
+
// path above, so a store that already had a .git - which every store that has ever committed
|
|
616
|
+
// does - went straight to `pull origin` with no origin at all and failed on 'no such remote',
|
|
617
|
+
// leaving memory.json and the profile config both claiming a remote git had never heard of.
|
|
618
|
+
const configured = await git.remoteGet(store.root).catch(() => null)
|
|
619
|
+
const installed = configured && configured.exitCode === 0 ? String(configured.stdout ?? '').trim() : ''
|
|
620
|
+
if (installed !== cfg.gitRemote) {
|
|
621
|
+
const applied = installed === ''
|
|
622
|
+
? await git.remoteAdd(store.root, cfg.gitRemote).catch(() => null)
|
|
623
|
+
: await git.remoteSet(store.root, cfg.gitRemote).catch(() => null)
|
|
624
|
+
if (!applied || applied.exitCode !== 0) {
|
|
625
|
+
return failSync('remote', { ok: false, reason: (applied && (applied.stderr || applied.stdout)) || 'could not set origin to ' + cfg.gitRemote }, committed)
|
|
626
|
+
}
|
|
627
|
+
}
|
|
593
628
|
const pulled = await git.pull(store.root, branch).catch((error) => ({ code: -1, stderr: String((error && error.message) || error) }))
|
|
594
629
|
const pullVerdict = classifyGitResult(pulled)
|
|
595
630
|
if (!pullVerdict.ok) return failSync('pull', pullVerdict, committed)
|
|
@@ -764,7 +799,7 @@ export function wireHost(ctx, store, cfg) {
|
|
|
764
799
|
}
|
|
765
800
|
|
|
766
801
|
function maybeSync() {
|
|
767
|
-
if (syncInFlight || !git) return
|
|
802
|
+
if (syncInFlight || !git.available()) return
|
|
768
803
|
if (!scheduler.shouldCommit() && !scheduler.shouldSync()) return
|
|
769
804
|
syncInFlight = true
|
|
770
805
|
// Sync wrote its outcome only to state/sync.json, so the log could not answer whether the
|