peaks-loop 4.0.50 → 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 +34 -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/share-commands.js +85 -18
- 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/dispatch/sub-agent-dispatcher.d.ts +11 -30
- package/dist/services/dispatch/sub-agent-dispatcher.js +5 -48
- 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/ide/adapters/claude-code-adapter.js +0 -1
- package/dist/services/ide/adapters/codex-adapter.js +1 -2
- package/dist/services/ide/adapters/cursor-adapter.js +1 -2
- package/dist/services/ide/adapters/hermes-adapter.js +1 -2
- package/dist/services/ide/adapters/openclaw-adapter.js +1 -2
- package/dist/services/ide/adapters/qoder-adapter.js +1 -2
- package/dist/services/ide/adapters/tongyi-lingma-adapter.js +1 -2
- package/dist/services/ide/adapters/trae-adapter.js +1 -2
- package/dist/services/ide/adapters/zcode-adapter.js +0 -1
- package/dist/services/ide/ide-types.d.ts +0 -2
- 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-perf-audit/SKILL.md +2 -2
- package/skills/bee/peaks-perf-audit/references/audit-protocol.md +1 -1
- package/skills/bee/peaks-prd/SKILL.md +4 -4
- package/skills/bee/peaks-prd/references/prd-for-multi-pass.md +1 -1
- package/skills/bee/peaks-prd/references/workflow.md +1 -1
- package/skills/bee/peaks-qa/SKILL.md +6 -6
- package/skills/bee/peaks-qa/references/external-capability-guidance.md +1 -1
- package/skills/bee/peaks-qa/references/qa-fanout-contract.md +1 -1
- package/skills/bee/peaks-qa/references/qa-skill-presence.md +1 -1
- package/skills/bee/peaks-qa/references/reading-handoff-frontmatter.md +2 -2
- package/skills/bee/peaks-rd/SKILL.md +2 -2
- package/skills/bee/peaks-rd/references/code-reviewer-4dim-hint.md +1 -1
- package/skills/bee/peaks-rd/references/external-references.md +1 -1
- package/skills/bee/peaks-rd/references/mandatory-perf-baseline.md +1 -1
- package/skills/bee/peaks-rd/references/ocr-multilang-1.8.md +2 -2
- package/skills/bee/peaks-rd/references/parallel-review-fanout.md +2 -2
- package/skills/bee/peaks-rd/references/rd-fanout-contracts.md +11 -8
- package/skills/bee/peaks-rd/references/rd-runbook.md +1 -1
- package/skills/bee/peaks-rd/references/rd-sub-agent-dispatch.md +7 -7
- package/skills/bee/peaks-rd/references/rd-transition-gates.md +1 -1
- package/skills/bee/peaks-rd/references/reading-v2-slice-results.md +1 -1
- package/skills/bee/peaks-rd/references/skill-presence-and-title.md +1 -1
- package/skills/bee/peaks-rd/references/v2-12-fanout-collapse.md +7 -5
- package/skills/bee/peaks-rd/references/writing-handoff-frontmatter.md +3 -3
- package/skills/bee/peaks-reviewer/SKILL.md +1 -1
- package/skills/bee/peaks-sc/SKILL.md +1 -1
- package/skills/bee/peaks-security-audit/SKILL.md +3 -3
- package/skills/bee/peaks-security-audit/references/audit-protocol.md +1 -1
- package/skills/bee/peaks-txt/SKILL.md +3 -3
- package/skills/bee/peaks-txt/references/context-capsule.md +1 -1
- package/skills/bee/peaks-ui/SKILL.md +1 -1
- package/skills/peaks-audit/SKILL.md +1 -1
- package/skills/peaks-code/SKILL.md +9 -9
- package/skills/peaks-code/references/context-governance.md +1 -1
- package/skills/peaks-code/references/dag-orchestrator.md +3 -4
- package/skills/peaks-code/references/external-references.md +1 -1
- package/skills/peaks-code/references/external-skill-invocation.md +2 -2
- package/skills/peaks-code/references/fanout-mandatory.md +3 -3
- package/skills/peaks-code/references/frontend-only-mode.md +2 -2
- package/skills/peaks-code/references/gstack-integration.md +1 -1
- package/skills/peaks-code/references/micro-cycle.md +1 -1
- package/skills/peaks-code/references/periodic-checkpoint.md +2 -2
- package/skills/peaks-code/references/project-memory-loading.md +19 -1
- package/skills/peaks-code/references/project-scan-checklist.md +1 -1
- package/skills/peaks-code/references/resume-detection.md +1 -1
- package/skills/peaks-code/references/runbook.md +3 -3
- package/skills/peaks-code/references/session-overload-signal-index.md +2 -2
- package/skills/peaks-code/references/startup-sequence.md +16 -16
- package/skills/peaks-code/references/step-11-memory-sediment.md +3 -3
- package/skills/peaks-code/references/sub-agent-dispatch.md +7 -6
- package/skills/peaks-code/references/swarm-dispatch-contract.md +1 -1
- package/skills/peaks-code/references/workflow-gates-and-types.md +3 -3
- package/skills/peaks-code/references/worktree-governance.md +1 -1
- package/skills/peaks-final-review/SKILL.md +3 -3
- package/skills/peaks-ide/references/audit-log-helper.md +5 -4
- package/skills/peaks-resume/SKILL.md +1 -1
- package/skills/peaks-slice-decompose/SKILL.md +4 -4
- package/skills/peaks-slice-decompose/references/cross-pass-edge-interpretation.md +1 -1
- package/skills/peaks-slice-decompose/references/granularity-decision.md +1 -1
- package/skills/peaks-slice-decompose/references/v2-schema.md +2 -2
- package/skills/peaks-solo/SKILL.md +1 -2
|
@@ -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;
|
|
@@ -20,9 +20,32 @@
|
|
|
20
20
|
// No filesystem imports. Pure functions only — easy to unit-test.
|
|
21
21
|
// ---------------------------------------------------------------------------
|
|
22
22
|
import { assertSafeMemory } from '../store/atomic-write.js';
|
|
23
|
-
import {
|
|
23
|
+
import { parseBlockResult, slugify, VALID_MEMORY_KINDS } from './frontmatter.js';
|
|
24
24
|
export const START_MARKER = '<!-- peaks-memory:start -->';
|
|
25
25
|
export const END_MARKER = '<!-- peaks-memory:end -->';
|
|
26
|
+
/**
|
|
27
|
+
* A marker-shaped HTML comment: `<!--` then horizontal space then
|
|
28
|
+
* `peaks-memory:start` / `:end`, then anything up to the closing `-->`.
|
|
29
|
+
*
|
|
30
|
+
* This deliberately matches the exact markers TOO — the caller skips those by
|
|
31
|
+
* comparing against `START_MARKER` / `END_MARKER`, so the two literals stay the
|
|
32
|
+
* single source of truth instead of being re-spelled here.
|
|
33
|
+
*
|
|
34
|
+
* Anchoring on `peaks-memory:` immediately after the comment open is what keeps
|
|
35
|
+
* this from firing on prose: a comment that merely *mentions* the marker
|
|
36
|
+
* (`<!-- see the peaks-memory:start docs -->`) does not match, because the text
|
|
37
|
+
* after `<!--` is `see`, not `peaks-memory:`. Only an attempted marker matches.
|
|
38
|
+
*/
|
|
39
|
+
const MARKER_SHAPED_COMMENT = /<!--[ \t]*peaks-memory:(start|end)\b[^>]*-->/g;
|
|
40
|
+
/**
|
|
41
|
+
* Why a marker-shaped comment was not usable, as a warning line. Names both the
|
|
42
|
+
* text that was found and the literal the locator wanted, because the whole
|
|
43
|
+
* failure is "these two differ in a way nothing told you about".
|
|
44
|
+
*/
|
|
45
|
+
function describeUnrecognizedMarker(found) {
|
|
46
|
+
const wanted = found.includes(':end') ? END_MARKER : START_MARKER;
|
|
47
|
+
return `found ${JSON.stringify(found)}, which is not the exact marker ${JSON.stringify(wanted)} — the locator searches for that literal, so any block this opens is never found`;
|
|
48
|
+
}
|
|
26
49
|
// Length bounds for index entry descriptions. The numbers were chosen when
|
|
27
50
|
// summarizeMemoryBody was first introduced; locking them in as named
|
|
28
51
|
// constants is a doc-as-code move so the truncation rule is no longer
|
|
@@ -47,8 +70,21 @@ export function summarizeMemoryBody(body) {
|
|
|
47
70
|
}
|
|
48
71
|
return first.slice(0, MAX_DESCRIPTION_LENGTH - ELLIPSIS_RESERVE) + '...';
|
|
49
72
|
}
|
|
50
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Same scan as `extractStableProjectMemories`, but also reports the blocks
|
|
75
|
+
* that were found between the markers and rejected by the parser.
|
|
76
|
+
*
|
|
77
|
+
* `extractStableProjectMemories` is the `.memories` projection of this, so the
|
|
78
|
+
* extracted set is identical by construction — the diagnostics cannot change
|
|
79
|
+
* which blocks are accepted.
|
|
80
|
+
*/
|
|
81
|
+
export function extractStableProjectMemoriesWithDiagnostics(content, sourceArtifact) {
|
|
51
82
|
const memories = [];
|
|
83
|
+
// Kept as `{index, drop}` pairs so located-block drops and near-miss-marker
|
|
84
|
+
// drops can be reported in DOCUMENT order through one channel. The index is
|
|
85
|
+
// ordering metadata only — it never reaches the caller.
|
|
86
|
+
const drops = [];
|
|
87
|
+
const locatedRanges = [];
|
|
52
88
|
let searchStart = 0;
|
|
53
89
|
while (searchStart < content.length) {
|
|
54
90
|
const start = content.indexOf(START_MARKER, searchStart);
|
|
@@ -58,14 +94,63 @@ export function extractStableProjectMemories(content, sourceArtifact) {
|
|
|
58
94
|
const end = content.indexOf(END_MARKER, bodyStart);
|
|
59
95
|
if (end < 0)
|
|
60
96
|
break;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
97
|
+
locatedRanges.push([start, end + END_MARKER.length]);
|
|
98
|
+
const parsed = parseBlockResult(content.slice(bodyStart, end).trim(), sourceArtifact);
|
|
99
|
+
if (parsed.ok) {
|
|
100
|
+
assertSafeMemory(parsed.memory);
|
|
101
|
+
memories.push(parsed.memory);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
drops.push({ index: start, drop: { sourceArtifact, reason: parsed.reason, detail: parsed.detail } });
|
|
65
105
|
}
|
|
66
106
|
searchStart = end + END_MARKER.length;
|
|
67
107
|
}
|
|
68
|
-
|
|
108
|
+
// Near-miss markers. These are invisible to the loop above by construction —
|
|
109
|
+
// it navigates by exact `indexOf` — so before this pass an artifact written
|
|
110
|
+
// with, say, an attribute inside the marker reported `extractedCount: 0` with
|
|
111
|
+
// `warnings: []`, indistinguishable from an artifact that had no blocks at
|
|
112
|
+
// all. Nothing is extracted here: this pass only names what was not found.
|
|
113
|
+
//
|
|
114
|
+
// A near-miss INSIDE a located block's span is the block's own body text, not
|
|
115
|
+
// an attempted marker, so it is skipped rather than reported.
|
|
116
|
+
for (const match of content.matchAll(MARKER_SHAPED_COMMENT)) {
|
|
117
|
+
const found = match[0];
|
|
118
|
+
if (found === START_MARKER || found === END_MARKER)
|
|
119
|
+
continue;
|
|
120
|
+
const index = match.index ?? 0;
|
|
121
|
+
if (locatedRanges.some(([from, to]) => index >= from && index < to))
|
|
122
|
+
continue;
|
|
123
|
+
drops.push({ index, drop: { sourceArtifact, reason: 'unrecognized-marker', detail: describeUnrecognizedMarker(found) } });
|
|
124
|
+
}
|
|
125
|
+
drops.sort((left, right) => left.index - right.index);
|
|
126
|
+
return {
|
|
127
|
+
memories: memories.sort((left, right) => slugify(left.title).localeCompare(slugify(right.title))),
|
|
128
|
+
dropped: drops.map((entry) => entry.drop)
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
export function extractStableProjectMemories(content, sourceArtifact) {
|
|
132
|
+
return extractStableProjectMemoriesWithDiagnostics(content, sourceArtifact).memories;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Render one warning line per rejected block, for the CLI envelope's
|
|
136
|
+
* `warnings` channel. `unknown-kind` additionally names the accepted
|
|
137
|
+
* vocabulary, because that is the failure whose remedy is a value change.
|
|
138
|
+
*/
|
|
139
|
+
export function describeMemoryBlockDrops(dropped) {
|
|
140
|
+
return dropped.map((drop) => {
|
|
141
|
+
const hint = drop.reason === 'unknown-kind'
|
|
142
|
+
? ` Accepted kinds: ${[...VALID_MEMORY_KINDS].join(', ')}.`
|
|
143
|
+
: '';
|
|
144
|
+
return `Skipped a memory block in ${drop.sourceArtifact}: ${drop.detail}.${hint}`;
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Render one warning line per session artifact that could not be read. The
|
|
149
|
+
* sibling of `describeMemoryBlockDrops`, for the failure one level coarser: a
|
|
150
|
+
* whole file that never yielded blocks at all.
|
|
151
|
+
*/
|
|
152
|
+
export function describeSessionScanFailures(failures) {
|
|
153
|
+
return failures.map((failure) => `Could not read the session artifact ${failure.file}: ${failure.detail}.`);
|
|
69
154
|
}
|
|
70
155
|
export function summarizeExtractResult(result) {
|
|
71
156
|
return {
|
|
@@ -35,6 +35,50 @@ export type ExtractedProjectMemory = {
|
|
|
35
35
|
body: string;
|
|
36
36
|
sourceArtifact: string;
|
|
37
37
|
};
|
|
38
|
+
/**
|
|
39
|
+
* Why a `<!-- peaks-memory:start -->` block that was FOUND was not extracted.
|
|
40
|
+
*
|
|
41
|
+
* One value per precondition in `parseBlockResult` (the extract path's block
|
|
42
|
+
* parser), in evaluation order. Before this existed, every one of these
|
|
43
|
+
* conditions collapsed into a bare `null` and the caller reported nothing, so
|
|
44
|
+
* `extractedCount: N` was indistinguishable from "the block was never there".
|
|
45
|
+
* Naming the cause is the whole point: a memory block that disappears must say
|
|
46
|
+
* why it disappeared.
|
|
47
|
+
*
|
|
48
|
+
* Diagnostics only — this type does not change WHICH blocks are accepted.
|
|
49
|
+
*/
|
|
50
|
+
export type MemoryBlockDropReason = 'missing-separator' | 'missing-title' | 'missing-kind' | 'unknown-kind' | 'empty-body'
|
|
51
|
+
/**
|
|
52
|
+
* NOT a `parseBlockResult` precondition — this one comes from the scanner.
|
|
53
|
+
* A marker-shaped comment that is not the exact literal the locator searches
|
|
54
|
+
* for. The block it opens is therefore never found: it is not "rejected",
|
|
55
|
+
* it is invisible. Reporting it is the whole point, because nothing else in
|
|
56
|
+
* the pipeline can see it.
|
|
57
|
+
*/
|
|
58
|
+
| 'unrecognized-marker';
|
|
59
|
+
/** A found-but-not-extracted memory block, with the precondition that failed. */
|
|
60
|
+
export type MemoryBlockDrop = {
|
|
61
|
+
/** Artifact path the block was found in (project-relative when extracted). */
|
|
62
|
+
sourceArtifact: string;
|
|
63
|
+
reason: MemoryBlockDropReason;
|
|
64
|
+
/** Human-readable explanation of the failed precondition. */
|
|
65
|
+
detail: string;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Result of parsing one memory block, as a discriminated union.
|
|
69
|
+
*
|
|
70
|
+
* `parseBlock` is the `null`-on-failure projection of this; both are produced
|
|
71
|
+
* by the same single implementation so they cannot disagree about which blocks
|
|
72
|
+
* are accepted.
|
|
73
|
+
*/
|
|
74
|
+
export type MemoryBlockParse = {
|
|
75
|
+
ok: true;
|
|
76
|
+
memory: ExtractedProjectMemory;
|
|
77
|
+
} | {
|
|
78
|
+
ok: false;
|
|
79
|
+
reason: MemoryBlockDropReason;
|
|
80
|
+
detail: string;
|
|
81
|
+
};
|
|
38
82
|
export type ProjectMemoryWrite = {
|
|
39
83
|
memory: ExtractedProjectMemory;
|
|
40
84
|
filePath: string;
|
|
@@ -47,6 +91,12 @@ export type ProjectMemoryExtractPlan = {
|
|
|
47
91
|
backupPolicy: 'project-memory-primary-artifact-backup';
|
|
48
92
|
extractedMemories: ExtractedProjectMemory[];
|
|
49
93
|
plannedWrites: ProjectMemoryWrite[];
|
|
94
|
+
/**
|
|
95
|
+
* Blocks that were found between the markers but rejected by the parser.
|
|
96
|
+
* Reported to the user through the CLI envelope's `warnings` channel.
|
|
97
|
+
* Purely informational — this array does not influence extraction.
|
|
98
|
+
*/
|
|
99
|
+
droppedBlocks: MemoryBlockDrop[];
|
|
50
100
|
};
|
|
51
101
|
export type ProjectMemoryExtractResult = ProjectMemoryExtractPlan & {
|
|
52
102
|
writtenFiles: string[];
|
|
@@ -123,6 +173,22 @@ export type ExtractSessionMemoriesOptions = {
|
|
|
123
173
|
sessionId: string;
|
|
124
174
|
apply?: boolean;
|
|
125
175
|
};
|
|
176
|
+
/**
|
|
177
|
+
* A session artifact that could not be read at all, so its blocks were never
|
|
178
|
+
* even candidates.
|
|
179
|
+
*
|
|
180
|
+
* Distinct from `MemoryBlockDrop`: that one describes a BLOCK that was found and
|
|
181
|
+
* rejected, and needs the artifact to have been readable in the first place.
|
|
182
|
+
* Folding a whole-file read failure into it would make `sourceArtifact` mean two
|
|
183
|
+
* different things, so this rides its own field and is rendered by its own
|
|
184
|
+
* `describeSessionScanFailures`.
|
|
185
|
+
*/
|
|
186
|
+
export type SessionScanFailure = {
|
|
187
|
+
/** Project-relative path of the artifact that could not be read. */
|
|
188
|
+
file: string;
|
|
189
|
+
/** The underlying error message. Never invented; taken from the throw. */
|
|
190
|
+
detail: string;
|
|
191
|
+
};
|
|
126
192
|
export type ExtractSessionMemoriesResult = {
|
|
127
193
|
apply: boolean;
|
|
128
194
|
projectRoot: string;
|
|
@@ -133,6 +199,26 @@ export type ExtractSessionMemoriesResult = {
|
|
|
133
199
|
extractedCount: number;
|
|
134
200
|
writtenFiles: string[];
|
|
135
201
|
updatedIndex: boolean;
|
|
202
|
+
/**
|
|
203
|
+
* Blocks that were FOUND between the markers in this session's artifacts but
|
|
204
|
+
* rejected by the parser. Same diagnostic contract as
|
|
205
|
+
* `ProjectMemoryExtractPlan.droppedBlocks` — the sibling `peaks memory
|
|
206
|
+
* extract` path has carried this since M2, and this one silently dropped
|
|
207
|
+
* them, so `extractedCount: 1` out of three blocks was indistinguishable
|
|
208
|
+
* from "there was one block".
|
|
209
|
+
*
|
|
210
|
+
* Purely informational: this array does not influence which blocks are
|
|
211
|
+
* extracted, and it adds no field to the CLI's `data` payload — the reasons
|
|
212
|
+
* ride the existing envelope `warnings` channel.
|
|
213
|
+
*/
|
|
214
|
+
droppedBlocks: MemoryBlockDrop[];
|
|
215
|
+
/**
|
|
216
|
+
* Session artifacts that could not be read, so their blocks were never
|
|
217
|
+
* candidates. Previously swallowed by a bare `catch {}`; reported now through
|
|
218
|
+
* the same CLI `warnings` channel. Diagnostic only — an unreadable artifact
|
|
219
|
+
* is still not an error, and the scan is otherwise unchanged.
|
|
220
|
+
*/
|
|
221
|
+
scanFailures: SessionScanFailure[];
|
|
136
222
|
};
|
|
137
223
|
export type ProjectMemoryShowResult = {
|
|
138
224
|
projectRoot: string;
|
|
@@ -69,7 +69,7 @@ export type SliceCheckResult = {
|
|
|
69
69
|
};
|
|
70
70
|
export type SliceCheckOptions = {
|
|
71
71
|
projectRoot: string;
|
|
72
|
-
/**
|
|
72
|
+
/** REQUIRED. The `.peaks/_runtime/current-change` binding file is gone (slice 2026-06-29-change-id-root-removal); when omitted, slice check throws rather than guessing. */
|
|
73
73
|
rid?: string;
|
|
74
74
|
/**
|
|
75
75
|
* When true, re-run the 3-way review fan-out (peaks-rd's code-review +
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The canonical top-level layout of `.peaks/_runtime/`.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS MODULE EXISTS
|
|
5
|
+
*
|
|
6
|
+
* `.peaks/_runtime/` holds two populations whose names look identical:
|
|
7
|
+
*
|
|
8
|
+
* 1. SESSION DIRS — `<YYYY-MM-DD>-session-<hex>`, one per peaks session.
|
|
9
|
+
* Anything else that *looks* like a session id but is not one is a
|
|
10
|
+
* defect (a test that passed `--session-id x`, a typo, an old
|
|
11
|
+
* `unknown-sid` bucket).
|
|
12
|
+
* 2. SYSTEM ENTRIES — dirs and files the code itself writes there on
|
|
13
|
+
* purpose: `callers/`, `change/`, `benchmarks/`, `session.json`, …
|
|
14
|
+
*
|
|
15
|
+
* The doctor check `L3:l3-orphan-sessions` must flag (1) and tolerate (2),
|
|
16
|
+
* and the only thing that can tell them apart is a list of the system
|
|
17
|
+
* entries. That list used to live inside the check as a hand-maintained
|
|
18
|
+
* `new Set(['change'])` — and it had already drifted: `callers/` is a
|
|
19
|
+
* designed location (`caller-binding-service.ts` stores
|
|
20
|
+
* `.peaks/_runtime/callers/<callerId>.json`), was never added, and made
|
|
21
|
+
* `peaks doctor` exit non-zero on a clean workspace *permanently*
|
|
22
|
+
* (`4 orphan session(s) …: callers, cli, unknown-sid, x`). The check's own
|
|
23
|
+
* doc comment asked future maintainers to keep a *second* list — the prose
|
|
24
|
+
* `RUNTIME_SYSTEM_SUBDIRS_DOC` — in sync by hand. Prose cannot enforce
|
|
25
|
+
* itself, so the two lists diverged exactly as you would expect.
|
|
26
|
+
*
|
|
27
|
+
* WHY DERIVATION WAS REJECTED AND A REGISTRY IS THE SOURCE OF TRUTH
|
|
28
|
+
*
|
|
29
|
+
* A derivation would have to classify a bare name as "system" or "bogus"
|
|
30
|
+
* without a list. There is no signal to derive from: `callers` (system) and
|
|
31
|
+
* `x` (bogus) are the same shape — a lowercase word. The session-id regex
|
|
32
|
+
* separates them from `2026-09-16-session-5bcf09`, not from each other. So
|
|
33
|
+
* the alternatives were (a) keep a literal in the check, or (b) put the
|
|
34
|
+
* literal *and its consumers* in one place. This module is (b): the check
|
|
35
|
+
* imports the set instead of re-declaring it, and
|
|
36
|
+
* `tests/unit/workspace/runtime-layout-drift-guard.test.ts` parses `src/**`
|
|
37
|
+
* and fails when the code writes a `_runtime` child that is not registered
|
|
38
|
+
* here. The registry can no longer drift silently — it can only drift loudly,
|
|
39
|
+
* as a red test.
|
|
40
|
+
*
|
|
41
|
+
* The guard parses ASTs rather than grepping, because this repository
|
|
42
|
+
* documents the layout in prose constantly: `migrate-1-4-1-service.ts` has an
|
|
43
|
+
* array whose two adjacent string literals are `'_runtime', '_sub_agents'`
|
|
44
|
+
* (they are two *separate* SKIP entries), and `evidence-generator.ts`
|
|
45
|
+
* describes a past escape as `` `.peaks/_runtime/pwned.md` `` inside a
|
|
46
|
+
* comment. Both are false positives for a text scan and invisible to an AST
|
|
47
|
+
* walk. See `tests/unit/runtime/no-runtime-input-guard.test.ts` for the same
|
|
48
|
+
* decision made the same way.
|
|
49
|
+
*/
|
|
50
|
+
/**
|
|
51
|
+
* `dir` entries are scanned by `L3:l3-orphan-sessions` and must not be
|
|
52
|
+
* flagged. `file` entries are never flagged by that check — it only reads
|
|
53
|
+
* directories — and are registered so the drift guard can classify them and
|
|
54
|
+
* so this module stays an honest census of the tree rather than half of one.
|
|
55
|
+
*/
|
|
56
|
+
export type RuntimeEntryKind = 'dir' | 'file';
|
|
57
|
+
export interface RuntimeSystemEntry {
|
|
58
|
+
/** The exact top-level name under `.peaks/_runtime/`. */
|
|
59
|
+
readonly name: string;
|
|
60
|
+
readonly kind: RuntimeEntryKind;
|
|
61
|
+
/** What writes it and why it is legitimate. */
|
|
62
|
+
readonly purpose: string;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Reserved top-level dir for session-less command output.
|
|
66
|
+
*
|
|
67
|
+
* `peaks baseline audit` runs with no bound session (it is the only scorer
|
|
68
|
+
* that can run inside the secretless OIDC publish gate), so it has no
|
|
69
|
+
* `<sid>` to write under. It used to pass the literal `'cli'` as its
|
|
70
|
+
* sessionId, which made `.peaks/_runtime/cli/capability-audit/*.json` look
|
|
71
|
+
* exactly like a session dir that failed validation — `peaks doctor` has
|
|
72
|
+
* been red on this repository ever since. The name is underscore-prefixed to
|
|
73
|
+
* match the tree's existing convention for "machinery, not a session"
|
|
74
|
+
* (`_runtime`, `_sub_agents`) and to make it impossible to mistake for a
|
|
75
|
+
* session id.
|
|
76
|
+
*/
|
|
77
|
+
export declare const RUNTIME_SESSIONLESS_SCOPE = "_audit";
|
|
78
|
+
/**
|
|
79
|
+
* Every legitimate top-level entry under `.peaks/_runtime/`.
|
|
80
|
+
*
|
|
81
|
+
* This is the list `L3:l3-orphan-sessions` tolerates. Adding an entry that
|
|
82
|
+
* nothing writes is a red test (the drift guard's reverse direction); writing
|
|
83
|
+
* an entry that is not here is also a red test. Both directions are pinned in
|
|
84
|
+
* `tests/unit/workspace/runtime-layout-drift-guard.test.ts`.
|
|
85
|
+
*/
|
|
86
|
+
export declare const RUNTIME_SYSTEM_ENTRIES: readonly RuntimeSystemEntry[];
|
|
87
|
+
/**
|
|
88
|
+
* The set `L3:l3-orphan-sessions` filters with. Derived from the registry
|
|
89
|
+
* above so the check and the census cannot disagree about the same name.
|
|
90
|
+
*/
|
|
91
|
+
export declare const RUNTIME_SYSTEM_SUBDIRS: ReadonlySet<string>;
|