peaks-loop 4.0.35 → 4.0.36
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 +16 -0
- package/dist/cli/commands/code-runtime-commands.d.ts +5 -2
- package/dist/cli/commands/code-runtime-commands.js +57 -2
- package/dist/cli/commands/core/doctor-command.d.ts +8 -0
- package/dist/cli/commands/core/doctor-command.js +44 -2
- package/dist/cli/commands/core/memory-command.js +5 -1
- package/dist/cli/commands/dispatch-commands.js +15 -3
- package/dist/cli/commands/dispatch-from-dag.js +17 -0
- package/dist/cli/commands/memory-commands.d.ts +24 -0
- package/dist/cli/commands/memory-commands.js +77 -10
- package/dist/cli/commands/request-commands.d.ts +8 -0
- package/dist/cli/commands/request-commands.js +23 -2
- package/dist/cli/commands/sub-agent-commands.js +2 -0
- package/dist/cli/commands/wave-plan-commands.d.ts +24 -0
- package/dist/cli/commands/wave-plan-commands.js +93 -0
- package/dist/services/context/build-dispatch-system-prompt.d.ts +66 -9
- package/dist/services/context/build-dispatch-system-prompt.js +132 -17
- package/dist/services/context/context-audit.d.ts +100 -0
- package/dist/services/context/context-audit.js +322 -0
- package/dist/services/context/summary-view.d.ts +54 -0
- package/dist/services/context/summary-view.js +114 -0
- package/dist/services/dispatch/file-overlap-wave-planner.d.ts +70 -0
- package/dist/services/dispatch/file-overlap-wave-planner.js +119 -0
- package/dist/services/dispatch/session-capsule.d.ts +23 -0
- package/dist/services/dispatch/session-capsule.js +56 -0
- package/dist/services/dispatch/slice-dag.d.ts +9 -0
- package/dist/services/dispatch/slice-dag.js +9 -1
- package/dist/services/dispatch/test-tool-detection.d.ts +12 -1
- package/dist/services/dispatch/test-tool-detection.js +14 -13
- package/dist/services/ide/adapters/claude-code-adapter.d.ts +10 -0
- package/dist/services/ide/adapters/claude-code-adapter.js +20 -1
- package/dist/services/ide/ide-types.d.ts +15 -0
- package/dist/services/memory/project-memory-service/parsers/frontmatter.d.ts +5 -0
- package/dist/services/memory/project-memory-service/parsers/frontmatter.js +55 -5
- package/package.json +5 -5
- package/skills/bee/peaks-qa/SKILL.md +2 -0
- package/skills/bee/peaks-qa/references/qa-sub-agent-dispatch.md +12 -0
- package/skills/bee/peaks-rd/SKILL.md +2 -0
- package/skills/bee/peaks-rd/references/rd-sub-agent-dispatch.md +14 -0
- package/skills/bee/peaks-txt/SKILL.md +2 -0
- package/skills/bee/peaks-ui/SKILL.md +2 -0
- package/skills/peaks-code/SKILL.md +8 -0
- package/skills/peaks-code/references/context-governance.md +29 -0
- package/skills/peaks-doctor/SKILL.md +2 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Reserved batch id for the session capsule (matches the channel path pattern). */
|
|
2
|
+
export declare const SESSION_CAPSULE_BATCH_ID = "session-capsule";
|
|
3
|
+
/** Reserved key inside that channel. */
|
|
4
|
+
export declare const SESSION_CAPSULE_KEY = "orchestrator.capsule";
|
|
5
|
+
export interface SessionCapsuleRef {
|
|
6
|
+
readonly batchId: string;
|
|
7
|
+
readonly key: string;
|
|
8
|
+
/** Byte size of the published value (for the pointer line). */
|
|
9
|
+
readonly bytes: number;
|
|
10
|
+
/** ISO8601 timestamp of the last write. */
|
|
11
|
+
readonly updatedAt: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Read the session capsule, if one was published. Returns `null` when the
|
|
15
|
+
* channel is absent, empty, or unreadable — the dispatch prompt then
|
|
16
|
+
* renders neither the pointer nor the precedence line (byte-identical to
|
|
17
|
+
* the pre-slice shape).
|
|
18
|
+
*/
|
|
19
|
+
export declare function readSessionCapsule(opts: {
|
|
20
|
+
projectRoot: string;
|
|
21
|
+
sid: string;
|
|
22
|
+
rid: string;
|
|
23
|
+
}): SessionCapsuleRef | null;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slice 2026-09-10-dispatch-token-and-swarm §4 — session capsule reader.
|
|
3
|
+
*
|
|
4
|
+
* The orchestrator publishes already-known background ONCE per session
|
|
5
|
+
* through the existing G8.4 channel:
|
|
6
|
+
*
|
|
7
|
+
* peaks sub-agent share \
|
|
8
|
+
* --batch session-capsule \
|
|
9
|
+
* --key orchestrator.capsule \
|
|
10
|
+
* --value '{"rootCauses":[...],"decisions":[...],"fileMap":{...}}'
|
|
11
|
+
*
|
|
12
|
+
* Dispatch prompts then carry a one-line `shared-read` pointer instead of
|
|
13
|
+
* re-explaining that background in every task spec.
|
|
14
|
+
*
|
|
15
|
+
* QUALITY GUARD: the capsule is ADVISORY BACKGROUND ONLY. Nothing a
|
|
16
|
+
* sub-agent must ACT on may live only in the capsule — the task spec is
|
|
17
|
+
* authoritative and wins on conflict. The precedence sentence is rendered
|
|
18
|
+
* by `renderCapsulePointer` in build-dispatch-system-prompt.ts and is not
|
|
19
|
+
* optional when the pointer is emitted.
|
|
20
|
+
*
|
|
21
|
+
* This module only READS. Publishing is the orchestrator's job through the
|
|
22
|
+
* already-shipped `peaks sub-agent share` primitive — no new write path.
|
|
23
|
+
*/
|
|
24
|
+
import { readSharedChannel } from 'peaks-loop-shared-channel';
|
|
25
|
+
/** Reserved batch id for the session capsule (matches the channel path pattern). */
|
|
26
|
+
export const SESSION_CAPSULE_BATCH_ID = 'session-capsule';
|
|
27
|
+
/** Reserved key inside that channel. */
|
|
28
|
+
export const SESSION_CAPSULE_KEY = 'orchestrator.capsule';
|
|
29
|
+
/**
|
|
30
|
+
* Read the session capsule, if one was published. Returns `null` when the
|
|
31
|
+
* channel is absent, empty, or unreadable — the dispatch prompt then
|
|
32
|
+
* renders neither the pointer nor the precedence line (byte-identical to
|
|
33
|
+
* the pre-slice shape).
|
|
34
|
+
*/
|
|
35
|
+
export function readSessionCapsule(opts) {
|
|
36
|
+
try {
|
|
37
|
+
const channel = readSharedChannel({
|
|
38
|
+
projectRoot: opts.projectRoot,
|
|
39
|
+
sid: opts.sid,
|
|
40
|
+
rid: opts.rid,
|
|
41
|
+
batchId: SESSION_CAPSULE_BATCH_ID
|
|
42
|
+
});
|
|
43
|
+
const entry = channel.entries[SESSION_CAPSULE_KEY];
|
|
44
|
+
if (entry === undefined)
|
|
45
|
+
return null;
|
|
46
|
+
return {
|
|
47
|
+
batchId: SESSION_CAPSULE_BATCH_ID,
|
|
48
|
+
key: SESSION_CAPSULE_KEY,
|
|
49
|
+
bytes: entry.valueSize,
|
|
50
|
+
updatedAt: entry.at
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return null; // fail-soft: a missing capsule never blocks a dispatch
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -31,6 +31,15 @@ export interface SliceNode {
|
|
|
31
31
|
* (complex = user-attended, simple/trivial = overnight). Optional.
|
|
32
32
|
*/
|
|
33
33
|
readonly complexity?: SliceComplexity;
|
|
34
|
+
/**
|
|
35
|
+
* Slice 2026-09-10-dispatch-token-and-swarm §3: files this slice is
|
|
36
|
+
* expected to touch. When EVERY node of a topological level declares
|
|
37
|
+
* `files`, `--from-dag` emits a file-overlap wave plan (`firstLevelWaves`)
|
|
38
|
+
* so the LLM can fan the level out without serializing on a shared file.
|
|
39
|
+
* Optional and additive: absent → DAG hash and dispatch behavior are
|
|
40
|
+
* byte-identical to before this slice.
|
|
41
|
+
*/
|
|
42
|
+
readonly files?: readonly string[];
|
|
34
43
|
}
|
|
35
44
|
export interface DependsOn {
|
|
36
45
|
readonly from: string;
|
|
@@ -103,6 +103,11 @@ export function validateDag(dag) {
|
|
|
103
103
|
if (n.complexity !== undefined && !isSliceComplexity(n.complexity)) {
|
|
104
104
|
throw new InvalidSliceDagError(`node ${n.id} complexity must be one of ${SLICE_COMPLEXITIES.join('|')} when present`);
|
|
105
105
|
}
|
|
106
|
+
// Slice 2026-09-10 §3: optional file list. Only shape-checked when
|
|
107
|
+
// present so pre-existing DAGs stay valid.
|
|
108
|
+
if (n.files !== undefined && (!Array.isArray(n.files) || n.files.some((f) => typeof f !== 'string' || f.length === 0))) {
|
|
109
|
+
throw new InvalidSliceDagError(`node ${n.id} files must be an array of non-empty strings when present`);
|
|
110
|
+
}
|
|
106
111
|
}
|
|
107
112
|
// v2.15.0 follow-up — G12 defensive rule: foundation slice can only
|
|
108
113
|
// depend on another foundation slice. Business depending on foundation
|
|
@@ -219,7 +224,10 @@ export function serializeDag(dag) {
|
|
|
219
224
|
// (only when present, preserving hash stability for old DAGs).
|
|
220
225
|
...(n.foundation !== undefined ? { foundation: n.foundation } : {}),
|
|
221
226
|
...(n.upstreamSync !== undefined ? { upstreamSync: n.upstreamSync } : {}),
|
|
222
|
-
...(n.complexity !== undefined ? { complexity: n.complexity } : {})
|
|
227
|
+
...(n.complexity !== undefined ? { complexity: n.complexity } : {}),
|
|
228
|
+
// Slice 2026-09-10 §3: only present when declared, so the hash of a
|
|
229
|
+
// file-less DAG is unchanged.
|
|
230
|
+
...(n.files !== undefined ? { files: [...n.files] } : {})
|
|
223
231
|
}));
|
|
224
232
|
const edges = [...dag.edges]
|
|
225
233
|
.sort((a, b) => {
|
|
@@ -26,8 +26,19 @@
|
|
|
26
26
|
* "remove the redundancy" of a test-tool-detection instruction because
|
|
27
27
|
* the dispatch CLI always prepends it. This is a guarantee, not a
|
|
28
28
|
* suggestion.
|
|
29
|
+
*
|
|
30
|
+
* 2026-09-10-dispatch-block-d (Option D): ONE block for EVERY role. The
|
|
31
|
+
* runner-table EXAMPLES were dropped — they were never rules, and
|
|
32
|
+
* `package.json#scripts.test` is the source of truth at run time — while
|
|
33
|
+
* the one missing real rule is stated inline: PB-5, i.e. repo-defined
|
|
34
|
+
* `test` / `test:*` scripts are the human/LLM direct path and are NOT
|
|
35
|
+
* gated by the scope rule. The soft fallback ("only as a last resort, ask
|
|
36
|
+
* the user before assuming a runner") and the Windows-aware note on
|
|
37
|
+
* `peaks test <file>` are RETAINED — they are quality guidance, not
|
|
38
|
+
* examples. No role split remains, so every role receives a byte-identical
|
|
39
|
+
* block.
|
|
29
40
|
*/
|
|
30
|
-
export declare const TEST_TOOL_DETECTION_BLOCK = "## Test Tool Detection (mandatory)\n\
|
|
41
|
+
export declare const TEST_TOOL_DETECTION_BLOCK = "## Test Tool Detection (mandatory)\n\nRead `package.json#scripts.test` for the project's framework and use the project-local runner \u2014 do NOT invoke `npx <runner>`. If unsure, run `peaks test --json` first. Only as a last resort, ask the user before assuming a runner. `peaks test <file>` already resolves the local binary for you (Windows-aware).\n\n## Test Scope (mandatory)\n\nAny test command MUST be **scoped** to a single file or pattern; a bare `./node_modules/.bin/vitest run` is **refused** unless you prefix the explicit opt-in token `PEAKS_FULL_TEST=1` (CI / release verification only, never routine slice verification).\n\nRepo-defined `test` / `test:*` scripts are the human/LLM direct path and are NOT gated by this scope rule (PB-5).";
|
|
31
42
|
/**
|
|
32
43
|
* Pure helper that returns the block. Exists as a function (not just an
|
|
33
44
|
* exported constant) so future variants can take a runtime parameter
|
|
@@ -26,26 +26,27 @@
|
|
|
26
26
|
* "remove the redundancy" of a test-tool-detection instruction because
|
|
27
27
|
* the dispatch CLI always prepends it. This is a guarantee, not a
|
|
28
28
|
* suggestion.
|
|
29
|
+
*
|
|
30
|
+
* 2026-09-10-dispatch-block-d (Option D): ONE block for EVERY role. The
|
|
31
|
+
* runner-table EXAMPLES were dropped — they were never rules, and
|
|
32
|
+
* `package.json#scripts.test` is the source of truth at run time — while
|
|
33
|
+
* the one missing real rule is stated inline: PB-5, i.e. repo-defined
|
|
34
|
+
* `test` / `test:*` scripts are the human/LLM direct path and are NOT
|
|
35
|
+
* gated by the scope rule. The soft fallback ("only as a last resort, ask
|
|
36
|
+
* the user before assuming a runner") and the Windows-aware note on
|
|
37
|
+
* `peaks test <file>` are RETAINED — they are quality guidance, not
|
|
38
|
+
* examples. No role split remains, so every role receives a byte-identical
|
|
39
|
+
* block.
|
|
29
40
|
*/
|
|
30
41
|
export const TEST_TOOL_DETECTION_BLOCK = `## Test Tool Detection (mandatory)
|
|
31
42
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
- **vitest** → \`./node_modules/.bin/vitest run <file>\` (or \`pnpm test -- <file>\`)
|
|
35
|
-
- **jest** → \`./node_modules/.bin/jest <file>\` (or \`pnpm test -- <file>\`)
|
|
36
|
-
- **mocha** → \`./node_modules/.bin/mocha <file>\` (or \`pnpm test -- <file>\`)
|
|
43
|
+
Read \`package.json#scripts.test\` for the project's framework and use the project-local runner — do NOT invoke \`npx <runner>\`. If unsure, run \`peaks test --json\` first. Only as a last resort, ask the user before assuming a runner. \`peaks test <file>\` already resolves the local binary for you (Windows-aware).
|
|
37
44
|
|
|
38
45
|
## Test Scope (mandatory)
|
|
39
46
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
- **scoped** → \`./node_modules/.bin/vitest run tests/unit/foo.test.ts\` (or any explicit file/pattern)
|
|
43
|
-
- **intentional full run** → prefix with the explicit opt-in token \`PEAKS_FULL_TEST=1\` to override the scope gate. Use only for CI / release verification, never for routine verification during a slice.
|
|
44
|
-
- **refused** → bare \`./node_modules/.bin/vitest run\` (no argument after \`run\`) without the opt-in token.
|
|
45
|
-
|
|
46
|
-
\`pnpm test\` / \`pnpm test:unit\` / \`pnpm test:cli\` / \`pnpm test:integration\` and any repo-defined \`test*\` script remain the **human / LLM direct path** and are not gated by this rule (PB-5).
|
|
47
|
+
Any test command MUST be **scoped** to a single file or pattern; a bare \`./node_modules/.bin/vitest run\` is **refused** unless you prefix the explicit opt-in token \`PEAKS_FULL_TEST=1\` (CI / release verification only, never routine slice verification).
|
|
47
48
|
|
|
48
|
-
|
|
49
|
+
Repo-defined \`test\` / \`test:*\` scripts are the human/LLM direct path and are NOT gated by this scope rule (PB-5).`;
|
|
49
50
|
/**
|
|
50
51
|
* Pure helper that returns the block. Exists as a function (not just an
|
|
51
52
|
* exported constant) so future variants can take a runtime parameter
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import type { IdeAdapter } from '../ide-types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Resolve the absolute path of a Claude Code transcript jsonl by OUTER
|
|
4
|
+
* session id, searching `~/.claude/projects/**` recursively.
|
|
5
|
+
*
|
|
6
|
+
* Exported (slice 2026-09-10-context-audit-and-discipline, Slice A) so
|
|
7
|
+
* `peaks code context-audit` reuses THIS locator instead of re-implementing
|
|
8
|
+
* the recursive find. Returns `null` when the transcript does not exist —
|
|
9
|
+
* callers MUST treat that as "unavailable", never as an error.
|
|
10
|
+
*/
|
|
11
|
+
export declare function resolveClaudeTranscriptPath(outerSessionId: string, projectsDir?: string): string | null;
|
|
2
12
|
/**
|
|
3
13
|
* Resolve the currently-active Claude Code model id from a runtime env map.
|
|
4
14
|
* Returns the first non-empty `CLAUDE_CODE_MODEL_ENV_VARS` value (trimmed), or
|
|
@@ -90,6 +90,20 @@ function findTranscriptJsonl(projectsDir, outerSessionId) {
|
|
|
90
90
|
}
|
|
91
91
|
return null;
|
|
92
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Resolve the absolute path of a Claude Code transcript jsonl by OUTER
|
|
95
|
+
* session id, searching `~/.claude/projects/**` recursively.
|
|
96
|
+
*
|
|
97
|
+
* Exported (slice 2026-09-10-context-audit-and-discipline, Slice A) so
|
|
98
|
+
* `peaks code context-audit` reuses THIS locator instead of re-implementing
|
|
99
|
+
* the recursive find. Returns `null` when the transcript does not exist —
|
|
100
|
+
* callers MUST treat that as "unavailable", never as an error.
|
|
101
|
+
*/
|
|
102
|
+
export function resolveClaudeTranscriptPath(outerSessionId, projectsDir = join(homedir(), '.claude', 'projects')) {
|
|
103
|
+
if (typeof outerSessionId !== 'string' || outerSessionId.length === 0)
|
|
104
|
+
return null;
|
|
105
|
+
return findTranscriptJsonl(projectsDir, outerSessionId);
|
|
106
|
+
}
|
|
93
107
|
/** 1M-context window size in tokens (documented single choice: 1,000,000). */
|
|
94
108
|
const ONE_MILLION_CONTEXT_TOKENS = 1_000_000;
|
|
95
109
|
/** Safe-default (non-1M) context window size in tokens. */
|
|
@@ -483,7 +497,12 @@ export const CLAUDE_CODE_ADAPTER = {
|
|
|
483
497
|
compactCommand: 'claude --compact',
|
|
484
498
|
compactPathway: 'ide-native',
|
|
485
499
|
postCompactDetectCommand: 'peaks code auto-compact --json',
|
|
486
|
-
readContextPercentFallback
|
|
500
|
+
readContextPercentFallback,
|
|
501
|
+
// Slice 2026-09-10-context-audit-and-discipline (Slice A): the vendor
|
|
502
|
+
// layout knowledge (`~/.claude/projects/**/<outerSessionId>.jsonl`)
|
|
503
|
+
// stays here; `peaks code context-audit` resolves it through the
|
|
504
|
+
// adapter registry, never by naming this adapter directly.
|
|
505
|
+
resolveTranscriptPath: (outerSessionId) => resolveClaudeTranscriptPath(outerSessionId),
|
|
487
506
|
},
|
|
488
507
|
// Slice #011: standards profile. Claude Code reads its constitution at
|
|
489
508
|
// CLAUDE.md + module-level rules under .claude/rules/**. The values mirror
|
|
@@ -217,6 +217,21 @@ export interface IdeCompactProfile {
|
|
|
217
217
|
* Added in slice 2026-09-02-vendor-neutral-context-probe.
|
|
218
218
|
*/
|
|
219
219
|
readonly readContextPercentFallback?: (input: ContextPercentFallbackInput) => ContextPercentProbe | null;
|
|
220
|
+
/**
|
|
221
|
+
* Optional locator for the IDE's per-session transcript file (jsonl),
|
|
222
|
+
* keyed by the OUTER (harness) session id. `peaks code context-audit`
|
|
223
|
+
* (slice 2026-09-10-context-audit-and-discipline, Slice A) uses it to
|
|
224
|
+
* group the session's tool results by tool + short input key, so the
|
|
225
|
+
* generic audit service never learns any vendor's on-disk layout.
|
|
226
|
+
*
|
|
227
|
+
* Returns the absolute path, or `null` when the transcript does not
|
|
228
|
+
* exist. Adapters MUST NOT throw on a missing file — the audit treats
|
|
229
|
+
* `null` as `available: false` and continues.
|
|
230
|
+
*
|
|
231
|
+
* Adapters that do not opt in simply omit the field; the audit then
|
|
232
|
+
* reports `transcript-locator-unavailable`.
|
|
233
|
+
*/
|
|
234
|
+
readonly resolveTranscriptPath?: (outerSessionId: string) => string | null;
|
|
220
235
|
}
|
|
221
236
|
/**
|
|
222
237
|
* Input the generic `readContextPercent` reader passes to an adapter's
|
|
@@ -55,6 +55,11 @@ export declare function resolveMemoryKind(content: string): MemoryKindResolution
|
|
|
55
55
|
* `parseStoredMemoryFile` (read path) and the reindex / ingest / doctor
|
|
56
56
|
* classifiers consume this so there is exactly one kind-resolution rule
|
|
57
57
|
* in the codebase.
|
|
58
|
+
*
|
|
59
|
+
* Tolerates a leading run of HTML-comment lines (e.g. the
|
|
60
|
+
* `<!-- peaks-memory:start -->` sediment marker) before the opening `---`
|
|
61
|
+
* fence. The closing fence is still required and body extraction is
|
|
62
|
+
* unchanged: the body is the text after the closing fence.
|
|
58
63
|
*/
|
|
59
64
|
export declare function parseMemoryFrontmatter(content: string): ParsedMemoryFrontmatter;
|
|
60
65
|
/** Which frontmatter field (or the filename) supplied a memory's name. */
|
|
@@ -10,7 +10,10 @@
|
|
|
10
10
|
//
|
|
11
11
|
// 2. `parseStoredMemoryFile` — read-path side. Files in `.peaks/memory/`
|
|
12
12
|
// are stored as standard YAML frontmatter (name / description /
|
|
13
|
-
// metadata.type / metadata.sourceArtifact) followed by the body.
|
|
13
|
+
// metadata.type / metadata.sourceArtifact) followed by the body. A file
|
|
14
|
+
// may also open with HTML-comment lines (the `<!-- peaks-memory:start -->`
|
|
15
|
+
// sediment marker) before the frontmatter; `parseMemoryFrontmatter`
|
|
16
|
+
// skips that leading comment run before looking for the `---` fence.
|
|
14
17
|
//
|
|
15
18
|
// Both parsers share the `VALID_MEMORY_KINDS` allow-list (derived from the
|
|
16
19
|
// canonical `PROJECT_MEMORY_KINDS` tuple in `../types.ts`) and the
|
|
@@ -83,23 +86,70 @@ export function resolveMemoryKind(content) {
|
|
|
83
86
|
const parsed = parseMemoryFrontmatter(content);
|
|
84
87
|
return parsed.kind;
|
|
85
88
|
}
|
|
89
|
+
/** Start of an HTML comment line, allowing leading horizontal whitespace. */
|
|
90
|
+
const LEADING_COMMENT_OPEN = /^[ \t]*<!--/;
|
|
91
|
+
/**
|
|
92
|
+
* Length of the leading run of blank lines and standalone HTML-comment lines.
|
|
93
|
+
*
|
|
94
|
+
* A stored memory may be written with the documented sediment marker
|
|
95
|
+
* (`<!-- peaks-memory:start -->`) — or any HTML comment — BEFORE its YAML
|
|
96
|
+
* frontmatter. This helper reports how much of the file to skip so the fence
|
|
97
|
+
* can still be found. At least one comment line must be present: a file that
|
|
98
|
+
* merely starts with blank lines is not treated as marker-prefixed, so the
|
|
99
|
+
* pre-existing (fence-at-byte-0) behaviour is preserved exactly.
|
|
100
|
+
*/
|
|
101
|
+
function leadingCommentPrefixLength(normalized) {
|
|
102
|
+
let offset = 0;
|
|
103
|
+
let sawComment = false;
|
|
104
|
+
for (;;) {
|
|
105
|
+
const rest = normalized.slice(offset);
|
|
106
|
+
const blank = /^[ \t]*\n/.exec(rest);
|
|
107
|
+
if (blank !== null) {
|
|
108
|
+
offset += blank[0].length;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const open = LEADING_COMMENT_OPEN.exec(rest);
|
|
112
|
+
if (open === null)
|
|
113
|
+
break;
|
|
114
|
+
const closeIndex = rest.indexOf('-->', open[0].length);
|
|
115
|
+
if (closeIndex < 0)
|
|
116
|
+
break;
|
|
117
|
+
const afterClose = closeIndex + '-->'.length;
|
|
118
|
+
const lineEnd = rest.indexOf('\n', afterClose);
|
|
119
|
+
if (lineEnd < 0)
|
|
120
|
+
break;
|
|
121
|
+
// Only a whole comment line counts; trailing prose after `-->` means the
|
|
122
|
+
// file does not open with a comment block.
|
|
123
|
+
if (rest.slice(afterClose, lineEnd).trim() !== '')
|
|
124
|
+
break;
|
|
125
|
+
offset += lineEnd + 1;
|
|
126
|
+
sawComment = true;
|
|
127
|
+
}
|
|
128
|
+
return sawComment ? offset : 0;
|
|
129
|
+
}
|
|
86
130
|
/**
|
|
87
131
|
* Single parse surface for stored memory frontmatter. Both
|
|
88
132
|
* `parseStoredMemoryFile` (read path) and the reindex / ingest / doctor
|
|
89
133
|
* classifiers consume this so there is exactly one kind-resolution rule
|
|
90
134
|
* in the codebase.
|
|
135
|
+
*
|
|
136
|
+
* Tolerates a leading run of HTML-comment lines (e.g. the
|
|
137
|
+
* `<!-- peaks-memory:start -->` sediment marker) before the opening `---`
|
|
138
|
+
* fence. The closing fence is still required and body extraction is
|
|
139
|
+
* unchanged: the body is the text after the closing fence.
|
|
91
140
|
*/
|
|
92
141
|
export function parseMemoryFrontmatter(content) {
|
|
93
142
|
const normalized = content.replace(/\r\n/g, '\n');
|
|
94
|
-
|
|
143
|
+
const head = normalized.slice(leadingCommentPrefixLength(normalized));
|
|
144
|
+
if (!head.startsWith('---\n')) {
|
|
95
145
|
return { hasFrontmatter: false, kind: { kind: null, source: 'none', rawKind: null }, frontmatter: '', body: normalized.trim() };
|
|
96
146
|
}
|
|
97
|
-
const endIndex =
|
|
147
|
+
const endIndex = head.indexOf('\n---\n', 4);
|
|
98
148
|
if (endIndex < 0) {
|
|
99
149
|
return { hasFrontmatter: false, kind: { kind: null, source: 'none', rawKind: null }, frontmatter: '', body: normalized.trim() };
|
|
100
150
|
}
|
|
101
|
-
const frontmatter =
|
|
102
|
-
const body =
|
|
151
|
+
const frontmatter = head.slice(4, endIndex);
|
|
152
|
+
const body = head.slice(endIndex + '\n---\n'.length).trim();
|
|
103
153
|
let name;
|
|
104
154
|
let titleField;
|
|
105
155
|
let description;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "peaks-loop",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.36",
|
|
4
4
|
"description": "Loop Engineering CLI — workflow primitive / loop guards / evaluators / slice orchestration",
|
|
5
5
|
"author": "SquabbyZ",
|
|
6
6
|
"keywords": [
|
|
@@ -101,10 +101,10 @@
|
|
|
101
101
|
"fzf": "^0.5.2",
|
|
102
102
|
"yaml": "^2.9.0",
|
|
103
103
|
"zod": "^4.4.3",
|
|
104
|
-
"peaks-loop-internal-runtime": "0.0.
|
|
105
|
-
"peaks-loop-
|
|
106
|
-
"peaks-loop-shared": "0.0.
|
|
107
|
-
"peaks-loop-
|
|
104
|
+
"peaks-loop-internal-runtime": "0.0.21",
|
|
105
|
+
"peaks-loop-shared-channel": "0.0.38",
|
|
106
|
+
"peaks-loop-shared": "0.0.70",
|
|
107
|
+
"peaks-loop-mut": "0.1.34"
|
|
108
108
|
},
|
|
109
109
|
"devDependencies": {
|
|
110
110
|
"@changesets/cli": "2.31.1",
|
|
@@ -210,6 +210,8 @@ Do not own product scope or implementation. Do not modify runtime configuration.
|
|
|
210
210
|
|
|
211
211
|
QA sub-agents (qa / qa-business / qa-perf / qa-security) follow the same G7 metadata-only + G8.6 share protocol as RD. Detailed: `skills/peaks-code/references/context-governance.md`.
|
|
212
212
|
|
|
213
|
+
**Large tool output (> 2 KB) MUST NOT be dumped into the orchestrator's context** — use `--summary` (`peaks doctor --summary`, `peaks memory list --summary`, `peaks request list --summary`) or write to a file and `Read` selectively. Rationale (measured, session 2026-09-07-session-245530): full-envelope dumps cost ≈ 40K tokens of a 68%-full 1M window. `--summary` is an additive view — no information is removed. → `skills/peaks-code/references/context-governance.md` §G0.
|
|
214
|
+
|
|
213
215
|
→ see `references/qa-context-governance.md` for the full G7 / G8.6 / G9 protocol + QA sub-agent prompt template.
|
|
214
216
|
|
|
215
217
|
## References
|
|
@@ -59,6 +59,18 @@ What the sub-agent **MUST** still do:
|
|
|
59
59
|
|
|
60
60
|
If `--type` is `docs` or `chore`, return `{"status":"skipped","reason":"type=<type>"}` and exit — there is no acceptance surface to plan tests for.
|
|
61
61
|
|
|
62
|
+
## Final report cap (mandatory, slice 2026-09-10-context-audit-and-discipline)
|
|
63
|
+
|
|
64
|
+
Your FINAL report to the parent MUST be **≤ 40 lines and ≤ 2 KB**. Write any longer detail into the artifact file you already own; the parent can `Read` that file for the full detail, so nothing is lost. The report itself MUST still carry:
|
|
65
|
+
|
|
66
|
+
- changed files (one line each)
|
|
67
|
+
- the exact commands you ran
|
|
68
|
+
- pass/fail counts
|
|
69
|
+
- tsc status
|
|
70
|
+
- any blocker
|
|
71
|
+
|
|
72
|
+
Do NOT paste file contents, full tool output, or logs into the report. **Rationale (measured, session 2026-09-07-session-245530):** 20 sub-agent final reports ≈ 60 KB ≈ 15K tokens of the orchestrator's window — the report is an index into the artifact, not a copy of it. This rule is also machine-injected into every dispatch prompt (`REPORT_CAP_BLOCK` in `src/services/context/build-dispatch-system-prompt.ts`); this doc and that constant MUST stay in lockstep.
|
|
73
|
+
|
|
62
74
|
## Test Tool Detection (mandatory)
|
|
63
75
|
|
|
64
76
|
The dispatch CLI (`peaks sub-agent dispatch`) automatically prepends a Test Tool Detection block to every sub-agent prompt — telling the sub-agent to read `package.json#scripts.test` first and use the project-local runner (`./node_modules/.bin/<runner>` or `pnpm test -- <file>`). NEVER use `npx <runner>`. This rule is machine-injected, not a prompt ritual — every sub-agent gets it including rd/qa/ui/txt/sc.
|
|
@@ -239,6 +239,8 @@ The main RD loop MUST call `peaks job karpathy-cost-check` after every `peaks re
|
|
|
239
239
|
|
|
240
240
|
RD sub-agent prompt template MUST include the G7 path convention + G8.6 share protocol. Detailed protocol: `skills/peaks-code/references/context-governance.md`.
|
|
241
241
|
|
|
242
|
+
**Large tool output (> 2 KB) MUST NOT be dumped into the orchestrator's context** — use `--summary` (`peaks memory reindex --summary`, `peaks memory list --summary`, `peaks doctor --summary`, `peaks request list --summary`) or write to a file and `Read` selectively. Rationale (measured, session 2026-09-07-session-245530): 4 full `reindex --json` dumps ≈ 40K tokens of a 68%-full 1M window. `--summary` is an additive view — no information is removed. → `skills/peaks-code/references/context-governance.md` §G0.
|
|
243
|
+
|
|
242
244
|
→ see `references/rd-context-governance.md` for the full G7 / G8.6 / G9 protocol + RD sub-agent prompt template.
|
|
243
245
|
|
|
244
246
|
## Sub-stages (Plan 3 — strategic + tactical split)
|
|
@@ -113,6 +113,20 @@ Any RD/QA/SC sub-agent dispatched by `peaks sub-agent dispatch --from-dag` (or b
|
|
|
113
113
|
|
|
114
114
|
---
|
|
115
115
|
|
|
116
|
+
## Final report cap (mandatory, slice 2026-09-10-context-audit-and-discipline)
|
|
117
|
+
|
|
118
|
+
Your FINAL report to the parent MUST be **≤ 40 lines and ≤ 2 KB**. Write any longer detail into the artifact file you already own (registered via `--write-artifact`); the parent can `Read` that file for the full detail, so nothing is lost. The report itself MUST still carry:
|
|
119
|
+
|
|
120
|
+
- changed files (one line each)
|
|
121
|
+
- the exact commands you ran
|
|
122
|
+
- pass/fail counts
|
|
123
|
+
- tsc status
|
|
124
|
+
- any blocker
|
|
125
|
+
|
|
126
|
+
Do NOT paste file contents, full tool output, or logs into the report. **Rationale (measured, session 2026-09-07-session-245530):** 20 sub-agent final reports ≈ 60 KB ≈ 15K tokens of the orchestrator's window — the report is an index into the artifact, not a copy of it. This rule is also machine-injected into every dispatch prompt (`REPORT_CAP_BLOCK` in `src/services/context/build-dispatch-system-prompt.ts`); this doc and that constant MUST stay in lockstep.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
116
130
|
## Test Tool Detection (mandatory)
|
|
117
131
|
|
|
118
132
|
The dispatch CLI (`peaks sub-agent dispatch`) automatically prepends a Test Tool Detection block to every sub-agent prompt — telling the sub-agent to read `package.json#scripts.test` first and use the project-local runner (`./node_modules/.bin/<runner>` or `pnpm test -- <file>`). NEVER use `npx <runner>`. This rule is machine-injected, not a prompt ritual — every sub-agent gets it including rd/qa/ui/txt/sc.
|
|
@@ -283,6 +283,8 @@ Reference: `references/context-capsule.md`.
|
|
|
283
283
|
|
|
284
284
|
> peaks-txt is the TXT reducer; it sees the metadata-only view from G7 + the share entries from G8. The TXT handoff summarizes the slice at the slice-close gate. Detailed: `skills/peaks-code/references/context-governance.md`.
|
|
285
285
|
|
|
286
|
+
**Large tool output (> 2 KB) MUST NOT be dumped into the orchestrator's context** — use `--summary` (`peaks request list --summary`, `peaks memory list --summary`, `peaks memory reindex --summary`) or write to a file and `Read` selectively. Rationale (measured, session 2026-09-07-session-245530): full-envelope dumps cost ≈ 40K tokens of a 68%-full 1M window. `--summary` is an additive view — no information is removed. → `skills/peaks-code/references/context-governance.md` §G0.
|
|
287
|
+
|
|
286
288
|
### G8 — TXT reducer sees share entries on completion
|
|
287
289
|
|
|
288
290
|
When TXT reduces a batch, it consumes:
|
|
@@ -333,6 +333,8 @@ Do not own backend architecture, non-UI implementation, runtime hook installatio
|
|
|
333
333
|
|
|
334
334
|
UI sub-agents follow the same G7 metadata-only + G8.6 share protocol. UI artifacts are large binary-ish; the 1MB artifact size limit (G7.3) applies. Detailed: `skills/peaks-code/references/context-governance.md`.
|
|
335
335
|
|
|
336
|
+
**Large tool output (> 2 KB) MUST NOT be dumped into the orchestrator's context** — use `--summary` (`peaks request list --summary`, `peaks memory list --summary`) or write to a file and `Read` selectively. Rationale (measured, session 2026-09-07-session-245530): full-envelope dumps cost ≈ 40K tokens of a 68%-full 1M window. `--summary` is an additive view — no information is removed. → `skills/peaks-code/references/context-governance.md` §G0.
|
|
337
|
+
|
|
336
338
|
### G7 — UI sub-agent protocol
|
|
337
339
|
|
|
338
340
|
1. Write design draft / component scaffold to `.peaks/_sub_agents/<sid>/artifacts/<rid>-ui-001.md` (size ≤ 1MB).
|
|
@@ -310,6 +310,14 @@ After final validation, refresh project-local standards via `peaks standards ini
|
|
|
310
310
|
|
|
311
311
|
Main LLM reducer sees metadata-only view (~200 chars/sub-agent); on-demand `Read` for full content. Threshold table: 50% soft warn, 75% `CONTEXT_NEAR_LIMIT`, 80% hard reject (CLI + hook double-guard). → `references/context-governance.md`.
|
|
312
312
|
|
|
313
|
+
## Large tool output discipline (BLOCKING — slice 2026-09-10-context-audit-and-discipline)
|
|
314
|
+
|
|
315
|
+
> **Hard rule.** Any tool output larger than **2 KB** MUST NOT be dumped into the orchestrator's own context. Use `--summary` (bounded counts + names-of-first-N, ≤ 2 KB) when the command offers it — `peaks memory reindex --summary`, `peaks memory list --summary`, `peaks doctor --summary`, `peaks request list --summary` — otherwise write the output to a file and `Read` only the slice you need. The default envelopes are unchanged; `--summary` is strictly opt-in.
|
|
316
|
+
>
|
|
317
|
+
> **Why (measured, not cargo-cult).** In session `2026-09-07-session-245530` the orchestrator spent ~68% of a 1M window on its OWN context. Two offenders dominated: 4 × dumping a full `peaks memory reindex --json` unclassified array ≈ 160 KB ≈ 40K tokens, and 20 × sub-agent final reports ≈ 60 KB ≈ 15K tokens. Neither was dispatch boilerplate — both were the orchestrator reading things it did not need in full. `peaks code context-audit` now measures this per group (`{tool, key, bytes, pctOfTotal, count}`); run it when the window feels heavy.
|
|
318
|
+
>
|
|
319
|
+
> **Quality guard.** `--summary` removes no information — it is an additive view. Every path, name and count stays on disk and is re-readable by re-running the command without the flag. Never silently drop data; shrink the in-context copy instead.
|
|
320
|
+
|
|
313
321
|
## Sub-agent cross-batch signal — G8.4 share / shared-read / await
|
|
314
322
|
|
|
315
323
|
Three CLI primitives: `peaks sub-agent share / shared-read / await` (last-write-wins, ≤ 1KB warn / ≥ 64KB reject). Channel gitignored under `.peaks/_sub_agents/<sessionId>/shared/`.
|
|
@@ -3,6 +3,35 @@
|
|
|
3
3
|
> Slice #010 (G7 + G8 + G9 context-governance push).
|
|
4
4
|
> See: `.peaks/memory/sub-agent-context-minimal-occupation.md` + `sub-agent-shared-channel-cross-completion.md` for the red lines.
|
|
5
5
|
|
|
6
|
+
## G0 — orchestrator large-tool-output discipline (slice 2026-09-10-context-audit-and-discipline)
|
|
7
|
+
|
|
8
|
+
### The measurement (session `2026-09-07-session-245530`, ~68% of a 1M window)
|
|
9
|
+
|
|
10
|
+
The real token cost is the ORCHESTRATOR's own context — not dispatch boilerplate:
|
|
11
|
+
|
|
12
|
+
| Offender | Volume | Cost |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| 4 × full `peaks memory reindex --json` unclassified array | ≈ 160 KB | ≈ 40K tokens |
|
|
15
|
+
| 20 × sub-agent final reports | ≈ 60 KB | ≈ 15K tokens |
|
|
16
|
+
| several `cat` of large docs | ≈ 15 KB | ≈ 4K tokens |
|
|
17
|
+
|
|
18
|
+
`peaks code context-now` reports a RATIO only. `peaks code context-audit --project <root> --json` reports WHAT fills the window — top-N groups of `{tool, key, bytes, pctOfTotal, count}` — so the next session can name the offender instead of guessing. Read-only, fail-soft (`available: false` + reason; never blocks, never exits non-zero).
|
|
19
|
+
|
|
20
|
+
### The rule (BLOCKING)
|
|
21
|
+
|
|
22
|
+
- Tool output **> 2 KB** MUST NOT be dumped into the orchestrator's context.
|
|
23
|
+
- Prefer the opt-in `--summary` flag, which emits counts + names-of-first-N (≤ 2 KB) instead of the full array:
|
|
24
|
+
- `peaks memory reindex --summary`
|
|
25
|
+
- `peaks memory list --summary`
|
|
26
|
+
- `peaks doctor --summary` (JSON envelope)
|
|
27
|
+
- `peaks request list --summary`
|
|
28
|
+
- When a command has no `--summary`, write the output to a file and `Read` only the needed slice (offset/limit), or pipe through a filter before it reaches the orchestrator.
|
|
29
|
+
- Default (no flag) envelopes are byte-identical to before — `--summary` is strictly opt-in, so back-compat is preserved.
|
|
30
|
+
|
|
31
|
+
### Quality guard (binding)
|
|
32
|
+
|
|
33
|
+
`--summary` is an ADDITIVE view. It removes no information: every path, name and count remains on disk and is re-readable by re-running the same command without the flag. Silently dropping data is forbidden; shrinking the in-context copy is the goal. The sub-agent FINAL report cap (≤ 40 lines / 2 KB, detail in the artifact the parent can `Read`) follows the same principle — see the dispatch prompt's `## Final report cap (mandatory)` block.
|
|
34
|
+
|
|
6
35
|
## G7 — sub-agent context minimal-occupation (metadata-only + 按需 Read)
|
|
7
36
|
|
|
8
37
|
### Path convention
|
|
@@ -47,6 +47,8 @@ If a doctor finding requires a code change, the workflow hands off to `peaks-rd`
|
|
|
47
47
|
- `peaks openspec from-doctor` — L3.3 proposal generator
|
|
48
48
|
- `peaks openspec validate` — gate a draft proposal
|
|
49
49
|
|
|
50
|
+
**Large tool output (> 2 KB) MUST NOT be dumped into the orchestrator's context.** `peaks doctor` returns ~69 checks — use `peaks doctor --summary` (counts + names-of-first-N, ≤ 2 KB) and re-run without the flag only for the specific check you need to read. Rationale (measured, session 2026-09-07-session-245530): full-envelope dumps cost ≈ 40K tokens of a 68%-full 1M window; `--summary` is an additive view, so no information is removed.
|
|
51
|
+
|
|
50
52
|
## Boundaries
|
|
51
53
|
|
|
52
54
|
- The doctor is read-only. It does NOT modify code, fix bugs, or clean up sessions.
|