peaks-loop 4.0.51 → 4.0.52
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/CHANGELOG.md +20 -0
- package/README-en.md +1 -1
- package/README.md +1 -1
- package/dist/cli/commands/baseline-commands.js +11 -1
- package/dist/cli/commands/codegraph-command-runtime.d.ts +28 -0
- package/dist/cli/commands/codegraph-command-runtime.js +72 -0
- package/dist/cli/commands/codegraph-commands.d.ts +2 -11
- package/dist/cli/commands/codegraph-commands.js +173 -228
- package/dist/cli/commands/codegraph-status-command.d.ts +22 -0
- package/dist/cli/commands/codegraph-status-command.js +299 -0
- package/dist/cli/commands/core/memory-command.js +6 -2
- package/dist/cli/commands/job-commands.js +121 -30
- package/dist/cli/commands/project-commands.js +13 -3
- package/dist/cli/commands/request-commands.js +19 -8
- package/dist/cli/commands/slice-commands.js +2 -2
- package/dist/services/artifacts/artifact-prerequisites.js +23 -1
- package/dist/services/codegraph/codegraph-autorefresh.d.ts +16 -0
- package/dist/services/codegraph/codegraph-autorefresh.js +51 -5
- package/dist/services/codegraph/codegraph-config-repair-writer.d.ts +88 -0
- package/dist/services/codegraph/codegraph-config-repair-writer.js +322 -0
- package/dist/services/codegraph/codegraph-exclude-integrity.d.ts +20 -2
- package/dist/services/codegraph/codegraph-exclude-integrity.js +24 -3
- package/dist/services/codegraph/codegraph-exclude-reconciler.d.ts +23 -2
- package/dist/services/codegraph/codegraph-exclude-reconciler.js +123 -12
- package/dist/services/codegraph/codegraph-exclude-repair.d.ts +109 -55
- package/dist/services/codegraph/codegraph-exclude-repair.js +249 -195
- package/dist/services/codegraph/codegraph-include-reconciler.d.ts +10 -0
- package/dist/services/codegraph/codegraph-include-reconciler.js +160 -0
- package/dist/services/codegraph/codegraph-index-integrity.d.ts +268 -0
- package/dist/services/codegraph/codegraph-index-integrity.js +471 -0
- package/dist/services/codegraph/codegraph-service.d.ts +54 -0
- package/dist/services/codegraph/codegraph-service.js +84 -1
- package/dist/services/doctor/doctor-service/checks/codegraph-exclude-integrity.js +19 -4
- package/dist/services/doctor/doctor-service/checks/codegraph-index-integrity.d.ts +54 -0
- package/dist/services/doctor/doctor-service/checks/codegraph-index-integrity.js +151 -0
- package/dist/services/doctor/doctor-service/checks/l3-orphan-sessions.js +10 -10
- package/dist/services/doctor/doctor-service/plugin-registry.js +2 -0
- package/dist/services/doctor/doctor-service/types.d.ts +25 -0
- package/dist/services/memory/project-memory-service/index/kind-dispatch.js +48 -13
- package/dist/services/memory/project-memory-service/index.d.ts +5 -3
- package/dist/services/memory/project-memory-service/index.js +2 -2
- package/dist/services/memory/project-memory-service/parsers/frontmatter.d.ts +15 -1
- package/dist/services/memory/project-memory-service/parsers/frontmatter.js +34 -6
- package/dist/services/memory/project-memory-service/parsers/markdown-pure.d.ts +27 -1
- package/dist/services/memory/project-memory-service/parsers/markdown-pure.js +92 -7
- package/dist/services/memory/project-memory-service/types.d.ts +86 -0
- package/dist/services/slice/slice-check-types.d.ts +1 -1
- package/dist/services/workspace/runtime-layout.d.ts +91 -0
- package/dist/services/workspace/runtime-layout.js +148 -0
- package/dist/services/workspace/workspace-claude-settings-materializer.js +14 -0
- package/package.json +6 -6
- package/scripts/clean-dist.mjs +15 -3
- package/scripts/sync-version.mjs +26 -4
- package/skills/bee/peaks-prd/SKILL.md +1 -1
- package/skills/bee/peaks-qa/references/qa-skill-presence.md +1 -1
- package/skills/bee/peaks-rd/references/skill-presence-and-title.md +1 -1
- package/skills/bee/peaks-sc/SKILL.md +1 -1
- package/skills/bee/peaks-txt/SKILL.md +3 -3
- package/skills/peaks-code/SKILL.md +1 -1
- package/skills/peaks-code/references/project-memory-loading.md +19 -1
- package/skills/peaks-code/references/step-11-memory-sediment.md +1 -1
|
@@ -30,8 +30,23 @@ import { inspectCodegraphExcludeIntegrity, isCodegraphExcludeConfigPresent } fro
|
|
|
30
30
|
const CHECK_ID = 'capability:codegraph-exclude-integrity';
|
|
31
31
|
/** How many rules / offending files the message names before eliding. */
|
|
32
32
|
const MAX_NAMED = 5;
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
/**
|
|
34
|
+
* 2026-09-17 — `projectRoot` is the doctor's resolved L3 root, NOT
|
|
35
|
+
* `process.cwd()`. Same defect as the sibling `codegraph-index-integrity`
|
|
36
|
+
* probe (see its note): with `projectRootResolver` injected, every other
|
|
37
|
+
* check inspected the caller's root while this one inspected the operator's
|
|
38
|
+
* checkout, so its verdict depended on which repository you ran the doctor
|
|
39
|
+
* in. This one is read-only — it reads `.codegraph/config.json` and
|
|
40
|
+
* `git ls-files`, never the sqlite index — so unlike its sibling it did not
|
|
41
|
+
* also materialise `-shm`/`-wal` sidecars; the wrong-root defect was the
|
|
42
|
+
* same either way.
|
|
43
|
+
*/
|
|
44
|
+
function defaultProbe(projectRoot) {
|
|
45
|
+
// An unresolved root is not a project — see the sibling probe's note: a
|
|
46
|
+
// relative lookup here would resolve against `cwd` and inspect the
|
|
47
|
+
// operator's checkout.
|
|
48
|
+
if (projectRoot.length === 0)
|
|
49
|
+
return null;
|
|
35
50
|
// No config → codegraph was never initialized here, so no exclude
|
|
36
51
|
// list is in play and there is nothing to report.
|
|
37
52
|
return isCodegraphExcludeConfigPresent(projectRoot)
|
|
@@ -48,8 +63,8 @@ function renderGapMessage(excludedTrackedCount, trackedSourceCount, rulesToRemov
|
|
|
48
63
|
const elidedFiles = violations.length > MAX_NAMED ? `; … (+${violations.length - MAX_NAMED})` : '';
|
|
49
64
|
return `codegraph index is incomplete: ${excludedTrackedCount} of ${trackedSourceCount} tracked source files are blocked by ${rulesToRemove.length} exclude rule(s) [${namedRules}${elidedRules}]. Blocked: ${namedFiles}${elidedFiles}. Run \`peaks codegraph repair-exclude --project <root>\` to drop them and rebuild the index.`;
|
|
50
65
|
}
|
|
51
|
-
function run({ options }) {
|
|
52
|
-
const probe = options.codegraphIntegrityProbe ?? defaultProbe;
|
|
66
|
+
function run({ options, resolvedL3Root }) {
|
|
67
|
+
const probe = options.codegraphIntegrityProbe ?? (() => defaultProbe(resolvedL3Root));
|
|
53
68
|
let report;
|
|
54
69
|
try {
|
|
55
70
|
report = probe();
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check: codegraph index integrity (`capability:codegraph-index-integrity`).
|
|
3
|
+
*
|
|
4
|
+
* The third codegraph check, and the one that stops `[OK]` from being a
|
|
5
|
+
* self-consistency assertion:
|
|
6
|
+
* - `capability:codegraph` answers "is upstream resolvable";
|
|
7
|
+
* - `capability:codegraph-exclude-integrity` answers "do the `exclude`
|
|
8
|
+
* rules drop tracked files";
|
|
9
|
+
* - this one answers "does the INDEX ITSELF cover the repository" —
|
|
10
|
+
* files the extractor supports that `include` never admitted, and rows
|
|
11
|
+
* the index still holds for paths that are gone.
|
|
12
|
+
*
|
|
13
|
+
* Why the exclude gate structurally cannot answer it: that gate runs the
|
|
14
|
+
* `include` filter FIRST and reconciles `exclude` against the survivors, so
|
|
15
|
+
* a file dropped by `include` is not "a tracked source file" as far as it is
|
|
16
|
+
* concerned; and it never reads the index at all, so dead rows are invisible
|
|
17
|
+
* to it. Both defects were present while `peaks codegraph status` printed
|
|
18
|
+
* `[OK] Index is up to date`.
|
|
19
|
+
*
|
|
20
|
+
* Read-only by construction — it consumes the same read-only inspector
|
|
21
|
+
* `peaks codegraph status` gates on. It never writes `.codegraph/config.json`
|
|
22
|
+
* and never invokes the upstream binary.
|
|
23
|
+
*
|
|
24
|
+
* Failure posture:
|
|
25
|
+
* - codegraph not initialized in the inspected root (no
|
|
26
|
+
* `.codegraph/codegraph.db`) → `ok: true`; there is no index to be
|
|
27
|
+
* incomplete or stale, and a fresh clone must not fail the doctor.
|
|
28
|
+
* - a confirmed gap on either axis → `ok: false`. ADVISORY by default
|
|
29
|
+
* (`severity: 'warning'`, the doctor exit code is left alone), and
|
|
30
|
+
* blocking (no severity tag) when the project opts in with
|
|
31
|
+
* `PEAKS_CODEGRAPH_INDEX_STRICT=1` — the user's option C decision.
|
|
32
|
+
* The finding is `ok: false` either way; only its severity moves.
|
|
33
|
+
* - could not evaluate (not a git work tree, missing/malformed config,
|
|
34
|
+
* unreadable index) → `ok: false, severity: 'warning'` so the doctor
|
|
35
|
+
* reports the blind spot without flipping the exit code on an
|
|
36
|
+
* unrelated failure. Kept non-blocking in BOTH modes: "could not
|
|
37
|
+
* evaluate" is not a finding about the project, and it is
|
|
38
|
+
* distinguishable from the `ok: true` "covers the repository"
|
|
39
|
+
* verdict by its `ok` value.
|
|
40
|
+
*
|
|
41
|
+
* R12-2 — the partial collapse that remains, stated rather than implied:
|
|
42
|
+
* this branch and the advisory `gap` branch below BOTH emit `ok: false`
|
|
43
|
+
* with `severity: 'warning'`, and `DoctorCheck` has no third field, so in
|
|
44
|
+
* the doctor envelope they are separated by message text alone. Not fixed,
|
|
45
|
+
* deliberately: the shape is legacy (`runDoctor` compatibility is a
|
|
46
|
+
* stated constraint), neither state is `ok: true` — so the invariant that
|
|
47
|
+
* a non-measurement may never read as "verified clean" holds — and in the
|
|
48
|
+
* mode where they are collapsed neither state moves the exit code.
|
|
49
|
+
* Under `PEAKS_CODEGRAPH_INDEX_STRICT=1` they ARE separated in the
|
|
50
|
+
* machine fields: the advisory `gap` branch drops its tag, this one keeps
|
|
51
|
+
* it. Pinned by the "(severity policy)" and "(unevaluable)" tests.
|
|
52
|
+
*/
|
|
53
|
+
import type { DoctorCheckPlugin } from '../types.js';
|
|
54
|
+
export declare const check: DoctorCheckPlugin;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check: codegraph index integrity (`capability:codegraph-index-integrity`).
|
|
3
|
+
*
|
|
4
|
+
* The third codegraph check, and the one that stops `[OK]` from being a
|
|
5
|
+
* self-consistency assertion:
|
|
6
|
+
* - `capability:codegraph` answers "is upstream resolvable";
|
|
7
|
+
* - `capability:codegraph-exclude-integrity` answers "do the `exclude`
|
|
8
|
+
* rules drop tracked files";
|
|
9
|
+
* - this one answers "does the INDEX ITSELF cover the repository" —
|
|
10
|
+
* files the extractor supports that `include` never admitted, and rows
|
|
11
|
+
* the index still holds for paths that are gone.
|
|
12
|
+
*
|
|
13
|
+
* Why the exclude gate structurally cannot answer it: that gate runs the
|
|
14
|
+
* `include` filter FIRST and reconciles `exclude` against the survivors, so
|
|
15
|
+
* a file dropped by `include` is not "a tracked source file" as far as it is
|
|
16
|
+
* concerned; and it never reads the index at all, so dead rows are invisible
|
|
17
|
+
* to it. Both defects were present while `peaks codegraph status` printed
|
|
18
|
+
* `[OK] Index is up to date`.
|
|
19
|
+
*
|
|
20
|
+
* Read-only by construction — it consumes the same read-only inspector
|
|
21
|
+
* `peaks codegraph status` gates on. It never writes `.codegraph/config.json`
|
|
22
|
+
* and never invokes the upstream binary.
|
|
23
|
+
*
|
|
24
|
+
* Failure posture:
|
|
25
|
+
* - codegraph not initialized in the inspected root (no
|
|
26
|
+
* `.codegraph/codegraph.db`) → `ok: true`; there is no index to be
|
|
27
|
+
* incomplete or stale, and a fresh clone must not fail the doctor.
|
|
28
|
+
* - a confirmed gap on either axis → `ok: false`. ADVISORY by default
|
|
29
|
+
* (`severity: 'warning'`, the doctor exit code is left alone), and
|
|
30
|
+
* blocking (no severity tag) when the project opts in with
|
|
31
|
+
* `PEAKS_CODEGRAPH_INDEX_STRICT=1` — the user's option C decision.
|
|
32
|
+
* The finding is `ok: false` either way; only its severity moves.
|
|
33
|
+
* - could not evaluate (not a git work tree, missing/malformed config,
|
|
34
|
+
* unreadable index) → `ok: false, severity: 'warning'` so the doctor
|
|
35
|
+
* reports the blind spot without flipping the exit code on an
|
|
36
|
+
* unrelated failure. Kept non-blocking in BOTH modes: "could not
|
|
37
|
+
* evaluate" is not a finding about the project, and it is
|
|
38
|
+
* distinguishable from the `ok: true` "covers the repository"
|
|
39
|
+
* verdict by its `ok` value.
|
|
40
|
+
*
|
|
41
|
+
* R12-2 — the partial collapse that remains, stated rather than implied:
|
|
42
|
+
* this branch and the advisory `gap` branch below BOTH emit `ok: false`
|
|
43
|
+
* with `severity: 'warning'`, and `DoctorCheck` has no third field, so in
|
|
44
|
+
* the doctor envelope they are separated by message text alone. Not fixed,
|
|
45
|
+
* deliberately: the shape is legacy (`runDoctor` compatibility is a
|
|
46
|
+
* stated constraint), neither state is `ok: true` — so the invariant that
|
|
47
|
+
* a non-measurement may never read as "verified clean" holds — and in the
|
|
48
|
+
* mode where they are collapsed neither state moves the exit code.
|
|
49
|
+
* Under `PEAKS_CODEGRAPH_INDEX_STRICT=1` they ARE separated in the
|
|
50
|
+
* machine fields: the advisory `gap` branch drops its tag, this one keeps
|
|
51
|
+
* it. Pinned by the "(severity policy)" and "(unevaluable)" tests.
|
|
52
|
+
*/
|
|
53
|
+
import { getErrorMessage } from 'peaks-loop-shared/result';
|
|
54
|
+
import { isCodegraphInitialized } from '../../../codegraph/codegraph-service.js';
|
|
55
|
+
import { CODEGRAPH_INDEX_STRICT_ENV_VAR, CODEGRAPH_REPAIR_INDEX_COMMAND, inspectCodegraphIndexIntegrity, isCodegraphIndexStrictMode } from '../../../codegraph/codegraph-index-integrity.js';
|
|
56
|
+
const CHECK_ID = 'capability:codegraph-index-integrity';
|
|
57
|
+
/** How many offending paths the message names per axis before eliding. */
|
|
58
|
+
const MAX_NAMED = 5;
|
|
59
|
+
function elide(paths) {
|
|
60
|
+
const named = paths.slice(0, MAX_NAMED).join(', ');
|
|
61
|
+
return paths.length > MAX_NAMED ? `${named}, … (+${paths.length - MAX_NAMED})` : named;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* 2026-09-17 — `projectRoot` is the doctor's resolved L3 root, NOT
|
|
65
|
+
* `process.cwd()`. It used to read `process.cwd()`, which is a different
|
|
66
|
+
* thing from the root the rest of the doctor was pointed at: a caller that
|
|
67
|
+
* injects `projectRootResolver` (e.g.
|
|
68
|
+
* `tests/unit/doctor/doctor-exit-code-warn-only.test.ts`) got every other
|
|
69
|
+
* check aimed at its temp root while THIS one opened the operator's real
|
|
70
|
+
* `.codegraph/codegraph.db`. Opening that database materialises its
|
|
71
|
+
* `-shm`/`-wal` sidecars, so a unit run mutated the operator's checkout and
|
|
72
|
+
* the check's result depended on which repository you happened to run it
|
|
73
|
+
* in. Every other L3 check reads `resolvedL3Root` from `DoctorContext`;
|
|
74
|
+
* this one now does too.
|
|
75
|
+
*/
|
|
76
|
+
function defaultProbe(projectRoot) {
|
|
77
|
+
// An unresolved root is not a project. It must NOT fall through to a
|
|
78
|
+
// relative lookup (`join('', '.codegraph', …)` resolves against `cwd`),
|
|
79
|
+
// because that fallback IS the defect this signature was changed to close:
|
|
80
|
+
// a caller that never resolved a root would silently inspect the operator's
|
|
81
|
+
// checkout. `runDoctor` always resolves one; only a hand-built test context
|
|
82
|
+
// can pass ''.
|
|
83
|
+
if (projectRoot.length === 0)
|
|
84
|
+
return null;
|
|
85
|
+
// No index → nothing to be incomplete or stale.
|
|
86
|
+
return isCodegraphInitialized(projectRoot) ? inspectCodegraphIndexIntegrity(projectRoot) : null;
|
|
87
|
+
}
|
|
88
|
+
function renderGapMessage(includeGap, admittedTrackedCount, trackedSourceCount, deadRows, indexedFileCount) {
|
|
89
|
+
const parts = [];
|
|
90
|
+
if (includeGap.length > 0) {
|
|
91
|
+
parts.push(`${includeGap.length} of ${trackedSourceCount} extractor-supported tracked file(s) are not admitted by the config's include globs (admitted: ${admittedTrackedCount}) [${elide(includeGap)}]`);
|
|
92
|
+
}
|
|
93
|
+
if (deadRows.length > 0) {
|
|
94
|
+
parts.push(`${deadRows.length} of ${indexedFileCount} indexed file(s) are gone from disk [${elide(deadRows)}]`);
|
|
95
|
+
}
|
|
96
|
+
return `codegraph index does not cover the repository: ${parts.join('; ')}.`;
|
|
97
|
+
}
|
|
98
|
+
// The remediation clause, in both modes. Naming the command is the
|
|
99
|
+
// slice-002 half of slice-001's design decision 4 (which deliberately named
|
|
100
|
+
// none while none existed); the command name is a shared constant, so this
|
|
101
|
+
// message cannot drift from the CLI surface it points at.
|
|
102
|
+
function remediation() {
|
|
103
|
+
return `Run \`${CODEGRAPH_REPAIR_INDEX_COMMAND}\` to add the missing include pattern(s) and rebuild the index without the stale rows.`;
|
|
104
|
+
}
|
|
105
|
+
function run({ options, resolvedL3Root }) {
|
|
106
|
+
const probe = options.codegraphIndexIntegrityProbe ?? (() => defaultProbe(resolvedL3Root));
|
|
107
|
+
const strict = isCodegraphIndexStrictMode();
|
|
108
|
+
let report;
|
|
109
|
+
try {
|
|
110
|
+
report = probe();
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
return [{
|
|
114
|
+
id: CHECK_ID,
|
|
115
|
+
ok: false,
|
|
116
|
+
severity: 'warning',
|
|
117
|
+
message: `codegraph index integrity could not be evaluated: ${getErrorMessage(error)}`
|
|
118
|
+
}];
|
|
119
|
+
}
|
|
120
|
+
if (report === null) {
|
|
121
|
+
return [{
|
|
122
|
+
id: CHECK_ID,
|
|
123
|
+
ok: true,
|
|
124
|
+
message: 'codegraph is not initialized in this project (no .codegraph/codegraph.db); there is no index to be incomplete or stale'
|
|
125
|
+
}];
|
|
126
|
+
}
|
|
127
|
+
if (!report.gap) {
|
|
128
|
+
return [{
|
|
129
|
+
id: CHECK_ID,
|
|
130
|
+
ok: true,
|
|
131
|
+
message: `codegraph index covers the repository (${report.admittedTrackedCount} extractor-supported tracked file(s) admitted, ${report.indexedFileCount} indexed row(s), none stale)`
|
|
132
|
+
}];
|
|
133
|
+
}
|
|
134
|
+
const gapMessage = renderGapMessage(report.includeGap, report.admittedTrackedCount, report.trackedSourceCount, report.deadRows, report.indexedFileCount);
|
|
135
|
+
// Option C: advisory by default, blocking on opt-in. `ok: false` in both
|
|
136
|
+
// modes — the finding is reported either way; only `severity` (and with
|
|
137
|
+
// it the doctor exit code) moves. The advisory suffix is the discovery
|
|
138
|
+
// path for the switch, so an operator who wants blocking is told how.
|
|
139
|
+
return strict
|
|
140
|
+
? [{ id: CHECK_ID, ok: false, message: `${gapMessage} ${remediation()}` }]
|
|
141
|
+
: [{
|
|
142
|
+
id: CHECK_ID,
|
|
143
|
+
ok: false,
|
|
144
|
+
severity: 'warning',
|
|
145
|
+
message: `${gapMessage} ${remediation()} Advisory: set ${CODEGRAPH_INDEX_STRICT_ENV_VAR}=1 to make this blocking.`
|
|
146
|
+
}];
|
|
147
|
+
}
|
|
148
|
+
export const check = {
|
|
149
|
+
name: 'codegraph-index-integrity',
|
|
150
|
+
run
|
|
151
|
+
};
|
|
@@ -18,16 +18,16 @@
|
|
|
18
18
|
import { existsSync, readdirSync } from 'node:fs';
|
|
19
19
|
import { join } from 'node:path';
|
|
20
20
|
import { getErrorMessage } from 'peaks-loop-shared/result';
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
21
|
+
import { RUNTIME_SYSTEM_SUBDIRS } from '../../../workspace/runtime-layout.js';
|
|
22
|
+
// The exclude-list moved to `src/services/workspace/runtime-layout.ts`.
|
|
23
|
+
// It is imported above, not re-declared here: the local literal
|
|
24
|
+
// (`new Set(['change'])`) had already drifted — `callers/` is a designed
|
|
25
|
+
// location written by `caller-binding-service.ts` and was never added, so
|
|
26
|
+
// this check reported `4 orphan session(s) …: callers, cli, unknown-sid, x`
|
|
27
|
+
// and `peaks doctor` exited 1 on a clean workspace, permanently.
|
|
28
|
+
// `tests/unit/workspace/runtime-layout-drift-guard.test.ts` now fails when
|
|
29
|
+
// the code writes a `.peaks/_runtime/` child the registry does not know, so
|
|
30
|
+
// the set can no longer drift silently.
|
|
31
31
|
function run({ resolvedL3Root, isValidSessionId }) {
|
|
32
32
|
try {
|
|
33
33
|
const runtimeDir = join(resolvedL3Root, '.peaks/_runtime');
|
|
@@ -43,6 +43,7 @@ import { check as statuslineInstall } from './checks/statusline-install.js';
|
|
|
43
43
|
import { check as statuslineRuntime } from './checks/statusline-runtime.js';
|
|
44
44
|
import { check as codegraphCapability } from './checks/codegraph-capability.js';
|
|
45
45
|
import { check as codegraphExcludeIntegrity } from './checks/codegraph-exclude-integrity.js';
|
|
46
|
+
import { check as codegraphIndexIntegrity } from './checks/codegraph-index-integrity.js';
|
|
46
47
|
import { check as distSourceVersion } from './checks/dist-source-version.js';
|
|
47
48
|
import { check as multiBinaryDrift } from './checks/multi-binary-drift.js';
|
|
48
49
|
import { check as workspaceLayout } from './checks/workspace-layout.js';
|
|
@@ -72,6 +73,7 @@ export const PLUGINS = [
|
|
|
72
73
|
statuslineRuntime, // id "statusline:runtime"
|
|
73
74
|
codegraphCapability, // id "capability:codegraph"
|
|
74
75
|
codegraphExcludeIntegrity, // id "capability:codegraph-exclude-integrity"
|
|
76
|
+
codegraphIndexIntegrity, // id "capability:codegraph-index-integrity"
|
|
75
77
|
distSourceVersion, // id "build:dist-version-matches-source"
|
|
76
78
|
multiBinaryDrift, // id "build:multi-binary-drift"
|
|
77
79
|
workspaceLayout, // id "build:workspace-layout-canonical"
|
|
@@ -94,6 +94,23 @@ export type CodegraphExcludeIntegrityProbe = {
|
|
|
94
94
|
readonly matchedRule: string;
|
|
95
95
|
}[];
|
|
96
96
|
};
|
|
97
|
+
/**
|
|
98
|
+
* Structural shape of the codegraph index-integrity report the
|
|
99
|
+
* `capability:codegraph-index-integrity` check gates on. Declared
|
|
100
|
+
* structurally (rather than imported from the codegraph service) to
|
|
101
|
+
* keep this type module dependency-free — the default probe returns a
|
|
102
|
+
* `CodegraphIndexIntegrityReport`, which is assignable here.
|
|
103
|
+
*/
|
|
104
|
+
export type CodegraphIndexIntegrityProbe = {
|
|
105
|
+
readonly gap: boolean;
|
|
106
|
+
readonly trackedSourceCount: number;
|
|
107
|
+
readonly admittedTrackedCount: number;
|
|
108
|
+
/** Class ① — extractor-supported tracked files `include` does not admit. */
|
|
109
|
+
readonly includeGap: readonly string[];
|
|
110
|
+
readonly indexedFileCount: number;
|
|
111
|
+
/** Class ② — index rows whose path is gone from disk. */
|
|
112
|
+
readonly deadRows: readonly string[];
|
|
113
|
+
};
|
|
97
114
|
export type DistVersionComparison = {
|
|
98
115
|
dist: string | null;
|
|
99
116
|
source: string;
|
|
@@ -256,6 +273,14 @@ export type DoctorOptions = {
|
|
|
256
273
|
* and reported as a non-blocking warning.
|
|
257
274
|
*/
|
|
258
275
|
codegraphIntegrityProbe?: () => CodegraphExcludeIntegrityProbe | null;
|
|
276
|
+
/**
|
|
277
|
+
* Optional override for the `capability:codegraph-index-integrity`
|
|
278
|
+
* check. Returns the report, or `null` when codegraph is not
|
|
279
|
+
* initialized in the inspected root (no index to inspect). When
|
|
280
|
+
* omitted, the check inspects `process.cwd()`. Throwing is allowed
|
|
281
|
+
* and reported as a non-blocking warning.
|
|
282
|
+
*/
|
|
283
|
+
codegraphIndexIntegrityProbe?: () => CodegraphIndexIntegrityProbe | null;
|
|
259
284
|
skillPresenceProbe?: () => DoctorSkillPresence | null;
|
|
260
285
|
skillPresenceFreshnessThresholdMs?: number;
|
|
261
286
|
statusLineInstalledProbe?: () => boolean;
|
|
@@ -27,7 +27,7 @@ import { copyFileSync, mkdirSync, readFileSync } from 'node:fs';
|
|
|
27
27
|
import { dirname, join, relative } from 'node:path';
|
|
28
28
|
import { isInsidePath, resolveInputPath, stablePath, stableRealPath } from '../../../../shared/path-utils.js';
|
|
29
29
|
import { renderMemoryFile, slugify } from '../parsers/frontmatter.js';
|
|
30
|
-
import {
|
|
30
|
+
import { extractStableProjectMemoriesWithDiagnostics, summarizeBackupResult, summarizeExtractResult } from '../parsers/markdown-pure.js';
|
|
31
31
|
import { assertInsideProject, assertSafeProjectMemoryDir, assertSafeSessionDir, normalizeRoot, realPathOrThrow } from '../store/paths.js';
|
|
32
32
|
import { assertSafeMemoryFileContent, writeNewFile } from '../store/atomic-write.js';
|
|
33
33
|
import { generateMemoryIndexFile, readStoredMemoryNames } from './ranking.js';
|
|
@@ -35,11 +35,18 @@ import { listMarkdownFiles } from './search.js';
|
|
|
35
35
|
export function createProjectMemoryExtractPlan(options) {
|
|
36
36
|
const projectRoot = normalizeRoot(options.projectRoot);
|
|
37
37
|
const primaryMemoryDir = assertSafeProjectMemoryDir(projectRoot);
|
|
38
|
-
const extractedMemories =
|
|
38
|
+
const extractedMemories = [];
|
|
39
|
+
const droppedBlocks = [];
|
|
40
|
+
for (const artifactPath of options.artifactPaths) {
|
|
39
41
|
const safeArtifactPath = assertInsideProject(artifactPath, projectRoot);
|
|
40
42
|
const relativeArtifactPath = relative(projectRoot, safeArtifactPath).replaceAll('\\', '/');
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
const extracted = extractStableProjectMemoriesWithDiagnostics(readFileSync(safeArtifactPath, 'utf8'), relativeArtifactPath);
|
|
44
|
+
// Push order + the sort below are the pre-existing ordering contract:
|
|
45
|
+
// artifacts in argument order, memories sorted by slug.
|
|
46
|
+
extractedMemories.push(...extracted.memories);
|
|
47
|
+
droppedBlocks.push(...extracted.dropped);
|
|
48
|
+
}
|
|
49
|
+
extractedMemories.sort((left, right) => slugify(left.title).localeCompare(slugify(right.title)));
|
|
43
50
|
const slugCounts = new Map();
|
|
44
51
|
for (const memory of extractedMemories) {
|
|
45
52
|
const slug = slugify(memory.title);
|
|
@@ -60,7 +67,8 @@ export function createProjectMemoryExtractPlan(options) {
|
|
|
60
67
|
primaryMemoryDir,
|
|
61
68
|
backupPolicy: 'project-memory-primary-artifact-backup',
|
|
62
69
|
extractedMemories,
|
|
63
|
-
plannedWrites
|
|
70
|
+
plannedWrites,
|
|
71
|
+
droppedBlocks
|
|
64
72
|
};
|
|
65
73
|
}
|
|
66
74
|
export function executeProjectMemoryExtract(options) {
|
|
@@ -177,22 +185,45 @@ export function extractSessionMemories(options) {
|
|
|
177
185
|
scannedFiles: 0,
|
|
178
186
|
extractedCount: 0,
|
|
179
187
|
writtenFiles: [],
|
|
180
|
-
updatedIndex: false
|
|
188
|
+
updatedIndex: false,
|
|
189
|
+
droppedBlocks: [],
|
|
190
|
+
scanFailures: []
|
|
181
191
|
};
|
|
182
192
|
}
|
|
183
193
|
throw error;
|
|
184
194
|
}
|
|
185
195
|
const scannedFiles = listMarkdownFiles(sessionDir, { maxDepth: 6, skipDotfiles: true });
|
|
186
196
|
const allExtracted = [];
|
|
197
|
+
// Symmetric with `createProjectMemoryExtractPlan`, which has reported the
|
|
198
|
+
// rejected blocks since M2. This path used to call the diagnostic-free
|
|
199
|
+
// projection, so a session handoff whose blocks were all malformed returned
|
|
200
|
+
// `extractedCount: 0` with nothing anywhere saying a block had been found.
|
|
201
|
+
const droppedBlocks = [];
|
|
202
|
+
const scanFailures = [];
|
|
187
203
|
for (const filePath of scannedFiles) {
|
|
204
|
+
const relativePath = relative(projectRoot, filePath).replaceAll('\\', '/');
|
|
188
205
|
try {
|
|
189
206
|
const content = readFileSync(filePath, 'utf8');
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
207
|
+
const extracted = extractStableProjectMemoriesWithDiagnostics(content, relativePath);
|
|
208
|
+
allExtracted.push(...extracted.memories);
|
|
209
|
+
droppedBlocks.push(...extracted.dropped);
|
|
193
210
|
}
|
|
194
|
-
catch
|
|
195
|
-
//
|
|
211
|
+
catch (error) {
|
|
212
|
+
// G2 resolution for this site: this was a bare `catch {}` carrying the
|
|
213
|
+
// repo-wide G2 grace marker that `scripts/lint/silent-warning-detector.mjs`
|
|
214
|
+
// reads (anti-pattern #1, `empty-catch`). The throw is no longer
|
|
215
|
+
// swallowed — the file is named in the same `warnings` channel as the
|
|
216
|
+
// block drops, which is what the grace period was deferring, so the
|
|
217
|
+
// marker is gone from this line. Still non-fatal: one unreadable artifact
|
|
218
|
+
// must not abort the scan of the rest, so the catch keeps its control
|
|
219
|
+
// flow and only gains a channel.
|
|
220
|
+
//
|
|
221
|
+
// `relativePath` is computed above the `try` so the catch can name the
|
|
222
|
+
// file. That is safe rather than convenient: `path.relative` is pure
|
|
223
|
+
// string arithmetic on two already-validated absolute path strings and
|
|
224
|
+
// does not throw for them, so hoisting it cannot turn a path-join problem
|
|
225
|
+
// into a fatal error that the old swallow would have absorbed.
|
|
226
|
+
scanFailures.push({ file: relativePath, detail: error instanceof Error ? error.message : String(error) });
|
|
196
227
|
}
|
|
197
228
|
}
|
|
198
229
|
if (allExtracted.length === 0) {
|
|
@@ -205,7 +236,9 @@ export function extractSessionMemories(options) {
|
|
|
205
236
|
scannedFiles: scannedFiles.length,
|
|
206
237
|
extractedCount: 0,
|
|
207
238
|
writtenFiles: [],
|
|
208
|
-
updatedIndex: false
|
|
239
|
+
updatedIndex: false,
|
|
240
|
+
droppedBlocks,
|
|
241
|
+
scanFailures
|
|
209
242
|
};
|
|
210
243
|
}
|
|
211
244
|
const slugCounts = new Map();
|
|
@@ -248,6 +281,8 @@ export function extractSessionMemories(options) {
|
|
|
248
281
|
scannedFiles: scannedFiles.length,
|
|
249
282
|
extractedCount: allExtracted.length,
|
|
250
283
|
writtenFiles,
|
|
251
|
-
updatedIndex: apply && writtenFiles.length > 0
|
|
284
|
+
updatedIndex: apply && writtenFiles.length > 0,
|
|
285
|
+
droppedBlocks,
|
|
286
|
+
scanFailures
|
|
252
287
|
};
|
|
253
288
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
export type { BackupPlanOptions, ExtractedProjectMemory, MemoryKindTier, ExtractPlanOptions, ExtractSessionMemoriesOptions, ExtractSessionMemoriesResult, MemoryIndex, MemoryIndexEntry, ProjectMemoryBackupPlan, ProjectMemoryBackupResult, ProjectMemoryBackupSummary, ProjectMemoryCopy, ProjectMemoryExtractPlan, ProjectMemoryExtractResult, ProjectMemoryExtractSummary, ProjectMemoryKind, ProjectMemoryReadResult, ProjectMemoryShowResult, ProjectMemoryWrite, StoredProjectMemory } from './types.js';
|
|
1
|
+
export type { BackupPlanOptions, ExtractedProjectMemory, MemoryBlockDrop, MemoryBlockDropReason, MemoryBlockParse, MemoryKindTier, ExtractPlanOptions, ExtractSessionMemoriesOptions, ExtractSessionMemoriesResult, MemoryIndex, MemoryIndexEntry, ProjectMemoryBackupPlan, ProjectMemoryBackupResult, ProjectMemoryBackupSummary, ProjectMemoryCopy, ProjectMemoryExtractPlan, ProjectMemoryExtractResult, ProjectMemoryExtractSummary, ProjectMemoryKind, ProjectMemoryReadResult, ProjectMemoryShowResult, ProjectMemoryWrite, StoredProjectMemory } from './types.js';
|
|
2
2
|
export { VALID_PROJECT_MEMORY_KINDS } from './parsers/frontmatter.js';
|
|
3
3
|
export { HOT_MEMORY_KINDS, MEMORY_KIND_TIER, PROJECT_MEMORY_KINDS, WARM_MEMORY_KINDS } from './types.js';
|
|
4
|
-
export { summarizeBackupResult, summarizeExtractResult, summarizeMemoryBody, extractStableProjectMemories, END_MARKER, START_MARKER } from './parsers/markdown-pure.js';
|
|
5
|
-
export {
|
|
4
|
+
export { describeMemoryBlockDrops, describeSessionScanFailures, summarizeBackupResult, summarizeExtractResult, summarizeMemoryBody, extractStableProjectMemories, extractStableProjectMemoriesWithDiagnostics, END_MARKER, START_MARKER } from './parsers/markdown-pure.js';
|
|
5
|
+
export type { SessionScanFailure } from './types.js';
|
|
6
|
+
export type { ExtractedMemoryBlocks } from './parsers/markdown-pure.js';
|
|
7
|
+
export { parseBlock, parseBlockResult, parseMemoryFrontmatter, parseStoredMemoryFile, renderMemoryFile, resolveMemoryKind, resolveMemoryName, slugify } from './parsers/frontmatter.js';
|
|
6
8
|
export type { MemoryKindResolution, MemoryKindSource, MemoryNameResolution, MemoryNameSource, ParsedMemoryFrontmatter } from './parsers/frontmatter.js';
|
|
7
9
|
export { assertInsideProject, assertSafeProjectMemoryDir, assertSafeSessionDir, normalizeRealRoot, normalizeRoot, realPathOrThrow, resolveProjectPath, safeRealpath } from './store/paths.js';
|
|
8
10
|
export { assertSafeMemory, assertSafeMemoryFileContent, hasSensitiveMemoryContent, writeNewFile } from './store/atomic-write.js';
|
|
@@ -14,9 +14,9 @@ export { VALID_PROJECT_MEMORY_KINDS } from './parsers/frontmatter.js';
|
|
|
14
14
|
// Canonical kind vocabulary + hot/warm tier map (slice E)
|
|
15
15
|
export { HOT_MEMORY_KINDS, MEMORY_KIND_TIER, PROJECT_MEMORY_KINDS, WARM_MEMORY_KINDS } from './types.js';
|
|
16
16
|
// Pure markdown helpers
|
|
17
|
-
export { summarizeBackupResult, summarizeExtractResult, summarizeMemoryBody, extractStableProjectMemories, END_MARKER, START_MARKER } from './parsers/markdown-pure.js';
|
|
17
|
+
export { describeMemoryBlockDrops, describeSessionScanFailures, summarizeBackupResult, summarizeExtractResult, summarizeMemoryBody, extractStableProjectMemories, extractStableProjectMemoriesWithDiagnostics, END_MARKER, START_MARKER } from './parsers/markdown-pure.js';
|
|
18
18
|
// Frontmatter parser + renderer
|
|
19
|
-
export { parseBlock, parseMemoryFrontmatter, parseStoredMemoryFile, renderMemoryFile, resolveMemoryKind, resolveMemoryName, slugify } from './parsers/frontmatter.js';
|
|
19
|
+
export { parseBlock, parseBlockResult, parseMemoryFrontmatter, parseStoredMemoryFile, renderMemoryFile, resolveMemoryKind, resolveMemoryName, slugify } from './parsers/frontmatter.js';
|
|
20
20
|
// Store: path safety + sensitive content
|
|
21
21
|
export { assertInsideProject, assertSafeProjectMemoryDir, assertSafeSessionDir, normalizeRealRoot, normalizeRoot, realPathOrThrow, resolveProjectPath, safeRealpath } from './store/paths.js';
|
|
22
22
|
export { assertSafeMemory, assertSafeMemoryFileContent, hasSensitiveMemoryContent, writeNewFile } from './store/atomic-write.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ExtractedProjectMemory, ProjectMemoryKind, StoredProjectMemory } from '../types.js';
|
|
1
|
+
import type { ExtractedProjectMemory, MemoryBlockParse, ProjectMemoryKind, StoredProjectMemory } from '../types.js';
|
|
2
2
|
/** Accepted-kind set, derived from the canonical `PROJECT_MEMORY_KINDS`
|
|
3
3
|
* tuple so the parser cannot drift from the union type / tier map. */
|
|
4
4
|
export declare const VALID_MEMORY_KINDS: ReadonlySet<ProjectMemoryKind>;
|
|
@@ -6,6 +6,20 @@ export declare const VALID_MEMORY_KINDS: ReadonlySet<ProjectMemoryKind>;
|
|
|
6
6
|
* set (CLI help text, `--kind` validation) without duplicating the literal. */
|
|
7
7
|
export declare const VALID_PROJECT_MEMORY_KINDS: readonly ProjectMemoryKind[];
|
|
8
8
|
export declare function slugify(title: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Single implementation of the extract-path block parse. Returns a
|
|
11
|
+
* discriminated result so the caller can say WHY a found block was rejected.
|
|
12
|
+
*
|
|
13
|
+
* The precondition order mirrors the original combined `if` exactly
|
|
14
|
+
* (separator → title → kind present → kind valid → body non-empty), so the
|
|
15
|
+
* accepted set is bit-for-bit what it always was; only the explanation is new.
|
|
16
|
+
*/
|
|
17
|
+
export declare function parseBlockResult(block: string, sourceArtifact: string): MemoryBlockParse;
|
|
18
|
+
/**
|
|
19
|
+
* `null`-on-failure projection of `parseBlockResult`. Kept because callers
|
|
20
|
+
* (and the parser's own contract) consume a plain nullable value; both views
|
|
21
|
+
* come from the one implementation above, so they cannot diverge.
|
|
22
|
+
*/
|
|
9
23
|
export declare function parseBlock(block: string, sourceArtifact: string): ExtractedProjectMemory | null;
|
|
10
24
|
export declare function renderMemoryFile(memory: ExtractedProjectMemory): string;
|
|
11
25
|
/**
|
|
@@ -31,11 +31,20 @@ export function slugify(title) {
|
|
|
31
31
|
const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
|
|
32
32
|
return slug.length > 0 ? slug : 'project-memory';
|
|
33
33
|
}
|
|
34
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Single implementation of the extract-path block parse. Returns a
|
|
36
|
+
* discriminated result so the caller can say WHY a found block was rejected.
|
|
37
|
+
*
|
|
38
|
+
* The precondition order mirrors the original combined `if` exactly
|
|
39
|
+
* (separator → title → kind present → kind valid → body non-empty), so the
|
|
40
|
+
* accepted set is bit-for-bit what it always was; only the explanation is new.
|
|
41
|
+
*/
|
|
42
|
+
export function parseBlockResult(block, sourceArtifact) {
|
|
35
43
|
const normalizedBlock = block.replace(/\r\n/g, '\n');
|
|
36
44
|
const separatorIndex = normalizedBlock.indexOf('\n---\n');
|
|
37
|
-
if (separatorIndex < 0)
|
|
38
|
-
return
|
|
45
|
+
if (separatorIndex < 0) {
|
|
46
|
+
return { ok: false, reason: 'missing-separator', detail: "block header is not followed by a '---' separator line" };
|
|
47
|
+
}
|
|
39
48
|
const header = normalizedBlock.slice(0, separatorIndex).trim();
|
|
40
49
|
const body = normalizedBlock.slice(separatorIndex + '\n---\n'.length).trim();
|
|
41
50
|
const fields = new Map();
|
|
@@ -49,9 +58,28 @@ export function parseBlock(block, sourceArtifact) {
|
|
|
49
58
|
}
|
|
50
59
|
const title = fields.get('title')?.trim();
|
|
51
60
|
const kind = fields.get('kind')?.trim();
|
|
52
|
-
if (!title
|
|
53
|
-
return
|
|
54
|
-
|
|
61
|
+
if (!title) {
|
|
62
|
+
return { ok: false, reason: 'missing-title', detail: "block header has no non-empty 'title:' field" };
|
|
63
|
+
}
|
|
64
|
+
if (!kind) {
|
|
65
|
+
return { ok: false, reason: 'missing-kind', detail: "block header has no non-empty 'kind:' field" };
|
|
66
|
+
}
|
|
67
|
+
if (!VALID_MEMORY_KINDS.has(kind)) {
|
|
68
|
+
return { ok: false, reason: 'unknown-kind', detail: `block declares kind '${kind}', which is not an accepted memory kind` };
|
|
69
|
+
}
|
|
70
|
+
if (body.length === 0) {
|
|
71
|
+
return { ok: false, reason: 'empty-body', detail: 'block body is empty' };
|
|
72
|
+
}
|
|
73
|
+
return { ok: true, memory: { title, kind, body, sourceArtifact } };
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* `null`-on-failure projection of `parseBlockResult`. Kept because callers
|
|
77
|
+
* (and the parser's own contract) consume a plain nullable value; both views
|
|
78
|
+
* come from the one implementation above, so they cannot diverge.
|
|
79
|
+
*/
|
|
80
|
+
export function parseBlock(block, sourceArtifact) {
|
|
81
|
+
const parsed = parseBlockResult(block, sourceArtifact);
|
|
82
|
+
return parsed.ok ? parsed.memory : null;
|
|
55
83
|
}
|
|
56
84
|
export function renderMemoryFile(memory) {
|
|
57
85
|
const name = slugify(memory.title);
|
|
@@ -1,7 +1,33 @@
|
|
|
1
|
-
import type { ExtractedProjectMemory, ProjectMemoryBackupResult, ProjectMemoryBackupSummary, ProjectMemoryExtractResult, ProjectMemoryExtractSummary } from '../types.js';
|
|
1
|
+
import type { ExtractedProjectMemory, MemoryBlockDrop, ProjectMemoryBackupResult, ProjectMemoryBackupSummary, ProjectMemoryExtractResult, ProjectMemoryExtractSummary, SessionScanFailure } from '../types.js';
|
|
2
2
|
export declare const START_MARKER = "<!-- peaks-memory:start -->";
|
|
3
3
|
export declare const END_MARKER = "<!-- peaks-memory:end -->";
|
|
4
4
|
export declare function summarizeMemoryBody(body: string): string;
|
|
5
|
+
/** Extracted memories plus the blocks that were found and rejected. */
|
|
6
|
+
export type ExtractedMemoryBlocks = {
|
|
7
|
+
memories: ExtractedProjectMemory[];
|
|
8
|
+
dropped: MemoryBlockDrop[];
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Same scan as `extractStableProjectMemories`, but also reports the blocks
|
|
12
|
+
* that were found between the markers and rejected by the parser.
|
|
13
|
+
*
|
|
14
|
+
* `extractStableProjectMemories` is the `.memories` projection of this, so the
|
|
15
|
+
* extracted set is identical by construction — the diagnostics cannot change
|
|
16
|
+
* which blocks are accepted.
|
|
17
|
+
*/
|
|
18
|
+
export declare function extractStableProjectMemoriesWithDiagnostics(content: string, sourceArtifact: string): ExtractedMemoryBlocks;
|
|
5
19
|
export declare function extractStableProjectMemories(content: string, sourceArtifact: string): ExtractedProjectMemory[];
|
|
20
|
+
/**
|
|
21
|
+
* Render one warning line per rejected block, for the CLI envelope's
|
|
22
|
+
* `warnings` channel. `unknown-kind` additionally names the accepted
|
|
23
|
+
* vocabulary, because that is the failure whose remedy is a value change.
|
|
24
|
+
*/
|
|
25
|
+
export declare function describeMemoryBlockDrops(dropped: ReadonlyArray<MemoryBlockDrop>): string[];
|
|
26
|
+
/**
|
|
27
|
+
* Render one warning line per session artifact that could not be read. The
|
|
28
|
+
* sibling of `describeMemoryBlockDrops`, for the failure one level coarser: a
|
|
29
|
+
* whole file that never yielded blocks at all.
|
|
30
|
+
*/
|
|
31
|
+
export declare function describeSessionScanFailures(failures: ReadonlyArray<SessionScanFailure>): string[];
|
|
6
32
|
export declare function summarizeExtractResult(result: ProjectMemoryExtractResult): ProjectMemoryExtractSummary;
|
|
7
33
|
export declare function summarizeBackupResult(result: ProjectMemoryBackupResult): ProjectMemoryBackupSummary;
|