peaks-loop 4.0.24 → 4.0.26
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 +29 -0
- package/dist/cli/commands/codegraph-commands.d.ts +10 -0
- package/dist/cli/commands/codegraph-commands.js +18 -4
- package/dist/cli/commands/core/memory-command.js +1 -3
- package/dist/cli/commands/dispatch-commands.js +11 -67
- package/dist/cli/commands/memory-commands.d.ts +0 -2
- package/dist/cli/commands/memory-commands.js +4 -7
- package/dist/cli/commands/preferences-commands.js +0 -1
- package/dist/cli/commands/qa-commands.js +5 -5
- package/dist/cli/commands/sub-agent-shared.d.ts +0 -4
- package/dist/cli/commands/sub-agent-shared.js +0 -14
- package/dist/cli/commands/workflow-commands.js +5 -5
- package/dist/services/context/build-dispatch-system-prompt.d.ts +4 -4
- package/dist/services/context/build-dispatch-system-prompt.js +6 -6
- package/dist/services/context/context-guard.d.ts +2 -4
- package/dist/services/context/context-guard.js +4 -6
- package/dist/services/context/{headroom-fetcher.d.ts → doc-cache-fetcher.d.ts} +2 -2
- package/dist/services/context/{headroom-fetcher.js → doc-cache-fetcher.js} +4 -3
- package/dist/services/context/memory-preflight-service.js +6 -17
- package/dist/services/context/threshold.d.ts +0 -3
- package/dist/services/context/threshold.js +0 -3
- package/dist/services/dispatch/batch-counter.js +1 -1
- package/dist/services/dispatch/leak-detector.js +1 -1
- package/dist/services/fuzzy-matching/fzf-pick-service.d.ts +1 -1
- package/dist/services/fuzzy-matching/fzf-pick-service.js +1 -1
- package/dist/services/memory/llm-reranker.d.ts +4 -5
- package/dist/services/memory/llm-reranker.js +6 -8
- package/dist/services/memory/memory-search-service.d.ts +0 -32
- package/dist/services/memory/memory-search-service.js +0 -37
- package/dist/services/preferences/preferences-service.d.ts +5 -8
- package/dist/services/preferences/preferences-service.js +0 -30
- package/dist/services/preferences/preferences-types.d.ts +0 -22
- package/dist/services/preferences/preferences-types.js +0 -12
- package/dist/services/retrospective/retrospective-search-service.d.ts +0 -18
- package/dist/services/retrospective/retrospective-search-service.js +0 -32
- package/dist/services/slice/slice-benchmark-service.d.ts +1 -1
- package/dist/services/slice/slice-benchmark-service.js +1 -1
- package/dist/services/slice/slice-pick-service.d.ts +1 -1
- package/dist/services/slice/slice-pick-service.js +1 -1
- package/package.json +5 -6
- package/skills/bee/peaks-qa/references/qa-context-governance.md +1 -1
- package/skills/bee/peaks-rd/SKILL.md +9 -2
- package/skills/bee/peaks-rd/references/frontend-acl-mapper.md +29 -0
- package/skills/bee/peaks-rd/references/rd-context-governance.md +2 -2
- package/skills/bee/peaks-txt/SKILL.md +1 -1
- package/skills/bee/peaks-ui/SKILL.md +1 -1
- package/skills/peaks-code/SKILL.md +1 -1
- package/skills/peaks-code/references/codegraph-orchestration.md +7 -1
- package/skills/peaks-code/references/context-governance.md +7 -34
- package/skills/peaks-code/references/envelope-contract.md +2 -7
- package/skills/peaks-code/references/sub-agent-dispatch.md +2 -4
- package/dist/services/context/headroom-client.d.ts +0 -34
- package/dist/services/context/headroom-client.js +0 -117
- package/dist/services/context/headroom-prefs.d.ts +0 -46
- package/dist/services/context/headroom-prefs.js +0 -34
- package/skills/peaks-code/references/headroom-integration.md +0 -107
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Threshold: 1h (configurable). Rationale: the longest empirical
|
|
12
12
|
* peaks-rd / peaks-qa fan-out + reducer cycle is < 60s; the threshold
|
|
13
|
-
* gives slow slices
|
|
13
|
+
* gives slow slices margin without hiding leaks for the next session.
|
|
14
14
|
*/
|
|
15
15
|
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
16
16
|
import { join } from 'node:path';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Generic fzf binary picker (slice 2026-06-14-fzf-
|
|
2
|
+
* Generic fzf binary picker (slice 2026-06-14-fzf-rollout).
|
|
3
3
|
*
|
|
4
4
|
* Promoted from `src/services/slice/slice-pick-service.ts` (which is
|
|
5
5
|
* the only prior caller). Encapsulates the canonical fzf integration
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Generic fzf binary picker (slice 2026-06-14-fzf-
|
|
2
|
+
* Generic fzf binary picker (slice 2026-06-14-fzf-rollout).
|
|
3
3
|
*
|
|
4
4
|
* Promoted from `src/services/slice/slice-pick-service.ts` (which is
|
|
5
5
|
* the only prior caller). Encapsulates the canonical fzf integration
|
|
@@ -35,10 +35,9 @@
|
|
|
35
35
|
*
|
|
36
36
|
* Why 4-bytes-per-token:
|
|
37
37
|
*
|
|
38
|
-
* - `
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* pipelines in the AC-ZA-5 benchmark.
|
|
38
|
+
* - `BYTES_PER_TOKEN = 4` is the standard rough English-text
|
|
39
|
+
* approximation (1 token ≈ 4 bytes). It keeps token estimates
|
|
40
|
+
* stable and comparable in the AC-ZA-5 benchmark.
|
|
42
41
|
*
|
|
43
42
|
* Out of scope (YAGNI per Karpathy #2 Simplicity First):
|
|
44
43
|
*
|
|
@@ -112,7 +111,7 @@ export interface RerankResult {
|
|
|
112
111
|
}
|
|
113
112
|
/**
|
|
114
113
|
* Estimate token count for a string. `1 token ≈ 4 bytes` for English
|
|
115
|
-
* text
|
|
114
|
+
* text (standard rough approximation).
|
|
116
115
|
*/
|
|
117
116
|
export declare function estimateTokens(text: string): number;
|
|
118
117
|
/**
|
|
@@ -35,10 +35,9 @@
|
|
|
35
35
|
*
|
|
36
36
|
* Why 4-bytes-per-token:
|
|
37
37
|
*
|
|
38
|
-
* - `
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* pipelines in the AC-ZA-5 benchmark.
|
|
38
|
+
* - `BYTES_PER_TOKEN = 4` is the standard rough English-text
|
|
39
|
+
* approximation (1 token ≈ 4 bytes). It keeps token estimates
|
|
40
|
+
* stable and comparable in the AC-ZA-5 benchmark.
|
|
42
41
|
*
|
|
43
42
|
* Out of scope (YAGNI per Karpathy #2 Simplicity First):
|
|
44
43
|
*
|
|
@@ -49,9 +48,8 @@
|
|
|
49
48
|
* - No integration with `memory-search-service.ts` (Z-A is a spike;
|
|
50
49
|
* the wiring into `peaks memory search` is Z-B).
|
|
51
50
|
*/
|
|
52
|
-
// Approximate 1 token = 4 bytes for English text.
|
|
53
|
-
//
|
|
54
|
-
// rerank input cost against headroom compressed cost directly.
|
|
51
|
+
// Approximate 1 token = 4 bytes for English text. Standard rough
|
|
52
|
+
// approximation used across the pipeline (AC-ZA-5 benchmark).
|
|
55
53
|
const BYTES_PER_TOKEN = 4;
|
|
56
54
|
// Hard caps so a pathological query / 60-candidate list cannot blow
|
|
57
55
|
// up the LLM prompt. These are conservative defaults; Z-B may tune.
|
|
@@ -62,7 +60,7 @@ const MAX_DESCRIPTION_CHARS = 240;
|
|
|
62
60
|
const DEFAULT_CHAT_TIMEOUT_MS = 5_000;
|
|
63
61
|
/**
|
|
64
62
|
* Estimate token count for a string. `1 token ≈ 4 bytes` for English
|
|
65
|
-
* text
|
|
63
|
+
* text (standard rough approximation).
|
|
66
64
|
*/
|
|
67
65
|
export function estimateTokens(text) {
|
|
68
66
|
return Math.ceil(Buffer.byteLength(text, 'utf8') / BYTES_PER_TOKEN);
|
|
@@ -59,35 +59,3 @@ export declare function loadMemoryIndex(projectRoot: string): MemoryIndexSnapsho
|
|
|
59
59
|
* spec §Component Details).
|
|
60
60
|
*/
|
|
61
61
|
export declare function searchMemory(input: MemorySearchInput): MemorySearchResult[];
|
|
62
|
-
/**
|
|
63
|
-
* Envelope for the optional headroom-compressed version of the search
|
|
64
|
-
* result text. The compressed text is intended for the LLM-side prompt
|
|
65
|
-
* (it preserves the entry names + descriptions in a more token-efficient
|
|
66
|
-
* form); the structured `matches` array is unchanged and remains the
|
|
67
|
-
* machine-readable form. `null` when compression was not requested,
|
|
68
|
-
* did not run, or failed (see `warning` for the reason).
|
|
69
|
-
*/
|
|
70
|
-
export interface CompressedResultsEnvelope {
|
|
71
|
-
readonly mode: 'balanced' | 'aggressive' | 'conservative';
|
|
72
|
-
readonly originalSize: number;
|
|
73
|
-
readonly compressedSize: number;
|
|
74
|
-
readonly compressionRatio: number;
|
|
75
|
-
readonly compressedText: string;
|
|
76
|
-
readonly warning: string | null;
|
|
77
|
-
}
|
|
78
|
-
export interface MemorySearchWithResults {
|
|
79
|
-
readonly matches: MemorySearchResult[];
|
|
80
|
-
readonly compressedResults: CompressedResultsEnvelope | null;
|
|
81
|
-
}
|
|
82
|
-
export interface MemorySearchOptions {
|
|
83
|
-
/** When true, read preferences.headroom.perTouchpoint.memorySearch and
|
|
84
|
-
* compress joined match text via headroom-ai. Falls back silently to
|
|
85
|
-
* the structured `matches` array when compression is unavailable. */
|
|
86
|
-
readonly compressResults?: boolean;
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Run the search + optionally compress the joined match text via headroom.
|
|
90
|
-
* The structured `matches` array is the canonical output. The
|
|
91
|
-
* `compressedResults` is a derived view for LLM-side prompt assembly.
|
|
92
|
-
*/
|
|
93
|
-
export declare function searchMemoryWithResults(input: MemorySearchInput, options?: MemorySearchOptions): Promise<MemorySearchWithResults>;
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { fuzzyMatchWithKey } from '../fuzzy-matching/fuzzy-match-service.js';
|
|
4
|
-
import { compressPrompt } from '../context/headroom-client.js';
|
|
5
|
-
import { loadPreferences } from '../preferences/preferences-service.js';
|
|
6
|
-
import { shouldCompressResults } from '../context/headroom-prefs.js';
|
|
7
4
|
const DEFAULT_LIMIT = 6;
|
|
8
5
|
/**
|
|
9
6
|
* Read `.peaks/memory/index.json` and flatten the on-disk `hot[<kind>][]`
|
|
@@ -81,37 +78,3 @@ export function searchMemory(input) {
|
|
|
81
78
|
};
|
|
82
79
|
});
|
|
83
80
|
}
|
|
84
|
-
/**
|
|
85
|
-
* Run the search + optionally compress the joined match text via headroom.
|
|
86
|
-
* The structured `matches` array is the canonical output. The
|
|
87
|
-
* `compressedResults` is a derived view for LLM-side prompt assembly.
|
|
88
|
-
*/
|
|
89
|
-
export async function searchMemoryWithResults(input, options = {}) {
|
|
90
|
-
const matches = searchMemory(input);
|
|
91
|
-
if (options.compressResults !== true) {
|
|
92
|
-
return { matches, compressedResults: null };
|
|
93
|
-
}
|
|
94
|
-
const projectRoot = input.projectRoot ?? process.cwd();
|
|
95
|
-
const prefs = loadPreferences(projectRoot).headroom;
|
|
96
|
-
const joinedText = matches.map((m) => `${m.name}: ${m.description}`).join('\n');
|
|
97
|
-
const joinedBytes = Buffer.byteLength(joinedText, 'utf8');
|
|
98
|
-
const decision = shouldCompressResults(prefs, joinedBytes, 'memorySearch');
|
|
99
|
-
if (!decision.compress) {
|
|
100
|
-
return { matches, compressedResults: null };
|
|
101
|
-
}
|
|
102
|
-
const result = await compressPrompt(joinedText, decision.mode);
|
|
103
|
-
if (result.warning !== null || result.compressedPrompt === null) {
|
|
104
|
-
return { matches, compressedResults: null };
|
|
105
|
-
}
|
|
106
|
-
return {
|
|
107
|
-
matches,
|
|
108
|
-
compressedResults: {
|
|
109
|
-
mode: decision.mode,
|
|
110
|
-
originalSize: result.originalSize,
|
|
111
|
-
compressedSize: result.compressedSize,
|
|
112
|
-
compressionRatio: result.compressionRatio,
|
|
113
|
-
compressedText: result.compressedPrompt,
|
|
114
|
-
warning: result.warning
|
|
115
|
-
}
|
|
116
|
-
};
|
|
117
|
-
}
|
|
@@ -16,14 +16,11 @@ export declare function writeAtomic(filePath: string, prefs: ProjectPreferences)
|
|
|
16
16
|
* current shape. Returns the migrated object + a list of changes
|
|
17
17
|
* applied (for surfacing in the CLI envelope and the migration log).
|
|
18
18
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* is new in v2 — it defaults to `{ defaultMode: 'fan-out' }` so the
|
|
25
|
-
* pre-v2 behavior (peak-code SKILL instructed fan-out when ≥ 2 leaves)
|
|
26
|
-
* is preserved.
|
|
19
|
+
* v1's `agentShieldPrompt` and `loopAutonomousEnabled` were already
|
|
20
|
+
* present, so they carry through unchanged. The `fanout` field is new
|
|
21
|
+
* in v2 — it defaults to `{ defaultMode: 'fan-out' }` so the pre-v2
|
|
22
|
+
* behavior (peak-code SKILL instructed fan-out when ≥ 2 leaves) is
|
|
23
|
+
* preserved.
|
|
27
24
|
*/
|
|
28
25
|
export type MigrateResult = {
|
|
29
26
|
readonly fromVersion: string;
|
|
@@ -135,25 +135,6 @@ export function migratePreferences(projectRoot, opts = {}) {
|
|
|
135
135
|
};
|
|
136
136
|
}
|
|
137
137
|
const changes = [];
|
|
138
|
-
// v1 → v2: headroom.perTouchpoint fill-in (v1 only had defaultMode).
|
|
139
|
-
const headroomRaw = (raw.headroom ?? {});
|
|
140
|
-
const defaultMode = isHeadroomModeString(headroomRaw.defaultMode)
|
|
141
|
-
? headroomRaw.defaultMode
|
|
142
|
-
: 'balanced';
|
|
143
|
-
const perTouchpoint = {
|
|
144
|
-
subAgentDispatch: defaultMode,
|
|
145
|
-
memorySearch: defaultMode,
|
|
146
|
-
retrospectiveSearch: defaultMode,
|
|
147
|
-
doctorScan: defaultMode,
|
|
148
|
-
doctorRoute: 'conservative'
|
|
149
|
-
};
|
|
150
|
-
changes.push(`headroom.perTouchpoint filled in with defaultMode='${defaultMode}' (v1 lacked per-touchpoint overrides)`);
|
|
151
|
-
if (typeof headroomRaw.compressMinBytes === 'number') {
|
|
152
|
-
changes.push(`headroom.compressMinBytes preserved at ${headroomRaw.compressMinBytes}`);
|
|
153
|
-
}
|
|
154
|
-
else {
|
|
155
|
-
changes.push(`headroom.compressMinBytes defaulted to 4096`);
|
|
156
|
-
}
|
|
157
138
|
// v1 → v2: fanout block was absent; v2 needs it. Default to the
|
|
158
139
|
// pre-v2 behavior (fan-out when ≥ 2 leaves).
|
|
159
140
|
if (raw.fanout === undefined) {
|
|
@@ -176,14 +157,6 @@ export function migratePreferences(projectRoot, opts = {}) {
|
|
|
176
157
|
// fully-valid v2 ProjectPreferences.
|
|
177
158
|
const migrated = mergePreferences(DEFAULT_PREFERENCES, {
|
|
178
159
|
...raw,
|
|
179
|
-
headroom: {
|
|
180
|
-
enabled: typeof headroomRaw.enabled === 'boolean' ? headroomRaw.enabled : true,
|
|
181
|
-
defaultMode,
|
|
182
|
-
perTouchpoint,
|
|
183
|
-
compressMinBytes: typeof headroomRaw.compressMinBytes === 'number'
|
|
184
|
-
? headroomRaw.compressMinBytes
|
|
185
|
-
: 4096
|
|
186
|
-
},
|
|
187
160
|
fanout: typeof raw.fanout === 'object' && raw.fanout !== null && !Array.isArray(raw.fanout)
|
|
188
161
|
? (raw.fanout.defaultMode === 'serial'
|
|
189
162
|
? { defaultMode: 'fan-out' }
|
|
@@ -203,6 +176,3 @@ export function migratePreferences(projectRoot, opts = {}) {
|
|
|
203
176
|
written
|
|
204
177
|
};
|
|
205
178
|
}
|
|
206
|
-
function isHeadroomModeString(value) {
|
|
207
|
-
return value === 'balanced' || value === 'aggressive' || value === 'conservative';
|
|
208
|
-
}
|
|
@@ -22,27 +22,6 @@ export type UaPromptDecision = 'unset' | 'skip-this-session' | 'skip-forever';
|
|
|
22
22
|
* - 'lax' — always downgrade to previous level (faster, riskier)
|
|
23
23
|
*/
|
|
24
24
|
export type ClassifyConservatism = 'default' | 'strict' | 'lax';
|
|
25
|
-
/**
|
|
26
|
-
* Per-touchpoint headroom-AI mode override.
|
|
27
|
-
* Spec §7.4 — default 'balanced'.
|
|
28
|
-
*/
|
|
29
|
-
export type HeadroomMode = 'balanced' | 'aggressive' | 'conservative';
|
|
30
|
-
export interface HeadroomPreferences {
|
|
31
|
-
/** Whether headroom integration is enabled globally. Default: true */
|
|
32
|
-
readonly enabled: boolean;
|
|
33
|
-
/** Default mode if a touchpoint doesn't override. Default: 'balanced' */
|
|
34
|
-
readonly defaultMode: HeadroomMode;
|
|
35
|
-
/** Per-touchpoint mode overrides */
|
|
36
|
-
readonly perTouchpoint: {
|
|
37
|
-
subAgentDispatch: HeadroomMode;
|
|
38
|
-
memorySearch: HeadroomMode;
|
|
39
|
-
retrospectiveSearch: HeadroomMode;
|
|
40
|
-
doctorScan: HeadroomMode;
|
|
41
|
-
doctorRoute: HeadroomMode;
|
|
42
|
-
};
|
|
43
|
-
/** Minimum joined-result byte count before search-touchpoint compression runs. Default: 4096. */
|
|
44
|
-
readonly compressMinBytes: number;
|
|
45
|
-
}
|
|
46
25
|
export interface ClassifyRuleOverrides {
|
|
47
26
|
/** File count threshold above which a task is promoted to 'feature' */
|
|
48
27
|
readonly feature_threshold_files?: number;
|
|
@@ -101,7 +80,6 @@ export interface ProjectPreferences {
|
|
|
101
80
|
readonly agentShieldPrompt: UaPromptDecision;
|
|
102
81
|
readonly classifyConservatism: ClassifyConservatism;
|
|
103
82
|
readonly classifyRules: ClassifyRuleOverrides;
|
|
104
|
-
readonly headroom: HeadroomPreferences;
|
|
105
83
|
readonly swarmSpeculative: SwarmSpeculativePreferences;
|
|
106
84
|
/** Loop Autonomous (L4 14.5) toggle. Default: false — never auto-enable. */
|
|
107
85
|
readonly loopAutonomousEnabled: boolean;
|
|
@@ -28,18 +28,6 @@ export const DEFAULT_PREFERENCES = {
|
|
|
28
28
|
feature_threshold_lines: 100,
|
|
29
29
|
runtime_clean_grace_hours: 24,
|
|
30
30
|
},
|
|
31
|
-
headroom: {
|
|
32
|
-
enabled: true,
|
|
33
|
-
defaultMode: 'balanced',
|
|
34
|
-
perTouchpoint: {
|
|
35
|
-
subAgentDispatch: 'balanced',
|
|
36
|
-
memorySearch: 'balanced',
|
|
37
|
-
retrospectiveSearch: 'balanced',
|
|
38
|
-
doctorScan: 'balanced',
|
|
39
|
-
doctorRoute: 'conservative',
|
|
40
|
-
},
|
|
41
|
-
compressMinBytes: 4096,
|
|
42
|
-
},
|
|
43
31
|
swarmSpeculative: {
|
|
44
32
|
enabled: true,
|
|
45
33
|
maxConcurrent: 3,
|
|
@@ -35,21 +35,3 @@ export interface RetrospectiveSearchResult {
|
|
|
35
35
|
* runs (cheaper to filter, then fuzzy on a smaller set).
|
|
36
36
|
*/
|
|
37
37
|
export declare function searchRetrospective(input: RetrospectiveSearchInput): RetrospectiveSearchResult[];
|
|
38
|
-
export interface CompressedRetrospectiveResultsEnvelope {
|
|
39
|
-
readonly mode: 'balanced' | 'aggressive' | 'conservative';
|
|
40
|
-
readonly originalSize: number;
|
|
41
|
-
readonly compressedSize: number;
|
|
42
|
-
readonly compressionRatio: number;
|
|
43
|
-
readonly compressedText: string;
|
|
44
|
-
readonly warning: string | null;
|
|
45
|
-
}
|
|
46
|
-
export interface RetrospectiveSearchWithResults {
|
|
47
|
-
readonly matches: RetrospectiveSearchResult[];
|
|
48
|
-
readonly compressedResults: CompressedRetrospectiveResultsEnvelope | null;
|
|
49
|
-
}
|
|
50
|
-
export interface RetrospectiveSearchOptions {
|
|
51
|
-
/** When true, compress joined match text via headroom-ai. Falls back
|
|
52
|
-
* silently to the structured `matches` array on failure. */
|
|
53
|
-
readonly compressResults?: boolean;
|
|
54
|
-
}
|
|
55
|
-
export declare function searchRetrospectiveWithResults(input: RetrospectiveSearchInput, options?: RetrospectiveSearchOptions): Promise<RetrospectiveSearchWithResults>;
|
|
@@ -73,35 +73,3 @@ export function searchRetrospective(input) {
|
|
|
73
73
|
};
|
|
74
74
|
});
|
|
75
75
|
}
|
|
76
|
-
import { compressPrompt as compressPromptForRetro } from '../context/headroom-client.js';
|
|
77
|
-
import { loadPreferences as loadPreferencesForRetro } from '../preferences/preferences-service.js';
|
|
78
|
-
import { shouldCompressResults as shouldCompressResultsForRetro } from '../context/headroom-prefs.js';
|
|
79
|
-
export async function searchRetrospectiveWithResults(input, options = {}) {
|
|
80
|
-
const matches = searchRetrospective(input);
|
|
81
|
-
if (options.compressResults !== true) {
|
|
82
|
-
return { matches, compressedResults: null };
|
|
83
|
-
}
|
|
84
|
-
const projectRoot = input.projectRoot ?? process.cwd();
|
|
85
|
-
const prefs = loadPreferencesForRetro(projectRoot).headroom;
|
|
86
|
-
const joinedText = matches.map((m) => `${m.title}: ${m.summary}`).join('\n');
|
|
87
|
-
const joinedBytes = Buffer.byteLength(joinedText, 'utf8');
|
|
88
|
-
const decision = shouldCompressResultsForRetro(prefs, joinedBytes, 'retrospectiveSearch');
|
|
89
|
-
if (!decision.compress) {
|
|
90
|
-
return { matches, compressedResults: null };
|
|
91
|
-
}
|
|
92
|
-
const result = await compressPromptForRetro(joinedText, decision.mode);
|
|
93
|
-
if (result.warning !== null || result.compressedPrompt === null) {
|
|
94
|
-
return { matches, compressedResults: null };
|
|
95
|
-
}
|
|
96
|
-
return {
|
|
97
|
-
matches,
|
|
98
|
-
compressedResults: {
|
|
99
|
-
mode: decision.mode,
|
|
100
|
-
originalSize: result.originalSize,
|
|
101
|
-
compressedSize: result.compressedSize,
|
|
102
|
-
compressionRatio: result.compressionRatio,
|
|
103
|
-
compressedText: result.compressedPrompt,
|
|
104
|
-
warning: result.warning
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Slice benchmark wrapper (slice 2026-06-14-fzf-
|
|
2
|
+
* Slice benchmark wrapper (slice 2026-06-14-fzf-rollout).
|
|
3
3
|
*
|
|
4
4
|
* Wraps `decomposeSlices` with a thin observability layer that records
|
|
5
5
|
* total wall time, codegraph call count, confidence distribution, and
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Slice benchmark wrapper (slice 2026-06-14-fzf-
|
|
2
|
+
* Slice benchmark wrapper (slice 2026-06-14-fzf-rollout).
|
|
3
3
|
*
|
|
4
4
|
* Wraps `decomposeSlices` with a thin observability layer that records
|
|
5
5
|
* total wall time, codegraph call count, confidence distribution, and
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Slice Pick Service — thin wrapper around the generic fzf picker.
|
|
3
3
|
*
|
|
4
|
-
* v2 (slice 2026-06-14-fzf-
|
|
4
|
+
* v2 (slice 2026-06-14-fzf-rollout): the actual fzf
|
|
5
5
|
* integration is now in `src/services/fuzzy-matching/fzf-pick-service.ts`.
|
|
6
6
|
* This file only knows how to format / parse SliceCandidate rows and
|
|
7
7
|
* pick the output path. The algorithm itself (slice-decompose-service)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Slice Pick Service — thin wrapper around the generic fzf picker.
|
|
3
3
|
*
|
|
4
|
-
* v2 (slice 2026-06-14-fzf-
|
|
4
|
+
* v2 (slice 2026-06-14-fzf-rollout): the actual fzf
|
|
5
5
|
* integration is now in `src/services/fuzzy-matching/fzf-pick-service.ts`.
|
|
6
6
|
* This file only knows how to format / parse SliceCandidate rows and
|
|
7
7
|
* pick the output path. The algorithm itself (slice-decompose-service)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "peaks-loop",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.26",
|
|
4
4
|
"description": "Loop Engineering CLI — workflow primitive / loop guards / evaluators / slice orchestration",
|
|
5
5
|
"author": "SquabbyZ",
|
|
6
6
|
"keywords": [
|
|
@@ -99,13 +99,12 @@
|
|
|
99
99
|
"better-sqlite3": "^12.11.1",
|
|
100
100
|
"commander": "^12.1.0",
|
|
101
101
|
"fzf": "^0.5.2",
|
|
102
|
-
"headroom-ai": "0.22.4",
|
|
103
102
|
"yaml": "^2.9.0",
|
|
104
103
|
"zod": "^4.4.3",
|
|
105
|
-
"peaks-loop-internal-runtime": "0.0.
|
|
106
|
-
"peaks-loop-
|
|
107
|
-
"peaks-loop-shared": "0.0.
|
|
108
|
-
"peaks-loop-
|
|
104
|
+
"peaks-loop-internal-runtime": "0.0.11",
|
|
105
|
+
"peaks-loop-shared": "0.0.60",
|
|
106
|
+
"peaks-loop-shared-channel": "0.0.28",
|
|
107
|
+
"peaks-loop-mut": "0.1.24"
|
|
109
108
|
},
|
|
110
109
|
"devDependencies": {
|
|
111
110
|
"@changesets/cli": "2.31.1",
|
|
@@ -21,4 +21,4 @@ PROTOCOL (mandatory):
|
|
|
21
21
|
|
|
22
22
|
## G9 — QA prompt size self-check
|
|
23
23
|
|
|
24
|
-
Same as RD: 50% soft warn, 75% `CONTEXT_NEAR_LIMIT`, 80% hard reject unless `--force`. QA test plans can grow large;
|
|
24
|
+
Same as RD: 50% soft warn, 75% `CONTEXT_NEAR_LIMIT`, 80% hard reject unless `--force`. QA test plans can grow large; trim or split into multiple dispatches for plans > 75%.
|
|
@@ -189,6 +189,12 @@ When RD work creates a frontend application and the user has not specified a tec
|
|
|
189
189
|
|
|
190
190
|
→ see `references/frontend-project-generation.md` for the scaffold protocol.
|
|
191
191
|
|
|
192
|
+
## Frontend anti-corruption layer (ACL)
|
|
193
|
+
|
|
194
|
+
When RD work touches the frontend (pure frontend or full-stack), enforce the ACL discipline: the frontend's internal model is never polluted by external / API shapes. DTO ↔ ViewModel mapping converts external fields to internal fields at the boundary, and all conversion logic lives in one mapper file per domain (e.g. `mappers/user.mapper.ts`) — never scattered across pages. Hard constraint, verified by QA / code-review.
|
|
195
|
+
|
|
196
|
+
→ see `references/frontend-acl-mapper.md` for the three rules + reference shape + verification.
|
|
197
|
+
|
|
192
198
|
## Artifact and standards output
|
|
193
199
|
|
|
194
200
|
When project identification or scanning produces reports, matrices, maps, plans, or validation files, write them under the configured Peaks-Loop artifact workspace (default: `.peaks/_runtime/<sessionId>/rd/`). Do not default to a git-backed artifact repository or external artifact sync. Route standards mutations through `peaks standards init/update`; do not hand-write. Do not update user-global `~/.claude/rules/**` from this workflow.
|
|
@@ -229,9 +235,9 @@ The `karpathy-reviewer` sub-agent now reports its own runtime cost in the JSON e
|
|
|
229
235
|
|
|
230
236
|
The main RD loop MUST call `peaks job karpathy-cost-check` after every `peaks request transition --state qa-handoff` and before the next-slice work begins. If the decision kind is `downgraded`, the LLM MUST honor the downgraded `warn` and proceed to the next slice; the `'block'` was an artifact of the reviewer's own cost, not the slice's quality. If the decision kind is `reported` (costRatio > 50, gate not `'block'`), the LLM MAY continue; the sediment will be appended by `peaks memory extract` at handoff time. The full design lives in `.peaks/memory/2026-07-30-karpathy-evaluation-cost-self-review-design.md` (sediment locked 2026-07-30).
|
|
231
237
|
|
|
232
|
-
## Sub-agent context governance (G7 +
|
|
238
|
+
## Sub-agent context governance (G7 + G8 + G9 — slice #010)
|
|
233
239
|
|
|
234
|
-
RD sub-agent prompt template MUST include the G7 path convention + G8.6 share protocol. Detailed protocol: `skills/peaks-code/references/context-governance.md
|
|
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`.
|
|
235
241
|
|
|
236
242
|
→ see `references/rd-context-governance.md` for the full G7 / G8.6 / G9 protocol + RD sub-agent prompt template.
|
|
237
243
|
|
|
@@ -267,6 +273,7 @@ Index of every `references/` file in this skill. Read on demand.
|
|
|
267
273
|
| `references/compact-handoff.md` | RD compact handoff capsule. |
|
|
268
274
|
| `references/external-references.md` | External 3rd-party inventory. |
|
|
269
275
|
| `references/frontend-project-generation.md` | React + Vite + shadcn/ui default. |
|
|
276
|
+
| `references/frontend-acl-mapper.md` | 前端防腐层 ACL + DTO↔VM 映射 + Mapper 聚合硬约束. |
|
|
270
277
|
| `references/library-version-awareness.md` | Breaking-change gate + freshness check. |
|
|
271
278
|
| `references/mandatory-perf-baseline.md` | RD-side perf baseline + `peaks perf baseline` workflow. |
|
|
272
279
|
| `references/matt-pocock-integration.md` | Matt Pocock skills as references. |
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Frontend anti-corruption layer (ACL) + Mapper aggregation (RD)
|
|
2
|
+
|
|
3
|
+
> Body of `## Frontend anti-corruption layer (ACL)`. When RD work touches the frontend (pure frontend OR full-stack), the frontend's internal model MUST NOT be polluted by external / API data shapes. Enforce the anti-corruption layer (ACL) discipline on every frontend slice.
|
|
4
|
+
|
|
5
|
+
## Three hard rules (BLOCKING)
|
|
6
|
+
|
|
7
|
+
1. **ACL — protect the frontend internals.** The frontend never consumes backend / API DTOs directly in components, pages, hooks, or stores. An anti-corruption layer translates every external shape into an internal one at the boundary.
|
|
8
|
+
|
|
9
|
+
2. **DTO ↔ ViewModel mapping — external fields → internal fields.** API / DTO field names and nesting are external concerns. Map them to internal ViewModel fields at the boundary. Backend field/schema drift must not leak past the mapper.
|
|
10
|
+
|
|
11
|
+
3. **Mapper aggregation — one file per domain.** All conversion logic for a domain lives in exactly one mapper file (e.g. `mappers/user.mapper.ts`), never scattered across pages / components. A page or component imports the mapper; it does not inline a mapping.
|
|
12
|
+
|
|
13
|
+
## Reference shape
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
// mappers/user.mapper.ts — the ONLY place user DTO ↔ ViewModel mapping lives
|
|
17
|
+
import type { UserDTO } from '@/api/types'; // external
|
|
18
|
+
import type { UserViewModel } from '@/models/user'; // internal
|
|
19
|
+
|
|
20
|
+
export function toUserViewModel(dto: UserDTO): UserViewModel {
|
|
21
|
+
return { id: dto.user_id, name: dto.display_name, avatar: dto.avatar_url };
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Components / hooks import `toUserViewModel`; they never destructure `dto.user_id` directly.
|
|
26
|
+
|
|
27
|
+
## Verification
|
|
28
|
+
|
|
29
|
+
QA / code-review must flag: (a) any component / hook / store consuming a `*DTO` or a raw API response field directly, (b) any `*.mapper.ts` duplication across pages. The mapper is the single translation seam between external DTOs and the internal ViewModel.
|
|
@@ -31,6 +31,6 @@ PROTOCOL (mandatory):
|
|
|
31
31
|
|
|
32
32
|
Before dispatching a sub-agent, RD self-checks prompt size:
|
|
33
33
|
- < 50%: pass through.
|
|
34
|
-
- 50-75%: soft warn (consider
|
|
35
|
-
- 75-80%: soft warn + `warnings: ["CONTEXT_NEAR_LIMIT"]` (
|
|
34
|
+
- 50-75%: soft warn (consider trimming the prompt).
|
|
35
|
+
- 75-80%: soft warn + `warnings: ["CONTEXT_NEAR_LIMIT"]` (trim or split into multiple dispatches).
|
|
36
36
|
- 80%+: reject (CLI 兜底 returns `code: "PROMPT_TOO_LARGE"`). Use `--force` at CLI only when overriding; hook layer will still reject (RL-30).
|
|
@@ -305,7 +305,7 @@ The TXT handoff summarizes the slice by listing the artifact metas + the share e
|
|
|
305
305
|
|
|
306
306
|
### G9 — TXT prompt size self-check
|
|
307
307
|
|
|
308
|
-
Same G9 threshold table. TXT handoff messages can grow large when the slice has many batches;
|
|
308
|
+
Same G9 threshold table. TXT handoff messages can grow large when the slice has many batches; trim or split the handoff prompt proactively for prompts > 50%.
|
|
309
309
|
|
|
310
310
|
|
|
311
311
|
## L2 surface reference (post rid-l2-extended)
|
|
@@ -302,7 +302,7 @@ After final validation, refresh project-local standards via `peaks standards ini
|
|
|
302
302
|
|
|
303
303
|
`peaks codegraph affected` is an optional project-analysis enhancement (untrusted supporting evidence) for role handoff. Code must not treat codegraph output as approval for scope, design, or QA verdict. Never mutate agent settings / hooks from codegraph; do not commit `.codegraph/` artifacts. RD writes `.peaks/_runtime/<sessionId>/rd/codegraph-context.md`; QA / TXT consume the same envelope. → `references/codegraph-orchestration.md`.
|
|
304
304
|
|
|
305
|
-
## Sub-agent context governance (G7 +
|
|
305
|
+
## Sub-agent context governance (G7 + G8 + G9 — slice #010)
|
|
306
306
|
|
|
307
307
|
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`.
|
|
308
308
|
|
|
@@ -2,4 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
> Body of `## Codegraph orchestration context`. Code treats `peaks codegraph affected --project <path> <changed-files...> --json` as an optional project-analysis enhancement that informs the role handoff between PRD, RD, and QA. The output is untrusted supporting evidence — Code must not treat codegraph output as approval for scope, design, or QA verdict.
|
|
4
4
|
|
|
5
|
-
Do not run upstream installer flows, mutate agent settings, or commit `.codegraph/` artifacts into git. Peaks-Loop gates remain authoritative; codegraph context is a hint, never a substitute for role-skill output.
|
|
5
|
+
Do not run upstream installer flows, mutate agent settings, or commit `.codegraph/` artifacts into git. Peaks-Loop gates remain authoritative; codegraph context is a hint, never a substitute for role-skill output.
|
|
6
|
+
|
|
7
|
+
## Post-slice incremental re-index (slice 2026-09-01-feedback)
|
|
8
|
+
|
|
9
|
+
After each slice / task completes (RD done → QA done), the orchestrator MUST proactively run `peaks codegraph index --project <path>` to refresh the codegraph so the next slice's `affected` / `query` / `context` reflect the latest code — never a stale pre-slice graph. `index` is an incremental refresh; `peaks codegraph init` runs once per project, `index` runs after every slice.
|
|
10
|
+
|
|
11
|
+
The upstream bare `codegraph <subcommand>` binary is NOT on PATH — always route through `peaks codegraph <subcommand>`. (Upstream's own `status` output prints `Run "codegraph init" to initialize`; that bare-command hint is misleading in the peaks-loop context. A separate fix slice rewrites it to `peaks codegraph init`.)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
# Context Governance — G7 +
|
|
1
|
+
# Context Governance — G7 + G8 + G9 protocol details
|
|
2
2
|
|
|
3
|
-
> Slice #010 (G7 +
|
|
4
|
-
> See: `.peaks/memory/sub-agent-context-minimal-occupation.md` + `sub-agent-shared-channel-cross-completion.md`
|
|
3
|
+
> Slice #010 (G7 + G8 + G9 context-governance push).
|
|
4
|
+
> See: `.peaks/memory/sub-agent-context-minimal-occupation.md` + `sub-agent-shared-channel-cross-completion.md` for the red lines.
|
|
5
5
|
|
|
6
6
|
## G7 — sub-agent context minimal-occupation (metadata-only + 按需 Read)
|
|
7
7
|
|
|
@@ -56,26 +56,6 @@ On completion:
|
|
|
56
56
|
|
|
57
57
|
3000-5000× improvement. Main LLM full-slice context net increase: < 10KB for 5 batches × 6 sub-agents.
|
|
58
58
|
|
|
59
|
-
## G7.7 — headroom-ai integration (opt-in)
|
|
60
|
-
|
|
61
|
-
### `--use-headroom` flag
|
|
62
|
-
|
|
63
|
-
Opt-in flag on `peaks sub-agent dispatch`. Default `false` (G7 metadata-only remains the default).
|
|
64
|
-
|
|
65
|
-
### Mode table
|
|
66
|
-
|
|
67
|
-
| Mode | tokenBudget | Use case |
|
|
68
|
-
|---|---|---|
|
|
69
|
-
| `balanced` (default) | promptSize * 0.40 / 4 | General sub-agent dispatch |
|
|
70
|
-
| `aggressive` | promptSize * 0.20 / 4 | Last-resort large prompt |
|
|
71
|
-
| `conservative` | promptSize * 0.70 / 4 | Sensitive code analysis |
|
|
72
|
-
|
|
73
|
-
### Failure mode (RL-22d / RL-32)
|
|
74
|
-
|
|
75
|
-
- headroom daemon dead / proxy unreachable / times out
|
|
76
|
-
- → `code: "HEADROOM_UNAVAILABLE"` warning + G7 metadata-only fallback
|
|
77
|
-
- → NOT blocking (warn, then continue dispatch)
|
|
78
|
-
|
|
79
59
|
## G8 — cross sub-agent shared channel
|
|
80
60
|
|
|
81
61
|
### Path convention
|
|
@@ -132,8 +112,8 @@ PROTOCOL (mandatory):
|
|
|
132
112
|
|
|
133
113
|
| Threshold | Prompt size | Behavior |
|
|
134
114
|
|---|---|---|
|
|
135
|
-
| 50% (early warn) | ≥ 128KB | Soft warning, suggest
|
|
136
|
-
| **75% (user red line)** | ≥ 192KB | Soft warn + mandatory
|
|
115
|
+
| 50% (early warn) | ≥ 128KB | Soft warning, suggest trimming the prompt |
|
|
116
|
+
| **75% (user red line)** | ≥ 192KB | Soft warn + mandatory trim/split suggestion; `warnings: ["CONTEXT_NEAR_LIMIT"]` |
|
|
137
117
|
| **80% (hard reject)** | ≥ 204KB | Hard reject `code: "PROMPT_TOO_LARGE"`; `--force` allowed at CLI |
|
|
138
118
|
| **90% (emergency)** | ≥ 230KB | Hard reject `code: "PROMPT_EMERGENCY"`; `--force` STILL rejects at 90% (no override) |
|
|
139
119
|
|
|
@@ -149,7 +129,7 @@ PROTOCOL (mandatory):
|
|
|
149
129
|
|
|
150
130
|
## AC mapping
|
|
151
131
|
|
|
152
|
-
- AC-38..AC-43 (G7) + AC-
|
|
132
|
+
- AC-38..AC-43 (G7) + AC-47..AC-49 (G8) + AC-50..AC-65 (G9)
|
|
153
133
|
- See PRD §Acceptance criteria.
|
|
154
134
|
|
|
155
135
|
---
|
|
@@ -171,13 +151,6 @@ Main LLM view format (G7.4.e):
|
|
|
171
151
|
- qa-perf → .../artifacts/003-qa-perf-001.md (5KB, sha256:ghi789) summary: "p95 latency target ≤ 200ms"
|
|
172
152
|
```
|
|
173
153
|
|
|
174
|
-
### G7.7 — headroom-ai integration (opt-in compression)
|
|
175
|
-
|
|
176
|
-
> Body of `### G7.7`. If a sub-agent prompt is too large even after G7 metadata-only (e.g. 1MB artifact description, 5MB mid-prompt analysis), use `--use-headroom`:
|
|
177
|
-
- Default `false` (G7 remains default).
|
|
178
|
-
- Modes: `balanced` (default) | `aggressive` | `conservative`.
|
|
179
|
-
- Failure: `HEADROOM_UNAVAILABLE` warning + G7 metadata-only fallback (NOT blocking).
|
|
180
|
-
|
|
181
154
|
### G8 — cross sub-agent shared channel (dispatcher-mediated indirect signal)
|
|
182
155
|
|
|
183
156
|
> Body of `### G8`. Sub-agent A's completion **immediately** writes a shared entry; sub-agent B (still in flight) can read shared entries from sibling sub-agents. **This is NOT peer-to-peer messaging.** The dispatcher stores, the sub-agents read/write; A and B never directly talk.
|
|
@@ -192,7 +165,7 @@ Main LLM view format (G7.4.e):
|
|
|
192
165
|
|
|
193
166
|
| Threshold | Prompt size | Behavior |
|
|
194
167
|
|---|---|---|
|
|
195
|
-
| 50% (early warn) | ≥ 128KB | Soft warning, suggest
|
|
168
|
+
| 50% (early warn) | ≥ 128KB | Soft warning, suggest trimming the prompt |
|
|
196
169
|
| **75% (user red line)** | ≥ 192KB | Soft warn + `warnings: ["CONTEXT_NEAR_LIMIT"]` |
|
|
197
170
|
| **80% (hard reject)** | ≥ 204KB | Hard reject `code: "PROMPT_TOO_LARGE"`; `--force` allowed at CLI |
|
|
198
171
|
| 90% (emergency) | ≥ 230KB | Hard reject + `contextWarning: 'high'` |
|