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
|
@@ -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>;
|
|
@@ -0,0 +1,148 @@
|
|
|
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
|
+
* Reserved top-level dir for session-less command output.
|
|
52
|
+
*
|
|
53
|
+
* `peaks baseline audit` runs with no bound session (it is the only scorer
|
|
54
|
+
* that can run inside the secretless OIDC publish gate), so it has no
|
|
55
|
+
* `<sid>` to write under. It used to pass the literal `'cli'` as its
|
|
56
|
+
* sessionId, which made `.peaks/_runtime/cli/capability-audit/*.json` look
|
|
57
|
+
* exactly like a session dir that failed validation — `peaks doctor` has
|
|
58
|
+
* been red on this repository ever since. The name is underscore-prefixed to
|
|
59
|
+
* match the tree's existing convention for "machinery, not a session"
|
|
60
|
+
* (`_runtime`, `_sub_agents`) and to make it impossible to mistake for a
|
|
61
|
+
* session id.
|
|
62
|
+
*/
|
|
63
|
+
export const RUNTIME_SESSIONLESS_SCOPE = '_audit';
|
|
64
|
+
/**
|
|
65
|
+
* Every legitimate top-level entry under `.peaks/_runtime/`.
|
|
66
|
+
*
|
|
67
|
+
* This is the list `L3:l3-orphan-sessions` tolerates. Adding an entry that
|
|
68
|
+
* nothing writes is a red test (the drift guard's reverse direction); writing
|
|
69
|
+
* an entry that is not here is also a red test. Both directions are pinned in
|
|
70
|
+
* `tests/unit/workspace/runtime-layout-drift-guard.test.ts`.
|
|
71
|
+
*/
|
|
72
|
+
export const RUNTIME_SYSTEM_ENTRIES = [
|
|
73
|
+
{
|
|
74
|
+
name: 'change',
|
|
75
|
+
kind: 'dir',
|
|
76
|
+
purpose: 'change-id routing root for reviewable artifacts (workflow/artifact-paths.ts, prd/prd-blocks-checker.ts, workspace/reconcile-service.ts)'
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: 'callers',
|
|
80
|
+
kind: 'dir',
|
|
81
|
+
purpose: 'per-caller binding files `.peaks/_runtime/callers/<callerId>.json` (session/caller-binding-service.ts)'
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: 'benchmarks',
|
|
85
|
+
kind: 'dir',
|
|
86
|
+
purpose: '`peaks slice benchmark` artifacts `<rid>.benchmark.json` (cli/commands/slice-commands.ts)'
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: 'prd',
|
|
90
|
+
kind: 'dir',
|
|
91
|
+
purpose: 'read-only PRD artifact root `.peaks/_runtime/prd/requests/<rid>.md` (prd/prd-blocks-checker.ts); the writer lives outside this repo, the check tolerates it'
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: 'playwright-userdata',
|
|
95
|
+
kind: 'dir',
|
|
96
|
+
purpose: 'per-terminal browser profile dirs (cli/commands/playwright-commands.ts)'
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: 'playwright-sessions',
|
|
100
|
+
kind: 'dir',
|
|
101
|
+
purpose: 'playwright session records `<terminalId>.json` (cli/commands/playwright-commands.ts)'
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: 'test-cache',
|
|
105
|
+
kind: 'dir',
|
|
106
|
+
purpose: 'per-test fingerprint cache (cli/commands/test-commands.ts)'
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: 'sop-state',
|
|
110
|
+
kind: 'dir',
|
|
111
|
+
purpose: 'SOP state migrated from `.peaks/sop-state/` (cli/commands/workspace/reconcile-command.ts)'
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: RUNTIME_SESSIONLESS_SCOPE,
|
|
115
|
+
kind: 'dir',
|
|
116
|
+
purpose: 'capability-audit output for session-less runs `.peaks/_runtime/_audit/capability-audit/*.json` (cli/commands/baseline-commands.ts)'
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: 'session.json',
|
|
120
|
+
kind: 'file',
|
|
121
|
+
purpose: 'project-global session binding (session/session-binding-service.ts)'
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: 'active-skill.json',
|
|
125
|
+
kind: 'file',
|
|
126
|
+
purpose: 'active-skill presence marker (skills/skill-presence-service.ts)'
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: 'generated-artifacts.json',
|
|
130
|
+
kind: 'file',
|
|
131
|
+
purpose: 'stamp describing which generated artifacts exist on this machine, so a refresh can tell "stale" from "never generated" (services/workspace/generated-artifacts-stamp.ts)'
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: '.outer-session-cache.json',
|
|
135
|
+
kind: 'file',
|
|
136
|
+
purpose: 'outer (IDE) session cache for CLI sub-processes (cli/commands/outer-cache-commands.ts, session/session-binding-bridge.ts)'
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
name: '.rebuild-binding.lock',
|
|
140
|
+
kind: 'file',
|
|
141
|
+
purpose: 'binding-store rebuild lock (session/binding-store.ts)'
|
|
142
|
+
}
|
|
143
|
+
];
|
|
144
|
+
/**
|
|
145
|
+
* The set `L3:l3-orphan-sessions` filters with. Derived from the registry
|
|
146
|
+
* above so the check and the census cannot disagree about the same name.
|
|
147
|
+
*/
|
|
148
|
+
export const RUNTIME_SYSTEM_SUBDIRS = new Set(RUNTIME_SYSTEM_ENTRIES.filter((entry) => entry.kind === 'dir').map((entry) => entry.name));
|
|
@@ -163,6 +163,20 @@ const PEAKS_GITIGNORE_SNIPPET = [
|
|
|
163
163
|
'# Both patterns below are PROJECT-ROOT-relative, so this snippet must land',
|
|
164
164
|
'# in the root .gitignore — see `upsertPeaksGitignoreSnippet`.',
|
|
165
165
|
'.peaks/.claude-settings-template.json',
|
|
166
|
+
'# Codegraph rollback copy (A3, 2026-09-17). Every `peaks codegraph init` /',
|
|
167
|
+
'# `repair-exclude` / `repair-index` / pre-dispatch preflight / post-slice',
|
|
168
|
+
'# auto-refresh copies `.codegraph/config.json` here before rewriting it, so',
|
|
169
|
+
'# this file is LOCAL CHURN: a committed one is silently replaced by the next',
|
|
170
|
+
'# repair. Upstream\'s own `.codegraph/.gitignore` does not cover it (it names',
|
|
171
|
+
'# *.db / *.db-wal / *.db-shm, cache/, *.log and .dirty only) and `.codegraph/`',
|
|
172
|
+
'# is NOT ignored wholesale downstream, so without this line a consumer project',
|
|
173
|
+
'# commits a file peaks-loop rewrites under it.',
|
|
174
|
+
'#',
|
|
175
|
+
'# ONLY the backup, never `config.json` itself: upstream deliberately keeps',
|
|
176
|
+
'# that config committable (it is a project\'s include/exclude policy, and',
|
|
177
|
+
'# committing it is how a team shares it), so ignoring it would hide a file',
|
|
178
|
+
'# people mean to commit. A rollback copy is the opposite kind of file.',
|
|
179
|
+
'.codegraph/config.json.bak',
|
|
166
180
|
PEAKS_GITIGNORE_FOOTER,
|
|
167
181
|
''
|
|
168
182
|
].join('\n');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "peaks-loop",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.52",
|
|
4
4
|
"description": "Loop Engineering CLI — workflow primitive / loop guards / evaluators / slice orchestration",
|
|
5
5
|
"author": "SquabbyZ",
|
|
6
6
|
"keywords": [
|
|
@@ -102,10 +102,10 @@
|
|
|
102
102
|
"picomatch": "4.0.4",
|
|
103
103
|
"yaml": "^2.9.0",
|
|
104
104
|
"zod": "^4.4.3",
|
|
105
|
-
"peaks-loop-internal-runtime": "0.0.
|
|
106
|
-
"peaks-loop-mut": "0.1.
|
|
107
|
-
"peaks-loop-shared": "0.0.
|
|
108
|
-
"peaks-loop-shared-channel": "0.0.
|
|
105
|
+
"peaks-loop-internal-runtime": "0.0.37",
|
|
106
|
+
"peaks-loop-mut": "0.1.50",
|
|
107
|
+
"peaks-loop-shared": "0.0.86",
|
|
108
|
+
"peaks-loop-shared-channel": "0.0.54"
|
|
109
109
|
},
|
|
110
110
|
"devDependencies": {
|
|
111
111
|
"@changesets/cli": "2.31.1",
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
"vitest": "^4.1.10"
|
|
129
129
|
},
|
|
130
130
|
"scripts": {
|
|
131
|
-
"build": "node ./scripts/sync-version.mjs && node ./scripts/clean-dist.mjs && pnpm -r --filter \"./packages/*\" run build && tsc -p tsconfig.build.json && node ./scripts/copy-templates.mjs && node ./scripts/check-build-integrity.mjs",
|
|
131
|
+
"build": "node ./scripts/sync-version.mjs && node ./scripts/clean-dist.mjs && pnpm -r --filter \"./packages/*\" run build && tsc -p tsconfig.build.json && node ./scripts/copy-templates.mjs && node ./scripts/check-build-integrity.mjs && node ./scripts/write-dist-stamp.mjs",
|
|
132
132
|
"prepublish": "node ./scripts/sync-version.mjs",
|
|
133
133
|
"postinstall": "node ./scripts/install-skills.mjs",
|
|
134
134
|
"predev": "node ./scripts/sync-version.mjs",
|
package/scripts/clean-dist.mjs
CHANGED
|
@@ -10,9 +10,21 @@ const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
|
10
10
|
// packages/*/dist. Rationale: tsc's incremental-build cache
|
|
11
11
|
// compares files by mtime + size, and a stale dist that
|
|
12
12
|
// survived from a previous build can cause tsc to skip the
|
|
13
|
-
// emission of newly-added src/*.ts (Bug-04 lineage
|
|
14
|
-
//
|
|
15
|
-
//
|
|
13
|
+
// emission of newly-added src/*.ts (Bug-04 lineage).
|
|
14
|
+
//
|
|
15
|
+
// E2 (rid 2026-09-17-cli-output-and-stale-refs) — the citation that
|
|
16
|
+
// stood here named a regression pin on version.ts that was deleted
|
|
17
|
+
// in `f17aa377` ("delete 559 legacy unit tests"), and, like the one
|
|
18
|
+
// in `sync-version.mjs`, it was written without backticks so the
|
|
19
|
+
// citation-integrity guard could not see it. Corrected: the pin on
|
|
20
|
+
// the version.ts OUTPUT now lives in the lockstep tests (see
|
|
21
|
+
// `sync-version.mjs` for both names and for what they read).
|
|
22
|
+
//
|
|
23
|
+
// Recorded, not hidden: this wipe itself has no direct pin — no
|
|
24
|
+
// test executes this script, so nothing asserts that a stale
|
|
25
|
+
// packages/*/dist is gone before tsc runs. The `dist-freshness.mjs`
|
|
26
|
+
// module reasons FROM this wipe (it is why a dist mtime can be read
|
|
27
|
+
// as the build time) but does not test it.
|
|
16
28
|
//
|
|
17
29
|
// The wider wipe is safe because:
|
|
18
30
|
// - watch.mjs only watches src/, schemas/, skills/ — it
|
package/scripts/sync-version.mjs
CHANGED
|
@@ -76,8 +76,30 @@ for (const ext of ['js', 'd.ts', 'd.ts.map']) {
|
|
|
76
76
|
// regression-test pin for Bug-04; the load-bearing dist
|
|
77
77
|
// invalidation moved up to `clean-dist.mjs`, which wipes every
|
|
78
78
|
// packages/*/dist wholesale before any subpackage build runs.
|
|
79
|
-
// This narrow unlink stays because
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
//
|
|
79
|
+
// This narrow unlink stays because catching the Bug-04 lineage
|
|
80
|
+
// on its own is cheap insurance against future pipeline
|
|
81
|
+
// reorderings.
|
|
82
|
+
//
|
|
83
|
+
// E2 (rid 2026-09-17-cli-output-and-stale-refs) — the sentence
|
|
84
|
+
// that stood here named a unit test as covering this unlink. That
|
|
85
|
+
// test (file name only, deliberately not spelled as a path: it was
|
|
86
|
+
// deleted in `f17aa377`, "delete 559 legacy unit tests", and a
|
|
87
|
+
// path-shaped token would read as a live citation to the guard
|
|
88
|
+
// that now checks these comments) is `sync-version-invalidation.test.ts`
|
|
89
|
+
// — so the coverage this paragraph claimed has not existed since.
|
|
90
|
+
//
|
|
91
|
+
// What DOES cover this script today, verified rather than
|
|
92
|
+
// assumed: the version lockstep tests pin its two outputs to the
|
|
93
|
+
// root package.json#version — `CLI_VERSION` in
|
|
94
|
+
// packages/peaks-loop-shared/src/version.ts, and the
|
|
95
|
+
// `RUNTIME_VERSION` emit added in slice 2026-09-11 plus the built
|
|
96
|
+
// shared dist/version.js. Both read the artifacts, so they catch a
|
|
97
|
+
// silent no-op in either writer. They are indirect pins, and they
|
|
98
|
+
// are the reason a broken emit cannot ship.
|
|
99
|
+
//
|
|
100
|
+
// NOT COVERED, recorded rather than hidden: the unlink itself has
|
|
101
|
+
// no direct pin. No test executes this script, so nothing asserts
|
|
102
|
+
// that a stale packages/peaks-loop-shared/dist/version.js is
|
|
103
|
+
// actually removed. If you need the unlink pinned, that test has
|
|
104
|
+
// to be written — the one named here no longer exists.
|
|
83
105
|
|
|
@@ -107,7 +107,7 @@ Read persistent project memory via CLI (durable, LLM-authored memories):
|
|
|
107
107
|
peaks project memories --project <repo> --json
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
This returns durable memories from `.peaks/memory` — decisions, conventions, modules, and rules captured in past sessions. Filter with `--kind <decision|convention|module|rule|
|
|
110
|
+
This returns durable memories from `.peaks/memory` — decisions, conventions, modules, and rules captured in past sessions. Filter with `--kind <project|decision|convention|rule|reference|feedback|module|lesson|bug|investigation|technical-pattern|project-rule|design|handoff|session-handoff|project-todo|publish-closure|project-closure|slice-closure|slice-pilot-findings|sediment>`. (`.peaks/PROJECT.md` is a human-readable session timeline only.)
|
|
111
111
|
Then display: `Peaks-Loop Skill: peaks-prd | Peaks-Loop Gate: startup | Next: <one short action>`. Update with `peaks skill presence:set peaks-prd --project <repo> --mode <mode> --gate <gate>` when gates change. When the role's work ends, run `peaks skill presence:clear --project <repo>`.
|
|
112
112
|
|
|
113
113
|
## Responsibilities
|
|
@@ -18,5 +18,5 @@ Read persistent project memory via CLI (durable, LLM-authored memories):
|
|
|
18
18
|
peaks project memories --project <repo> --json
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
This returns durable memories from `.peaks/memory` — decisions, conventions, modules, and rules captured in past sessions. Filter with `--kind <decision|convention|module|rule|
|
|
21
|
+
This returns durable memories from `.peaks/memory` — decisions, conventions, modules, and rules captured in past sessions. Filter with `--kind <project|decision|convention|rule|reference|feedback|module|lesson|bug|investigation|technical-pattern|project-rule|design|handoff|session-handoff|project-todo|publish-closure|project-closure|slice-closure|slice-pilot-findings|sediment>`. (`.peaks/PROJECT.md` is a human-readable session timeline only.)
|
|
22
22
|
Then display: `Peaks-Loop Skill: peaks-qa | Peaks-Loop Gate: startup | Next: <one short action>`. Update with `peaks skill presence:set peaks-qa --project <repo> --mode <mode> --gate <gate>` when gates change. When the role's work ends, run `peaks skill presence:clear --project <repo>`.
|
|
@@ -18,5 +18,5 @@ Read persistent project memory via CLI (durable, LLM-authored memories):
|
|
|
18
18
|
peaks project memories --project <repo> --json
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
This returns durable memories from `.peaks/memory` — decisions, conventions, modules, and rules captured in past sessions. Filter with `--kind <decision|convention|module|rule|
|
|
21
|
+
This returns durable memories from `.peaks/memory` — decisions, conventions, modules, and rules captured in past sessions. Filter with `--kind <project|decision|convention|rule|reference|feedback|module|lesson|bug|investigation|technical-pattern|project-rule|design|handoff|session-handoff|project-todo|publish-closure|project-closure|slice-closure|slice-pilot-findings|sediment>`. (`.peaks/PROJECT.md` is a human-readable session timeline only.)
|
|
22
22
|
Then display: `Peaks-Loop Skill: peaks-rd | Peaks-Loop Gate: startup | Next: <one short action>`. Update with `peaks skill presence:set peaks-rd --project <repo> --mode <mode> --gate <gate>` when gates change. When the role's work ends, run `peaks skill presence:clear --project <repo>`.
|
|
@@ -75,7 +75,7 @@ Read persistent project memory via CLI (durable, LLM-authored memories):
|
|
|
75
75
|
peaks project memories --project <repo> --json
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
This returns durable memories from `.peaks/memory` — decisions, conventions, modules, and rules captured in past sessions. Filter with `--kind <decision|convention|module|rule|
|
|
78
|
+
This returns durable memories from `.peaks/memory` — decisions, conventions, modules, and rules captured in past sessions. Filter with `--kind <project|decision|convention|rule|reference|feedback|module|lesson|bug|investigation|technical-pattern|project-rule|design|handoff|session-handoff|project-todo|publish-closure|project-closure|slice-closure|slice-pilot-findings|sediment>`. (`.peaks/PROJECT.md` is a human-readable session timeline only.)
|
|
79
79
|
Then display: `Peaks-Loop Skill: peaks-sc | Peaks-Loop Gate: startup | Next: <one short action>`. Update with `peaks skill presence:set peaks-sc --project <repo> --mode <mode> --gate <gate>` when gates change. When the role's work ends, run `peaks skill presence:clear --project <repo>`.
|
|
80
80
|
|
|
81
81
|
## Responsibilities
|