peaks-loop 3.0.3 → 3.1.1
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 +55 -0
- package/dist/src/cli/commands/job-commands.d.ts +3 -0
- package/dist/src/cli/commands/job-commands.js +236 -0
- package/dist/src/cli/commands/solo-commands.js +137 -0
- package/dist/src/cli/program.js +6 -0
- package/dist/src/services/job/job-event-emitter.d.ts +30 -0
- package/dist/src/services/job/job-event-emitter.js +5 -0
- package/dist/src/services/job/job-orchestrator.d.ts +50 -0
- package/dist/src/services/job/job-orchestrator.js +104 -0
- package/dist/src/services/job/job-resource-snapshot.d.ts +7 -0
- package/dist/src/services/job/job-resource-snapshot.js +47 -0
- package/dist/src/services/job/job-rotation.d.ts +17 -0
- package/dist/src/services/job/job-rotation.js +30 -0
- package/dist/src/services/job/job-state-store.d.ts +24 -0
- package/dist/src/services/job/job-state-store.js +60 -0
- package/dist/src/services/job/job-types.d.ts +444 -0
- package/dist/src/services/job/job-types.js +87 -0
- package/dist/src/services/job/subagent-job-wrapper.d.ts +35 -0
- package/dist/src/services/job/subagent-job-wrapper.js +30 -0
- package/dist/src/services/solo/job-shape-decision.d.ts +71 -0
- package/dist/src/services/solo/job-shape-decision.js +151 -0
- package/dist/src/shared/version.d.ts +1 -1
- package/dist/src/shared/version.js +1 -1
- package/package.json +1 -1
- package/skills/peaks-solo/SKILL.md +51 -13
- package/skills/peaks-solo/references/job-loop.md +68 -0
- package/skills/peaks-solo/references/runbook.md +40 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v3.1.1 Step 0.8 — Job-shape decision recorder.
|
|
3
|
+
*
|
|
4
|
+
* The CLI is a *recorder and gate*, not a judge. The LLM makes the
|
|
5
|
+
* semantic call ("is this a Job?") and writes the verdict via
|
|
6
|
+
* `peaks solo detect-job`; this service persists the verdict to
|
|
7
|
+
* `.peaks/_runtime/<sessionId>/job-shape.json` and refuses downstream
|
|
8
|
+
* steps that need a decision when the file is missing.
|
|
9
|
+
*
|
|
10
|
+
* Karpathy §2 (Simplicity First): ~100 lines, pure validation + fs
|
|
11
|
+
* writes, NO keyword regex, NO LLM call inside. The LLM is the
|
|
12
|
+
* source of truth for whether the request is Job-shaped; this file
|
|
13
|
+
* just enforces that a decision was made at all.
|
|
14
|
+
*
|
|
15
|
+
* Decision file lives at `.peaks/_runtime/<sessionId>/job-shape.json`
|
|
16
|
+
* (sibling of `session.json`, NOT under `job/<jid>/` because no jid
|
|
17
|
+
* exists yet at the moment of Step 0.8).
|
|
18
|
+
*/
|
|
19
|
+
import { createHash } from 'node:crypto';
|
|
20
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
21
|
+
import { join } from 'node:path';
|
|
22
|
+
import { z } from 'zod';
|
|
23
|
+
export const JOB_SHAPE_SCHEMA_VERSION = 1;
|
|
24
|
+
export const JOB_SHAPE_FILE_NAME = 'job-shape.json';
|
|
25
|
+
export const JOB_SHAPE_ALREADY_DECIDED = 'JOB_SHAPE_ALREADY_DECIDED';
|
|
26
|
+
export const JOB_SHAPE_NOT_DECIDED = 'JOB_SHAPE_NOT_DECIDED';
|
|
27
|
+
export const JOB_SHAPE_INVALID = 'JOB_SHAPE_INVALID';
|
|
28
|
+
const SUGGESTED_JID_RE = /^[a-z0-9][a-z0-9-]{2,40}$/;
|
|
29
|
+
const JobShapeDecisionSchema = z.object({
|
|
30
|
+
isJob: z.boolean(),
|
|
31
|
+
rationale: z.string().min(1).max(2000),
|
|
32
|
+
suggestedJobId: z.string().regex(SUGGESTED_JID_RE, 'suggestedJobId must match /^[a-z0-9][a-z0-9-]{2,40}$/'),
|
|
33
|
+
suggestedStrategy: z.enum(['single', 'rotating']),
|
|
34
|
+
confidence: z.enum(['high', 'medium', 'low']),
|
|
35
|
+
decidedAt: z.string().datetime()
|
|
36
|
+
});
|
|
37
|
+
const JobShapeRecordSchema = z.object({
|
|
38
|
+
sessionId: z.string().min(1),
|
|
39
|
+
promptHash: z.string().regex(/^[a-f0-9]{16}$/, 'promptHash must be 16 hex chars'),
|
|
40
|
+
decision: JobShapeDecisionSchema,
|
|
41
|
+
schemaVersion: z.literal(JOB_SHAPE_SCHEMA_VERSION)
|
|
42
|
+
});
|
|
43
|
+
export class JobShapeDecisionError extends Error {
|
|
44
|
+
code;
|
|
45
|
+
details;
|
|
46
|
+
constructor(opts) {
|
|
47
|
+
super(opts.message);
|
|
48
|
+
this.name = 'JobShapeDecisionError';
|
|
49
|
+
this.code = opts.code;
|
|
50
|
+
if (opts.details !== undefined)
|
|
51
|
+
this.details = opts.details;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function runtimeDir(projectRoot, sessionId) {
|
|
55
|
+
return join(projectRoot, '.peaks', '_runtime', sessionId);
|
|
56
|
+
}
|
|
57
|
+
function decisionPath(projectRoot, sessionId) {
|
|
58
|
+
return join(runtimeDir(projectRoot, sessionId), JOB_SHAPE_FILE_NAME);
|
|
59
|
+
}
|
|
60
|
+
function sha1Prefix16(input) {
|
|
61
|
+
return createHash('sha1').update(input, 'utf8').digest('hex').slice(0, 16);
|
|
62
|
+
}
|
|
63
|
+
/** Pure validator; throws on bad input. No fs IO. */
|
|
64
|
+
export function validateJobShapeDecision(input) {
|
|
65
|
+
return JobShapeDecisionSchema.parse(input);
|
|
66
|
+
}
|
|
67
|
+
function validateRecord(input) {
|
|
68
|
+
return JobShapeRecordSchema.parse(input);
|
|
69
|
+
}
|
|
70
|
+
/** Read the canonical record; throws JOB_SHAPE_NOT_DECIDED if absent or malformed. */
|
|
71
|
+
export function readJobShapeDecision(projectRoot, sessionId) {
|
|
72
|
+
const path = decisionPath(projectRoot, sessionId);
|
|
73
|
+
if (!existsSync(path)) {
|
|
74
|
+
throw new JobShapeDecisionError({
|
|
75
|
+
code: JOB_SHAPE_NOT_DECIDED,
|
|
76
|
+
message: `No Job-shape decision recorded for session ${sessionId}. Run \`peaks solo detect-job\` to record a decision.`,
|
|
77
|
+
details: { path }
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
let raw;
|
|
81
|
+
try {
|
|
82
|
+
raw = readFileSync(path, 'utf8');
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
throw new JobShapeDecisionError({
|
|
86
|
+
code: JOB_SHAPE_NOT_DECIDED,
|
|
87
|
+
message: `Job-shape decision file is unreadable for session ${sessionId}. Run \`peaks solo detect-job\` to re-record.`,
|
|
88
|
+
details: { path, cause: err instanceof Error ? err.message : String(err) }
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
let parsed;
|
|
92
|
+
try {
|
|
93
|
+
parsed = JSON.parse(raw);
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
throw new JobShapeDecisionError({
|
|
97
|
+
code: JOB_SHAPE_NOT_DECIDED,
|
|
98
|
+
message: `Job-shape decision file is malformed JSON for session ${sessionId}. Run \`peaks solo detect-job --force\` to overwrite.`,
|
|
99
|
+
details: { path, cause: err instanceof Error ? err.message : String(err) }
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
return validateRecord(parsed);
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
throw new JobShapeDecisionError({
|
|
107
|
+
code: JOB_SHAPE_NOT_DECIDED,
|
|
108
|
+
message: `Job-shape decision file is schema-invalid for session ${sessionId}. Run \`peaks solo detect-job --force\` to overwrite.`,
|
|
109
|
+
details: { path, cause: err instanceof Error ? err.message : String(err) }
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Persist a new decision; refuses overwrite unless `force: true`.
|
|
115
|
+
* Stamps `decidedAt` server-side via `now` (defaults to `new Date()`)
|
|
116
|
+
* so the LLM cannot back-date.
|
|
117
|
+
*/
|
|
118
|
+
export function writeJobShapeDecision(projectRoot, sessionId, decision, opts = {}) {
|
|
119
|
+
const path = decisionPath(projectRoot, sessionId);
|
|
120
|
+
if (existsSync(path) && opts.force !== true) {
|
|
121
|
+
throw new JobShapeDecisionError({
|
|
122
|
+
code: JOB_SHAPE_ALREADY_DECIDED,
|
|
123
|
+
message: `Job-shape decision already exists for session ${sessionId}. Re-run with --force to overwrite.`,
|
|
124
|
+
details: { path }
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
// Validate the LLM-supplied fields first so a bad call fails loud
|
|
128
|
+
// before we touch the filesystem.
|
|
129
|
+
const partial = {
|
|
130
|
+
isJob: decision.isJob,
|
|
131
|
+
rationale: decision.rationale,
|
|
132
|
+
suggestedJobId: decision.suggestedJobId,
|
|
133
|
+
suggestedStrategy: decision.suggestedStrategy,
|
|
134
|
+
confidence: decision.confidence
|
|
135
|
+
};
|
|
136
|
+
// Validate the LLM-supplied shape (decidedAt is server-stamped).
|
|
137
|
+
JobShapeDecisionSchema.omit({ decidedAt: true }).parse(partial);
|
|
138
|
+
const now = opts.now ?? (() => new Date());
|
|
139
|
+
const stamped = { ...partial, decidedAt: now().toISOString() };
|
|
140
|
+
const promptHash = sha1Prefix16(decision.prompt);
|
|
141
|
+
const record = {
|
|
142
|
+
sessionId,
|
|
143
|
+
promptHash,
|
|
144
|
+
decision: stamped,
|
|
145
|
+
schemaVersion: JOB_SHAPE_SCHEMA_VERSION
|
|
146
|
+
};
|
|
147
|
+
const dir = runtimeDir(projectRoot, sessionId);
|
|
148
|
+
mkdirSync(dir, { recursive: true });
|
|
149
|
+
writeFileSync(path, JSON.stringify(record, null, 2) + '\n', 'utf8');
|
|
150
|
+
return record;
|
|
151
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "3.
|
|
1
|
+
export declare const CLI_VERSION = "3.1.1";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const CLI_VERSION = "3.
|
|
1
|
+
export const CLI_VERSION = "3.1.1";
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@ The `.peaks/` workspace has a **single scope axis** (session-id) plus a nested *
|
|
|
11
11
|
|
|
12
12
|
## Karpathy guidance (Slice 1/6 — karpathy prompt-injection-lift)
|
|
13
13
|
|
|
14
|
-
Every sub-agent dispatch (`peaks-prd`, `peaks-rd`, `peaks-qa`, `peaks-ui`, `peaks-sc`, `peaks-txt`) MUST receive the 4 Karpathy guidelines. Append the verbatim block from `peaks-rd/references/rd-sub-agent-dispatch.md` §"Karpathy-guidelines context" to the dispatch prompt
|
|
14
|
+
Every sub-agent dispatch (`peaks-prd`, `peaks-rd`, `peaks-qa`, `peaks-ui`, `peaks-sc`, `peaks-txt`) MUST receive the 4 Karpathy guidelines. Append the verbatim block from `peaks-rd/references/rd-sub-agent-dispatch.md` §"Karpathy-guidelines context" to the dispatch prompt. Canonical skill id: `andrej-karpathy-skills:karpathy-guidelines`. Summary: **#1 Think Before Coding**, **#2 Simplicity First** (min code, no speculative features, 800-line cap), **#3 Surgical Changes**, **#4 Goal-Driven Execution** (verifiable ACs, plan + verify checkpoints).
|
|
15
15
|
|
|
16
16
|
## Hard ban (effective 2.8.3 — read every session, no exceptions)
|
|
17
17
|
|
|
@@ -31,7 +31,7 @@ This skill is the **primary surface**. The `peaks <cmd>` CLI is **auxiliary**
|
|
|
31
31
|
|
|
32
32
|
## Code-Change Red Line (BLOCKING — read before ANY tool call)
|
|
33
33
|
|
|
34
|
-
**Peaks-Loop Solo is an orchestrator, NOT an implementer. You MUST NOT write, edit, or modify any application source code directly.** Every code change goes through `peaks-solo → RD → QA → verdict`. `full-auto`/`swarm` use `peaks sub-agent dispatch <role>`; `assisted`/`strict`/inline-fallback executes inline.
|
|
34
|
+
**Peaks-Loop Solo is an orchestrator, NOT an implementer. You MUST NOT write, edit, or modify any application source code directly.** Every code change goes through `peaks-solo → RD → QA → verdict`. `full-auto`/`swarm` use `peaks sub-agent dispatch <role>`; `assisted`/`strict`/inline-fallback executes inline. **If you catch yourself about to write code, STOP.** Hand off to RD. Before declaring workflow complete, run `peaks workflow verify-pipeline --rid <rid> --project <repo> --json`.
|
|
35
35
|
|
|
36
36
|
## Peaks-Loop Startup sequence (MANDATORY — execute in order)
|
|
37
37
|
|
|
@@ -41,11 +41,11 @@ This skill is the **primary surface**. The `peaks <cmd>` CLI is **auxiliary**
|
|
|
41
41
|
|
|
42
42
|
### Peaks-Loop Step 0.75: Resume from checkpoint (BLOCKING on same-day re-invocation)
|
|
43
43
|
|
|
44
|
-
When a NEW conversation opens on a session whose `lastActivity` is from today AND `.peaks/_runtime/<sessionId>/checkpoints/`
|
|
44
|
+
When a NEW conversation opens on a session whose `lastActivity` is from today AND `.peaks/_runtime/<sessionId>/checkpoints/` has `*.json`, surface via `peaks session info --active --json` + `peaks session resume --from <path> --project <repo>`. Prompt via `AskUserQuestion` (resume / fresh); if "resume", prepend the block. Step 0.75 is no-op on precondition fail. See `references/checkpoint-resume.md`.
|
|
45
45
|
|
|
46
46
|
### Peaks-Loop Step N: Periodic checkpoint (auto-fire, no user action)
|
|
47
47
|
|
|
48
|
-
Proactive context-overflow defense. CLI: `peaks session checkpoint [--reason <r>] [...flags]`. Fire `--reason periodic` every 20 tool calls (G1 hard-coded), `--reason artifact-written` per PRD/RD/QA/TXT write, `--reason context-fill` / `user-pause` / `user-close` as appropriate.
|
|
48
|
+
Proactive context-overflow defense. CLI: `peaks session checkpoint [--reason <r>] [...flags]`. Fire `--reason periodic` every 20 tool calls (G1 hard-coded), `--reason artifact-written` per PRD/RD/QA/TXT write, `--reason context-fill` / `user-pause` / `user-close` as appropriate. See `references/periodic-checkpoint.md` (otherwise `tests/unit/solo/checkpoint-periodic-frequency.test.ts` fails).
|
|
49
49
|
|
|
50
50
|
### Peaks-Loop Step 0.5: OpenSpec first-run opt-in (conditional)
|
|
51
51
|
|
|
@@ -67,7 +67,7 @@ After autonomous work (RD, QA, security, perf), invoke peaks-final-review for 4-
|
|
|
67
67
|
|
|
68
68
|
After every 4th tool call, probe via `peaks solo context-now --project <repo> --json`. Thresholds: 50% / **0.85 pre-compact / 0.95 red-line** (v2.13.0).
|
|
69
69
|
|
|
70
|
-
**≥ 0.85**, LLM MUST fire `peaks solo auto-compact --execute --project <repo>` autonomously
|
|
70
|
+
**≥ 0.85**, LLM MUST fire `peaks solo auto-compact --execute --project <repo>` autonomously. **≥ 0.95** red line: next Bash/Task tool call fires `peaks session auto-compact-hook` (PreToolUse), which in-band spawns `claude --compact` against the current runner.
|
|
71
71
|
|
|
72
72
|
**Karpathy §4 exception**: compact red line keeps runner alive → zero-intervention wins. LLM does NOT ask user to run `/compact`. Honor `--in-flight-batch` (D6.e).
|
|
73
73
|
|
|
@@ -87,19 +87,56 @@ Run `peaks workspace init` + `peaks skill presence:set peaks-solo` BEFORE any an
|
|
|
87
87
|
|
|
88
88
|
After Step 0, run the resume-detection probe; surface via `AskUserQuestion` if a slice is in flight.
|
|
89
89
|
|
|
90
|
-
**v2.11.0 D7 override:** if the user just `/compact`ed, run `peaks solo post-compact-detect --project <repo> --json` FIRST. If `shouldAutoResume: true`, skip
|
|
90
|
+
**v2.11.0 D7 override:** if the user just `/compact`ed, run `peaks solo post-compact-detect --project <repo> --json` FIRST. If `shouldAutoResume: true`, skip AskUserQuestion (D7.b). Log to `.peaks/_runtime/<sessionId>/txt/auto-decisions.md`. Cross-day / cross-machine resume NOT in scope (D7.g).
|
|
91
91
|
|
|
92
92
|
→ see `references/resume-detection.md` for the full detection algorithm + classification table.
|
|
93
93
|
|
|
94
94
|
### Peaks-Loop Step 0.55: 1.x → 2.0 detection (BLOCKING on first invocation per session, when the project is not on a 2.0 layout)
|
|
95
95
|
|
|
96
|
-
Per the "one-key completion" tenet, peaks-loop 2.0 detects 1.x consumers and prompts upgrade. After Step 0.7 returns "fresh", run `peaks upgrade --detect-1x --project <root> --json`. If `isOneX: true`, surface `AskUserQuestion`. Persist to `.peaks/preferences.json
|
|
96
|
+
Per the "one-key completion" tenet, peaks-loop 2.0 detects 1.x consumers and prompts upgrade. After Step 0.7 returns "fresh", run `peaks upgrade --detect-1x --project <root> --json`. If `isOneX: true`, surface `AskUserQuestion`. Persist to `.peaks/preferences.json`.
|
|
97
97
|
|
|
98
98
|
→ see `references/step-0-55-1x-detection.md` for the detection algorithm + AskUserQuestion options + persistence contract.
|
|
99
99
|
|
|
100
|
+
### Peaks-Loop Step 0.8 — Job 启动 (BLOCKING on LLM judgement — v3.1.1 patch)
|
|
101
|
+
|
|
102
|
+
The CLI does NOT judge whether the request is Job-shaped — that's the LLM's job. Keyword regexes are wrong here (too brittle, miss natural-language variants, shift the LLM's semantic understanding into code). The CLI is a **recorder and gate**: the LLM makes the judgement, writes it via `peaks solo detect-job`, and downstream steps refuse to proceed until the decision file exists.
|
|
103
|
+
|
|
104
|
+
LLM judgement criteria (read once, then call the recorder):
|
|
105
|
+
- N parallel targets (`app/components/*`, `app/modules/*`, `for each subdir`, "以目录为维度", "把剩下的也跑了").
|
|
106
|
+
- Completion phrase implying batch continuation (`until all done`, "继续执行下个 slice", "全部添加完", "all of them").
|
|
107
|
+
- Cost / length disavowal (`不用考虑费用`, "don't worry about cost", "一直跑").
|
|
108
|
+
- Explicit slice count (`25 slices`, "切分成 N 段", "拆成 N 块").
|
|
109
|
+
|
|
110
|
+
Action:
|
|
111
|
+
1. LLM reads the user prompt and applies judgement → `isJob: boolean + rationale + suggestedJobId + suggestedStrategy + confidence`.
|
|
112
|
+
2. LLM calls `peaks solo detect-job --is-job <bool> --rationale <text> --suggested-job-id <jid> --suggested-strategy <single|rotating> --confidence <high|medium|low> [--force]`.
|
|
113
|
+
3. CLI records to `.peaks/_runtime/<sessionId>/job-shape.json`.
|
|
114
|
+
4. If `isJob: true` → `peaks job init --job-id <jid> --slice-list <...> --main-loop-strategy <strategy> --rotate-every 3` (BEFORE Step 1).
|
|
115
|
+
5. Downstream steps (Step 1, Step 0.81, etc.) call `read-job-shape` to refuse if missing.
|
|
116
|
+
|
|
117
|
+
**Hard rule (v3.1.1 red-line #10):** LLM MUST NOT skip `peaks solo detect-job` even when the trigger is obvious from context. If the next step's `read-job-shape` throws `JOB_SHAPE_NOT_DECIDED`, Solo MUST record a decision before proceeding. Keyword-based "I already know it's a Job" is not a substitute.
|
|
118
|
+
|
|
119
|
+
### Peaks-Loop Step 0.81 — per-slice 收尾
|
|
120
|
+
|
|
121
|
+
After commit: `peaks job checkpoint --slice-id <rid> --state done --commit-sha $(git rev-parse HEAD)` → `peaks job status` → loop (remaining>0 → Step 1; ==0 → Step 8/9/10/11; blocked → Step 0.85).
|
|
122
|
+
|
|
123
|
+
### Peaks-Loop Step 0.85 — slice 阻塞处理
|
|
124
|
+
|
|
125
|
+
Trigger: `repair-status` atCap=true, context-now red-line ≥5min, or `subagent-cleanup` fails twice. Action: `peaks job block --slice-id <rid> --reason "<reason>"` then STOP with TXT handoff.
|
|
126
|
+
|
|
127
|
+
### Peaks-Loop Step 0.86 — main session rotation
|
|
128
|
+
|
|
129
|
+
Active in rotating mode; fires every `rotateEvery` slices or on-demand. Sequence: cycle-summary → checkpoint rotate-marker → `peaks session rotate` → next turn resumes via `peaks session resume --job-id <jid>`.
|
|
130
|
+
|
|
131
|
+
### Peaks-Loop Step 0.87 — sub-agent cleanup gate
|
|
132
|
+
|
|
133
|
+
After every `peaks sub-agent dispatch --batch-id <id>` inside a Job, BEFORE next slice checkpoint: `peaks job subagent-cleanup --job-id <jid> --batch-id <id> --force`. Non-zero → block.
|
|
134
|
+
|
|
135
|
+
→ see `references/job-loop.md` for state machine, visibility table, rotation cadence, cleanup gate, cross-day recovery, 9 red lines.
|
|
136
|
+
|
|
100
137
|
### Peaks-Loop Step 1: Mode selection
|
|
101
138
|
|
|
102
|
-
Use `AskUserQuestion` with `Full auto (Recommended)`
|
|
139
|
+
Use `AskUserQuestion` with `Full auto (Recommended)` first when user did not name a profile OR skill presence is stale (run `peaks skill presence:check-stale --project <path> --json` first; `stale: true` ⇒ re-ask).
|
|
103
140
|
|
|
104
141
|
→ see `references/mode-selection.md`, `references/fast-mode.md`, `references/mode-selection-with-stale-presence.md`.
|
|
105
142
|
|
|
@@ -153,7 +190,7 @@ When the project has no live backend (no swagger.json, no API server), Solo must
|
|
|
153
190
|
|
|
154
191
|
## Peaks-Loop Request type classification + Workflow order + Transition verification gates
|
|
155
192
|
|
|
156
|
-
The contract for the 6-type classification table, the 11-step workflow order, and the 7 transition verification gates (A through G with their `ls` / `grep` shell snippets) lives in `references/workflow-gates-and-types.md`. The peaks-solo narrative
|
|
193
|
+
The contract for the 6-type classification table, the 11-step workflow order, and the 7 transition verification gates (A through G with their `ls` / `grep` shell snippets) lives in `references/workflow-gates-and-types.md`. The peaks-solo narrative references those gate numbers (Gate A through Gate G) — keep both files in lockstep when adding or renaming a gate.
|
|
157
194
|
|
|
158
195
|
## Peaks-Loop Default sub-agent fan-out (≥ 2 leaves/topological level → `--from-dag`)
|
|
159
196
|
|
|
@@ -212,7 +249,7 @@ After final validation, refresh project-local standards via `peaks standards ini
|
|
|
212
249
|
|
|
213
250
|
## Peaks-Loop Step 11: Memory sediment (BLOCKING on workflow complete)
|
|
214
251
|
|
|
215
|
-
> **Hard rule.** Solo MUST NOT declare a workflow complete until Step 11 has produced ≥ 1 file in `.peaks/memory/` (the durable, LLM-authored memory store) OR the user has explicitly approved a no-sediment outcome via AskUserQuestion.
|
|
252
|
+
> **Hard rule.** Solo MUST NOT declare a workflow complete until Step 11 has produced ≥ 1 file in `.peaks/memory/` (the durable, LLM-authored memory store) OR the user has explicitly approved a no-sediment outcome via AskUserQuestion. Applies to **all modes** including `assisted` and `strict` — `assisted` previously skipped Step 10 because there was no `[CONFIRM]` gate; Step 11 fixes that.
|
|
216
253
|
|
|
217
254
|
### Substeps (BLOCKING)
|
|
218
255
|
|
|
@@ -246,15 +283,15 @@ peaks memory extract --project <repo> --artifact .peaks/_runtime/<sessionId>/txt
|
|
|
246
283
|
|
|
247
284
|
Default option = (a). Solo MUST NOT silently accept (b) without user pick.
|
|
248
285
|
|
|
249
|
-
**Why this step exists:** audit 2026-07-03 confirmed 2 consecutive sessions produced zero `.peaks/memory/` files despite completing RD + QA + handoff artifacts.
|
|
286
|
+
**Why this step exists:** audit 2026-07-03 confirmed 2 consecutive sessions produced zero `.peaks/memory/` files despite completing RD + QA + handoff artifacts. `assisted` mode silently skipped runbook Step 10 (no STOP condition). Step 11 makes the BLOCKING semantics explicit. **Why `peaks memory extract` (not `peaks project memories:extract`):** artifact-scoped extract is canonical; the batch-scoped sibling is for non-handoff flows. Always use `peaks memory extract --apply`.
|
|
250
287
|
|
|
251
288
|
## Peaks-Loop External references and lifecycle
|
|
252
289
|
|
|
253
|
-
3rd-party integrations (codegraph, mattpocock/skills, shadcn/ui, MCPs, Context7) follow Discovery → Reference → Side effect through Peaks CLI only. Run `peaks capabilities` for capability discovery; treat external skills as reference material only. MCP servers (Playwright / Chrome DevTools / Figma) are user-installed — check the tool list for `mcp__<server>__*` entries. → `references/external-references.md` + `references/external-skill-invocation.md`.
|
|
290
|
+
3rd-party integrations (codegraph, mattpocock/skills, shadcn/ui, MCPs, Context7) follow Discovery → Reference → Side effect through Peaks CLI only. Run `peaks capabilities` for capability discovery; treat external skills as reference material only. MCP servers (Playwright / Chrome DevTools / Figma) are user-installed — check the tool list for `mcp__<server>__*` entries. Do not execute upstream installer / run upstream commands / persist sensitive examples / install upstream resources directly — funnel side effects through the Peaks CLI surface. Peaks-Loop Solo gates and artifacts remain authoritative. → `references/external-references.md` + `references/external-skill-invocation.md`.
|
|
254
291
|
|
|
255
292
|
## Codegraph orchestration context
|
|
256
293
|
|
|
257
|
-
`peaks codegraph affected` is an optional project-analysis enhancement (untrusted supporting evidence) used during role handoff. Solo must not treat codegraph output as approval for scope, design, or QA verdict. Never mutate agent settings /
|
|
294
|
+
`peaks codegraph affected` is an optional project-analysis enhancement (untrusted supporting evidence) used during role handoff. Solo 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`.
|
|
258
295
|
|
|
259
296
|
## Sub-agent context governance (G7 + G7.7 + G8 + G9 — slice #010)
|
|
260
297
|
|
|
@@ -297,6 +334,7 @@ Index of every `references/` file. Read on demand.
|
|
|
297
334
|
| `references/refactor-mode.md` | Refactor mode + red lines. |
|
|
298
335
|
| `references/resume-detection.md` | Step 0.7 unfinished-work. |
|
|
299
336
|
| `references/runbook.md` | End-to-end CLI sequence. |
|
|
337
|
+
| `references/job-loop.md` | Step 0.8 / 0.81 / 0.85 / 0.86 / 0.87 deep-dive. |
|
|
300
338
|
| `references/skill-presence-and-title.md` | Step 2 + Step 2.5. |
|
|
301
339
|
| `references/standards-preflight.md` | Standards preflight. |
|
|
302
340
|
| `references/sub-agent-dispatch.md` | IDE-agnostic dispatch. |
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Peaks-Loop Solo — Job Loop Deep-Dive
|
|
2
|
+
|
|
3
|
+
> Read alongside `SKILL.md` Steps 0.8 / 0.81 / 0.85 / 0.86 / 0.87.
|
|
4
|
+
|
|
5
|
+
## State machine
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
init ─────────► pending ─► in-progress ─┐
|
|
9
|
+
│ │
|
|
10
|
+
▼ ▼
|
|
11
|
+
blocked (one slice) done ──► (next slice)
|
|
12
|
+
│ ▲
|
|
13
|
+
▼ │
|
|
14
|
+
(strict: STOP, │
|
|
15
|
+
best-effort: skipped) │
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Visibility
|
|
19
|
+
|
|
20
|
+
| Layer | Tool | Cost | User reads |
|
|
21
|
+
|---|---|---|---|
|
|
22
|
+
| LLM-runner transcript | (the chat) | free | always |
|
|
23
|
+
| `--watch` poll | peaks job status --watch | 1% CPU | when in a 2nd terminal pane |
|
|
24
|
+
| Statusline | peaks statusline install | ambient | in IDE bottom bar |
|
|
25
|
+
|
|
26
|
+
## Rotation cadence
|
|
27
|
+
|
|
28
|
+
- `len(slices) ≤ 2` → single-mode (auto-compact passively).
|
|
29
|
+
- `len(slices) ≥ 3` → rotating-mode, K=3 default.
|
|
30
|
+
- LLM-initiated override (rotating→single) is **strongly discouraged**; if used, must record `mainLoopOverride` in state.json with `reason ≥ 10 chars` and predicted wall-time ≤ 30 min.
|
|
31
|
+
|
|
32
|
+
## Cleanup gate
|
|
33
|
+
|
|
34
|
+
Every `peaks sub-agent dispatch` inside a Job scope MUST be matched by a `peaks job subagent-cleanup --force` BEFORE the next slice checkpoint. The wrapper (M5) refuses dispatch return-success without matching cleanup.
|
|
35
|
+
|
|
36
|
+
## Cross-day recovery
|
|
37
|
+
|
|
38
|
+
`peaks session resume --job-id <jid>` reads:
|
|
39
|
+
1. `job/<jid>/state.json` — current job state
|
|
40
|
+
2. `session/cycle-<n>.md` — main-session cycle summary (rotating mode)
|
|
41
|
+
3. `session/auto-decisions.md` — auto-compact history
|
|
42
|
+
4. `session/checkpoints/*.json` — generic session checkpoints
|
|
43
|
+
|
|
44
|
+
The resume endpoint cross-checks all four before offering continuation; if any layer is stale, the user gets an AskUserQuestion (resume / restart / skip).
|
|
45
|
+
|
|
46
|
+
## Visibility prose
|
|
47
|
+
|
|
48
|
+
The Job loop is foreground. Three visibility layers, all on by default:
|
|
49
|
+
1. **LLM-runner transcript** — primary surface; user reads the chat to see active step.
|
|
50
|
+
2. **`peaks job status --watch`** — terminal poll, ANSI bar, refresh every 3 s.
|
|
51
|
+
3. **Statusline** — ambient `job: <jid> [done/total] currentSlice ETA m:s context main%. cycle`.
|
|
52
|
+
|
|
53
|
+
No detached workers, no `nohup`, no `disown`. Any attempt to spawn a background job → red line violation → block.
|
|
54
|
+
|
|
55
|
+
## Red lines (9 hard rules)
|
|
56
|
+
|
|
57
|
+
The LLM-runner MUST NOT:
|
|
58
|
+
1. Enter Step 11 / write final handoff while job has remaining slices.
|
|
59
|
+
2. Re-ask the user about cost / length / context.
|
|
60
|
+
3. Coalesce multiple slices into one rid.
|
|
61
|
+
4. Modify a committed slice (`git commit --amend` on `done`).
|
|
62
|
+
5. Fake completion (CLI verifies commit-sha exists in git log).
|
|
63
|
+
6. Use detached / background / daemon-mode sub-agents inside a Job.
|
|
64
|
+
7. Skip `peaks job subagent-cleanup` between dispatch and slice checkpoint.
|
|
65
|
+
8. Skip or postpone a scheduled `peaks session rotate`.
|
|
66
|
+
9. Suppress visibility — no silencing statusline / `--watch`.
|
|
67
|
+
|
|
68
|
+
Violations trigger a `peaks job block` event with the specific red-line number.
|
|
@@ -187,3 +187,43 @@ peaks skill doctor --json
|
|
|
187
187
|
```
|
|
188
188
|
|
|
189
189
|
Repair loop details: see `## Mandatory RD QA repair loop` in SKILL.md for the full 5-step procedure and the 3-cycle cap. Append transition notes via `--reason` rather than rewriting artifacts during repair cycles.
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
|
|
193
|
+
# Peaks-Loop Default runbook — Job path (excerpt; full flow in references/job-loop.md)
|
|
194
|
+
|
|
195
|
+
# Step 0.8 (BLOCKING): LLM judges Job-shape, CLI records it.
|
|
196
|
+
# This is a RECORDER, not a detector. The LLM supplies --is-job + --rationale;
|
|
197
|
+
# the CLI writes .peaks/_runtime/<sid>/job-shape.json. Downstream steps call
|
|
198
|
+
# `peaks solo read-job-shape` to enforce the decision exists.
|
|
199
|
+
|
|
200
|
+
# (The LLM does the semantic judgement, e.g.:)
|
|
201
|
+
# "The user named N=35 parallel app/ subdirs, said 'continue until all done',
|
|
202
|
+
# and disavowed cost. isJob=true, rationale=..., suggestedJobId=app-ut-batch,
|
|
203
|
+
# suggestedStrategy=rotating, confidence=high."
|
|
204
|
+
peaks solo detect-job \
|
|
205
|
+
--is-job true \
|
|
206
|
+
--rationale "35 parallel app/ subdirs + 'until all done' + '不用考虑费用'" \
|
|
207
|
+
--suggested-job-id app-ut-batch \
|
|
208
|
+
--suggested-strategy rotating \
|
|
209
|
+
--confidence high
|
|
210
|
+
|
|
211
|
+
# Step 0.81-init: if Job-shaped, init BEFORE Step 1.
|
|
212
|
+
DECISION=$(peaks solo read-job-shape --json)
|
|
213
|
+
if [ "$(echo "$DECISION" | jq -r '.data.decision.isJob')" = "true" ]; then
|
|
214
|
+
JID=$(echo "$DECISION" | jq -r '.data.decision.suggestedJobId')
|
|
215
|
+
STRATEGY=$(echo "$DECISION" | jq -r '.data.decision.suggestedStrategy')
|
|
216
|
+
# LLM-derived slice list (not CLI-derived).
|
|
217
|
+
peaks job init --job-id "$JID" --slice-list "<LLM_DERIVED>" --main-loop-strategy "$STRATEGY" --rotate-every 3 --json
|
|
218
|
+
fi
|
|
219
|
+
|
|
220
|
+
# After Step 7 (RD+QA commit) lands AND Step 0.8 fired (state.json exists):
|
|
221
|
+
peaks job checkpoint --slice-id <rid> --state done --commit-sha $(git rev-parse HEAD)
|
|
222
|
+
peaks job status --job-id <jid> --json
|
|
223
|
+
peaks job subagent-cleanup --job-id <jid> --batch-id <bid> --force # Step 0.87 gate
|
|
224
|
+
# Loop control:
|
|
225
|
+
# remaining > 0 → return to Step 1 (next slice)
|
|
226
|
+
# remaining == 0 → Step 8/9/10/11 (original tail)
|
|
227
|
+
# blocked (strict) → peaks job block + STOP
|
|
228
|
+
# Rotating-mode: every rotateEvery slices → Step 0.86 (peaks session rotate + resume)
|
|
229
|
+
```
|