peaks-loop 4.0.47 → 4.0.49
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 +44 -0
- package/README-en.md +1 -1
- package/README.md +1 -1
- package/agents/karpathy-reviewer.md +11 -10
- package/dist/cli/cli-helpers.d.ts +34 -0
- package/dist/cli/cli-helpers.js +57 -0
- package/dist/cli/commands/code-job-shape-commands.js +8 -0
- package/dist/cli/commands/code-runtime-commands.js +48 -8
- package/dist/cli/commands/compact-command.js +110 -0
- package/dist/cli/commands/config-commands.js +15 -9
- package/dist/cli/commands/dashboard-long-run.js +6 -0
- package/dist/cli/commands/dispatch-commands.js +11 -1
- package/dist/cli/commands/doctor/invoke-from-code.js +6 -0
- package/dist/cli/commands/feedback-commands.d.ts +11 -7
- package/dist/cli/commands/feedback-commands.js +49 -17
- package/dist/cli/commands/final-review-commands.js +12 -0
- package/dist/cli/commands/hooks-commands.js +4 -4
- package/dist/cli/commands/job-commands.js +8 -0
- package/dist/cli/commands/loop-eval-commands.js +31 -0
- package/dist/cli/commands/perf-audit-commands.js +2 -0
- package/dist/cli/commands/playwright-commands.js +12 -0
- package/dist/cli/commands/prd-commands.js +1 -1
- package/dist/cli/commands/qa-commands.js +22 -0
- package/dist/cli/commands/request-commands.js +8 -0
- package/dist/cli/commands/scan-commands.js +1 -1
- package/dist/cli/commands/security-audit-commands.js +2 -0
- package/dist/cli/commands/slice-integrate-commands.js +22 -0
- package/dist/cli/commands/statusline-commands.js +44 -4
- package/dist/cli/commands/sub-agent/detached.d.ts +14 -1
- package/dist/cli/commands/sub-agent/detached.js +47 -22
- package/dist/cli/commands/sub-agent-shutdown-commands.js +11 -0
- package/dist/cli/commands/verdict-aggregate-command.js +95 -13
- package/dist/cli/commands/workflow-commands.js +1 -1
- package/dist/cli/index.js +5 -45
- package/dist/services/artifacts/artifact-prerequisites.d.ts +38 -7
- package/dist/services/artifacts/artifact-prerequisites.js +140 -65
- package/dist/services/artifacts/request-artifact-service.d.ts +8 -0
- package/dist/services/artifacts/request-artifact-service.js +77 -46
- package/dist/services/artifacts/request-artifact-state-helpers.d.ts +57 -0
- package/dist/services/artifacts/request-artifact-state-helpers.js +91 -10
- package/dist/services/audit/enforcers/active-skill-resolver.js +14 -1
- package/dist/services/audit-independent/perf-audit-service.d.ts +9 -0
- package/dist/services/audit-independent/perf-audit-service.js +27 -5
- package/dist/services/audit-independent/security-audit-service.d.ts +12 -2
- package/dist/services/audit-independent/security-audit-service.js +28 -6
- package/dist/services/code/auto-compact-lifecycle.d.ts +194 -0
- package/dist/services/code/auto-compact-lifecycle.js +229 -11
- package/dist/services/code/auto-compact-orchestrator.js +118 -7
- package/dist/services/code/compact-event-settle.d.ts +134 -0
- package/dist/services/code/compact-event-settle.js +240 -0
- package/dist/services/compact-history/compact-history-service.d.ts +14 -0
- package/dist/services/compact-statusline/compact-statusline-service.js +56 -22
- package/dist/services/config/config-restore.d.ts +12 -1
- package/dist/services/config/config-restore.js +35 -4
- package/dist/services/config/config-rollback.js +6 -1
- package/dist/services/context/auto-compact-types.d.ts +20 -2
- package/dist/services/context/harness-context-witness.d.ts +310 -0
- package/dist/services/context/harness-context-witness.js +606 -0
- package/dist/services/evidence/evidence-generator.js +86 -49
- package/dist/services/feedback/feedback-promotion-service.d.ts +137 -14
- package/dist/services/feedback/feedback-promotion-service.js +341 -20
- package/dist/services/feedback/promotion-artifact-evidence.d.ts +69 -0
- package/dist/services/feedback/promotion-artifact-evidence.js +332 -0
- package/dist/services/final-review/final-review-service.d.ts +9 -0
- package/dist/services/final-review/final-review-service.js +36 -12
- package/dist/services/ide/ide-registry.d.ts +19 -0
- package/dist/services/ide/ide-registry.js +21 -0
- package/dist/services/job/job-progress-store.js +18 -3
- package/dist/services/job/job-state-store.js +7 -0
- package/dist/services/observability/jsonl-store.d.ts +19 -0
- package/dist/services/observability/jsonl-store.js +27 -2
- package/dist/services/observability/observability-service.d.ts +10 -3
- package/dist/services/observability/observability-service.js +16 -3
- package/dist/services/polyrepo/polyrepo-dispatcher.js +11 -0
- package/dist/services/prd/handoff-auto-regen.js +31 -27
- package/dist/services/prd/handoff-frontmatter.d.ts +44 -0
- package/dist/services/prd/handoff-frontmatter.js +75 -0
- package/dist/services/prd/handoff-service.d.ts +41 -2
- package/dist/services/prd/handoff-service.js +124 -8
- package/dist/services/prd/handoff-types.d.ts +3 -2
- package/dist/services/prd/handoff-types.js +3 -2
- package/dist/services/qa/qa-business-review-state.js +23 -0
- package/dist/services/sc/sc-service.d.ts +8 -0
- package/dist/services/sc/sc-service.js +8 -1
- package/dist/services/scan/karpathy-service.js +2 -2
- package/dist/services/session/getSessionDir.d.ts +33 -0
- package/dist/services/session/getSessionDir.js +60 -0
- package/dist/services/session/session-checkpoint-service.js +8 -0
- package/dist/services/skill/resume-detector.js +29 -11
- package/dist/services/skills/hooks-codegate-superpowers.d.ts +6 -0
- package/dist/services/skills/hooks-codegate-superpowers.js +61 -2
- package/dist/services/skills/hooks-settings-service.js +14 -4
- package/dist/services/skills/session-start-hook-constants.d.ts +45 -0
- package/dist/services/skills/session-start-hook-constants.js +45 -0
- package/dist/services/skills/skill-statusline-service.d.ts +14 -0
- package/dist/services/slice/slice-check-service.js +29 -11
- package/dist/services/slice/slice-review-state.js +23 -0
- package/dist/services/workflow/pipeline-verify-gate-support.d.ts +47 -10
- package/dist/services/workflow/pipeline-verify-gate-support.js +221 -103
- package/dist/services/workflow/pipeline-verify-service.d.ts +1 -1
- package/dist/services/workflow/pipeline-verify-service.js +47 -33
- package/dist/services/workflow/pipeline-verify-types.d.ts +15 -6
- package/dist/services/workspace/claude-settings-template.d.ts +56 -8
- package/dist/services/workspace/claude-settings-template.js +98 -20
- package/dist/services/workspace/workspace-claude-settings-materializer.js +78 -7
- package/dist/shared/runtime-root.d.ts +73 -0
- package/dist/shared/runtime-root.js +77 -0
- package/package.json +6 -6
- package/skills/bee/peaks-prd/SKILL.md +7 -5
- package/skills/bee/peaks-qa/SKILL.md +5 -5
- package/skills/bee/peaks-qa/references/qa-runbook.md +2 -2
- package/skills/bee/peaks-qa/references/qa-transition-gates.md +7 -7
- package/skills/bee/peaks-rd/SKILL.md +8 -6
- package/skills/bee/peaks-rd/references/artifact-per-request.md +2 -2
- package/skills/bee/peaks-rd/references/parallel-review-fanout.md +7 -5
- package/skills/bee/peaks-rd/references/rd-fanout-contracts.md +13 -13
- package/skills/bee/peaks-rd/references/rd-runbook.md +9 -5
- package/skills/bee/peaks-rd/references/rd-transition-gates.md +9 -7
- package/skills/bee/peaks-rd/references/writing-handoff-frontmatter.md +6 -6
- package/skills/peaks-code/SKILL.md +1 -1
- package/skills/peaks-code/references/a2a-artifact-mapping.md +3 -3
- package/skills/peaks-code/references/local-artifact-workspace.md +1 -1
- package/skills/peaks-code/references/resume-detection.md +13 -7
- package/skills/peaks-code/references/runbook.md +3 -2
- package/skills/peaks-code/references/session-overload-signal-index.md +2 -1
- package/skills/peaks-code/references/workflow-gates-and-types.md +8 -6
|
@@ -7,22 +7,34 @@
|
|
|
7
7
|
* gates plus the verify-pipeline, so the orchestrator no longer hand-writes
|
|
8
8
|
* them per mechanical file-split (or similar) slice.
|
|
9
9
|
*
|
|
10
|
-
* The content markers emitted here are the exact strings the CLI gates
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* `src/services/workflow/
|
|
16
|
-
*
|
|
10
|
+
* The content markers emitted here are the exact strings the CLI gates
|
|
11
|
+
* mechanically check — see `src/services/artifacts/artifact-prerequisites.ts`
|
|
12
|
+
* (mustContain / mustContainAny / headingMustContain — the authoritative
|
|
13
|
+
* table). `peaks workflow verify-pipeline` reads that same table for both the
|
|
14
|
+
* evidence paths it probes and the markers it enforces
|
|
15
|
+
* (`src/services/workflow/pipeline-verify-gate-support.ts#contractEvidencePaths`).
|
|
16
|
+
* Do NOT reword the marker lines.
|
|
17
|
+
*
|
|
18
|
+
* This paragraph used to send editors to `src/services/workflow/artifact-paths.ts`
|
|
19
|
+
* as the checker of "suffixed security/performance findings". That is no longer
|
|
20
|
+
* true: the `security-findings-<rid>.md` / `performance-findings-<rid>.md` gates
|
|
21
|
+
* were removed in rid `2026-09-14-verify-pipeline-contract-drift` (no
|
|
22
|
+
* `qa:verdict-issued` table names those paths; security and perf evidence is
|
|
23
|
+
* resolved on the RD side at `audit/security-<rid>.md` / `audit/perf-<rid>.md`),
|
|
24
|
+
* and `artifact-paths.ts` has had zero code consumers since. It is not a marker
|
|
25
|
+
* authority, so it is not a pointer worth keeping.
|
|
17
26
|
*
|
|
18
27
|
* Karpathy §2 (Simplicity First): a single generator function + a handful of
|
|
19
28
|
* pure body builders. No speculative options, no validation beyond what the
|
|
20
29
|
* reference prototype performs.
|
|
21
30
|
*/
|
|
22
|
-
import { createHash } from 'node:crypto';
|
|
23
31
|
import { mkdir, readdir, writeFile } from 'node:fs/promises';
|
|
24
32
|
import { join } from 'node:path';
|
|
33
|
+
import { serializeHandoffFrontmatter } from '../prd/handoff-frontmatter.js';
|
|
34
|
+
import { handoffRelativePath, sha256OfBody } from '../prd/handoff-service.js';
|
|
25
35
|
import { getSessionDir } from '../session/getSessionDir.js';
|
|
36
|
+
import { REQUEST_ID_PATTERN } from '../artifacts/request-artifact-service.js';
|
|
37
|
+
import { isUnsafePathInput } from '../../shared/path-safety.js';
|
|
26
38
|
/** Comma/space tolerant `--files` splitter. */
|
|
27
39
|
export function parseFiles(raw) {
|
|
28
40
|
return raw
|
|
@@ -235,40 +247,20 @@ function buildQaRequest(rid, sid, files) {
|
|
|
235
247
|
`;
|
|
236
248
|
}
|
|
237
249
|
/**
|
|
238
|
-
* Build the `prd/handoff
|
|
239
|
-
*
|
|
240
|
-
* `
|
|
241
|
-
*
|
|
250
|
+
* Build the `prd/handoff-<rid>.md` frontmatter + body. The frontmatter comes from the
|
|
251
|
+
* ONE canonical serializer and `handoffHash === sha256(body)` — the same
|
|
252
|
+
* pairing `handoff-service.initHandoff` and `handoff-auto-regen.ts` use, so
|
|
253
|
+
* this producer cannot drift from them.
|
|
254
|
+
*
|
|
255
|
+
* This function used to hand-roll a third, divergent frontmatter: a
|
|
256
|
+
* `handoffHash: sha256:<hex>` value with **no line beginning `sha256:`**, over
|
|
257
|
+
* a hash computed on `frontmatter + body` rather than the body. The
|
|
258
|
+
* `AUDIT_REQUIRES_HANDOFF` gate (a substring check) accepted that file while
|
|
259
|
+
* `readAndVerifyHandoff` in both audit skills returned `null` — the very
|
|
260
|
+
* producer/consumer divergence rid `2026-09-14-handoff-writer-gate-divergence`
|
|
261
|
+
* exists to remove, surviving in a function the same slice edited.
|
|
242
262
|
*/
|
|
243
263
|
function buildHandoff(rid, sid, title, files, lineCounts) {
|
|
244
|
-
const bulletList = files.map((f) => ` - ${f}`).join('\n');
|
|
245
|
-
const frontmatter = `---
|
|
246
|
-
requestId: ${rid}
|
|
247
|
-
scope:
|
|
248
|
-
${bulletList}
|
|
249
|
-
files:
|
|
250
|
-
${bulletList}
|
|
251
|
-
handoffPath: .peaks/_runtime/${sid}/prd/handoff.md
|
|
252
|
-
handoffHash: sha256:PLACEHOLDER
|
|
253
|
-
decisions:
|
|
254
|
-
- id: D1
|
|
255
|
-
summary: "Mechanical verbatim module split"
|
|
256
|
-
rationale: "Satisfy the 800-line file-size gate."
|
|
257
|
-
risks:
|
|
258
|
-
- id: R1
|
|
259
|
-
description: "Public exports may be imported elsewhere"
|
|
260
|
-
mitigation: "Import sites updated; tsc clean."
|
|
261
|
-
nextActions:
|
|
262
|
-
- "peaks-qa validates the regression matrix"
|
|
263
|
-
gateEvidence:
|
|
264
|
-
projectScan: .peaks/project-scan/project-scan.md
|
|
265
|
-
prdHandoff: .peaks/_runtime/${sid}/prd/handoff.md
|
|
266
|
-
codeReview: .peaks/_runtime/${sid}/rd/code-review.md
|
|
267
|
-
securityReview: .peaks/_runtime/${sid}/rd/security-review.md
|
|
268
|
-
perfBaseline: .peaks/_runtime/${sid}/audit/perf.md
|
|
269
|
-
schemaVersion: 2
|
|
270
|
-
---
|
|
271
|
-
`;
|
|
272
264
|
const body = `# PRD Handoff — ${rid}
|
|
273
265
|
|
|
274
266
|
${title}. Mechanical verbatim module split; behavior-preserving.
|
|
@@ -279,10 +271,19 @@ ${filesMd(files)}
|
|
|
279
271
|
Line counts:
|
|
280
272
|
${lineCountsMd(lineCounts)}
|
|
281
273
|
`;
|
|
282
|
-
const
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
|
|
274
|
+
const handoffHash = sha256OfBody(body);
|
|
275
|
+
const frontmatter = {
|
|
276
|
+
requestId: rid,
|
|
277
|
+
sessionId: sid,
|
|
278
|
+
schemaVersion: '2',
|
|
279
|
+
handoffHash,
|
|
280
|
+
writtenAt: new Date().toISOString(),
|
|
281
|
+
goals: [],
|
|
282
|
+
acceptanceCriteria: [],
|
|
283
|
+
preservedBehavior: [],
|
|
284
|
+
handoffPath: handoffRelativePath(sid, rid)
|
|
285
|
+
};
|
|
286
|
+
return { content: `${serializeHandoffFrontmatter(frontmatter)}${body}`, hash: handoffHash };
|
|
286
287
|
}
|
|
287
288
|
/** Find the existing numbered QA request file for `rid`, else the default. */
|
|
288
289
|
async function resolveQaRequestPath(qaDir, rid) {
|
|
@@ -300,6 +301,21 @@ async function resolveQaRequestPath(qaDir, rid) {
|
|
|
300
301
|
}
|
|
301
302
|
export async function generateEvidence(options) {
|
|
302
303
|
const { projectRoot, rid, title, files, lineCounts, sessionId } = options;
|
|
304
|
+
// Both values become path segments below — the rid as a filename, the sid as
|
|
305
|
+
// the session directory. Guard them BEFORE the first mkdir so a rejected run
|
|
306
|
+
// leaves nothing behind. `REQUEST_ID_PATTERN` is the repo's own request-id
|
|
307
|
+
// control (`request-artifact-service.ts`, F-1 slice 025 security); the sid
|
|
308
|
+
// gets the segment check because `--session-id` has no pinned format.
|
|
309
|
+
// Measured before these lines existed: `--rid '../../../pwned'` wrote
|
|
310
|
+
// `.peaks/_runtime/pwned.md`, outside the per-session evidence dir, and a
|
|
311
|
+
// deeper rid wrote above the project root with the string echoed into the
|
|
312
|
+
// artifact body.
|
|
313
|
+
if (!REQUEST_ID_PATTERN.test(rid)) {
|
|
314
|
+
throw new Error(`Invalid request id: ${rid} (expected letters, digits, dots, underscores, or dashes)`);
|
|
315
|
+
}
|
|
316
|
+
if (isUnsafePathInput(sessionId)) {
|
|
317
|
+
throw new Error(`Invalid session id: ${sessionId} (must be a single path segment)`);
|
|
318
|
+
}
|
|
303
319
|
const sessionRoot = getSessionDir(projectRoot, sessionId);
|
|
304
320
|
const rdDir = join(sessionRoot, 'rd');
|
|
305
321
|
const qaDir = join(sessionRoot, 'qa');
|
|
@@ -319,18 +335,39 @@ export async function generateEvidence(options) {
|
|
|
319
335
|
}
|
|
320
336
|
const qaRequestPath = await resolveQaRequestPath(qaDir, rid);
|
|
321
337
|
const handoff = buildHandoff(rid, sessionId, title, files, lineCounts);
|
|
338
|
+
// Four of these twelve paths carry the rid because the `rd:qa-handoff` gate
|
|
339
|
+
// requires `<rid>` in them: every slice in a session shares `rd/` and
|
|
340
|
+
// `audit/`, so writing the ridless name silently destroyed the previous
|
|
341
|
+
// slice's evidence on 2026-09-13 while the gate stayed green (slice
|
|
342
|
+
// `2026-09-14-audit-artifact-rid-scoping`).
|
|
322
343
|
const writes = [
|
|
323
|
-
[join(rdDir,
|
|
344
|
+
[join(rdDir, `code-review-${rid}.md`), buildCodeReview(rid, title, files, lineCounts)],
|
|
345
|
+
[join(auditDir, `security-${rid}.md`), buildSecurityReview(rid)],
|
|
346
|
+
// ...but the `config` type is the one whose `rd:qa-handoff` row is
|
|
347
|
+
// `SECURITY_REVIEW` — the genuinely ridless `rd/security-review.md` — and
|
|
348
|
+
// this generator is request-type-agnostic (no `--request-type`). So it
|
|
349
|
+
// writes BOTH names: the rid-scoped one the fanout types resolve
|
|
350
|
+
// (`AUDIT_SECURITY`) and the bare one `config` resolves. Nothing is lost
|
|
351
|
+
// by having both; a `config` slice has nothing else to fall back on, and
|
|
352
|
+
// dropping this write made a `config` slice fail its own gate while the
|
|
353
|
+
// generator reported success (repair round: measured `missing:
|
|
354
|
+
// ['rd/security-review.md']` with the real generator and the real gate).
|
|
324
355
|
[join(rdDir, 'security-review.md'), buildSecurityReview(rid)],
|
|
325
|
-
[join(rdDir,
|
|
356
|
+
[join(rdDir, `karpathy-review-${rid}.md`), buildKarpathyReview(rid, lineCounts)],
|
|
357
|
+
// `rd/tech-doc.md` stays ridless on purpose: the TECH_DOC prereq was
|
|
358
|
+
// removed in v2.11.0, so nothing gates it and there is no rid-scoped
|
|
359
|
+
// sibling that anything reads.
|
|
326
360
|
[join(rdDir, 'tech-doc.md'), buildTechDoc(rid, title, files, lineCounts)],
|
|
327
|
-
[join(auditDir,
|
|
361
|
+
[join(auditDir, `perf-${rid}.md`), buildPerfAudit(rid)],
|
|
328
362
|
[join(qaDir, 'test-cases', `${rid}.md`), buildTestCases(rid)],
|
|
329
363
|
[join(qaDir, 'test-reports', `${rid}.md`), buildTestReport(rid)],
|
|
330
364
|
[join(qaDir, `security-findings-${rid}.md`), buildSecurityFindings(rid)],
|
|
331
365
|
[join(qaDir, `performance-findings-${rid}.md`), buildPerformanceFindings(rid)],
|
|
332
366
|
[qaRequestPath, buildQaRequest(rid, sessionId, files)],
|
|
333
|
-
|
|
367
|
+
// The handoff capsule carries the rid for the same reason as the four
|
|
368
|
+
// above (slice `2026-09-14-prd-capsule-rid-scoping`): one slot per
|
|
369
|
+
// session means the second slice's capsule overwrites the first's.
|
|
370
|
+
[join(prdDir, `handoff-${rid}.md`), handoff.content]
|
|
334
371
|
];
|
|
335
372
|
const writtenFiles = [];
|
|
336
373
|
for (const [path, content] of writes) {
|
|
@@ -341,7 +378,7 @@ export async function generateEvidence(options) {
|
|
|
341
378
|
rid,
|
|
342
379
|
sessionId,
|
|
343
380
|
sessionRoot,
|
|
344
|
-
handoffPath: join(prdDir,
|
|
381
|
+
handoffPath: join(prdDir, `handoff-${rid}.md`),
|
|
345
382
|
handoffHash: handoff.hash,
|
|
346
383
|
writtenFiles,
|
|
347
384
|
createdDirectories
|
|
@@ -8,23 +8,27 @@
|
|
|
8
8
|
* primitive behind the `peaks feedback promote` and
|
|
9
9
|
* `peaks feedback check-unpromoted` CLI commands.
|
|
10
10
|
*
|
|
11
|
-
* Promotion tracking convention
|
|
12
|
-
* "promoted" when one of the following is true:
|
|
11
|
+
* Promotion tracking convention — two parts, and BOTH are required:
|
|
13
12
|
*
|
|
14
|
-
* (a)
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
13
|
+
* (a) A MARKER, either an HTML comment near the top of the body
|
|
14
|
+
* (`<!-- peaks-feedback-promoted: layer=<A|B|C> -->`) or a sibling
|
|
15
|
+
* `.peaks/memory/<name>.promotion.json` sidecar with
|
|
16
|
+
* `{ layer: "A" | "B" | "C", ... }`. Written by `peaks feedback
|
|
17
|
+
* promote` so a single read of the memory file is enough to see the
|
|
18
|
+
* claimed layer.
|
|
18
19
|
*
|
|
19
|
-
* (b)
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
20
|
+
* (b) The ARTIFACT that layer implies — see `promotionArtifactChecks`.
|
|
21
|
+
* rid 2026-09-14-gate-h-promotion: the marker alone used to count,
|
|
22
|
+
* which made the gate self-certifying, because the only thing a marker
|
|
23
|
+
* proves is that `peaks feedback promote` ran. Every layer-A marker in
|
|
24
|
+
* this repo pointed at `sops/<name>.md`, a file that did not exist and
|
|
25
|
+
* that no engine reads.
|
|
23
26
|
*
|
|
24
|
-
* The comment marker is the SOURCE OF TRUTH for human review; the
|
|
25
|
-
*
|
|
26
|
-
* to mark a feedback memory as promoted.
|
|
27
|
+
* The comment marker is the SOURCE OF TRUTH for human review; the sidecar is
|
|
28
|
+
* the source of truth for the scanner. Neither is evidence on its own.
|
|
27
29
|
*/
|
|
30
|
+
import { type PromotionArtifactCheck, type PromotionEvidence } from './promotion-artifact-evidence.js';
|
|
31
|
+
export type { PromotionArtifactCheck, PromotionEvidence };
|
|
28
32
|
export type PromotionLayer = 'A' | 'B' | 'C';
|
|
29
33
|
export declare const PROMOTION_LAYERS: readonly PromotionLayer[];
|
|
30
34
|
export type PromotionLayerDetail = {
|
|
@@ -34,6 +38,59 @@ export type PromotionLayerDetail = {
|
|
|
34
38
|
description: string;
|
|
35
39
|
};
|
|
36
40
|
export declare const PROMOTION_LAYER_DETAILS: readonly PromotionLayerDetail[];
|
|
41
|
+
/**
|
|
42
|
+
* rid 2026-09-14-gate-h-promotion — what actually backs a promotion.
|
|
43
|
+
*
|
|
44
|
+
* Before this, a promotion was honored on the marker alone (HTML comment or
|
|
45
|
+
* sidecar). Both are written by `peaks feedback promote` and neither proves
|
|
46
|
+
* that anything was enforced: every layer-A promotion in this repo pointed at
|
|
47
|
+
* `sops/<name>.md`, a file that did not exist and that no engine reads. The
|
|
48
|
+
* gate was therefore self-certifying — it read only what the command it tells
|
|
49
|
+
* you to run had written.
|
|
50
|
+
*
|
|
51
|
+
* A promotion is now honored only when its layer's enforcement surface carries
|
|
52
|
+
* the artifact. The three layers keep artifacts in three different shapes, so
|
|
53
|
+
* the check is a small table rather than one rule:
|
|
54
|
+
*
|
|
55
|
+
* - A (peaks-sop gate): a SOP manifest at `.peaks/sops/<id>/sop.json` AND an
|
|
56
|
+
* entry for `<id>` in `.peaks/sops/registry.json`. The registry half is not
|
|
57
|
+
* decoration: `gate-enforce-service.enforceBashCommand` enumerates SOPs via
|
|
58
|
+
* `readRegistry()`, so an unregistered manifest is off the enforcement path
|
|
59
|
+
* no matter how valid it is.
|
|
60
|
+
* - B (peaks-hooks PreToolUse): `.peaks/.claude-settings-template.json` must
|
|
61
|
+
* register the rule inside its `hooks` block. The file always exists, so
|
|
62
|
+
* existence proves nothing — the evidence is the registration inside it.
|
|
63
|
+
* - C (mode-gate hardFloorCategory): `src/services/code/mode-gate.ts` must
|
|
64
|
+
* register the rule in the hard-floor vocabulary, for the same reason. This
|
|
65
|
+
* is the repo's existing convention: the one real layer-C promotion cites
|
|
66
|
+
* its memory by path, from the category's doc block.
|
|
67
|
+
*
|
|
68
|
+
* R2 (2026-09-14-gate-h-promotion): each of those is now a PARSE plus a shape
|
|
69
|
+
* assertion, in `promotion-artifact-evidence.ts`. Every check used to be a
|
|
70
|
+
* `text.includes(<rule>)` over the whole file, which certified a tree that was
|
|
71
|
+
* a refusal — an invalid-JSON registry, a template saying "do NOT add a
|
|
72
|
+
* matcher", a mode-gate line saying the rule is deliberately not a category —
|
|
73
|
+
* because in each case the rule's name was still in the file's bytes. A name in
|
|
74
|
+
* a file is not a registration, and a file that cannot be parsed is a finding,
|
|
75
|
+
* not a permit.
|
|
76
|
+
*/
|
|
77
|
+
/**
|
|
78
|
+
* SOP id used for a feedback memory's layer-A artifact.
|
|
79
|
+
*
|
|
80
|
+
* Prefixed because `peaks-*` SOP ids are reserved for the built-in namespace
|
|
81
|
+
* (`reservedIdReason` in sop-service.ts) and several feedback memories start
|
|
82
|
+
* with `peaks-`, which would make them unregistrable under their own name.
|
|
83
|
+
*/
|
|
84
|
+
export declare function sopIdForFeedback(memoryName: string): string;
|
|
85
|
+
/** The artifact(s) and the structural evidence each must carry for `layer` to mean anything. */
|
|
86
|
+
export declare function promotionArtifactChecks(memoryName: string, layer: PromotionLayer): PromotionArtifactCheck[];
|
|
87
|
+
/**
|
|
88
|
+
* Which of `checks` are not satisfied under `projectRoot`. Empty means the
|
|
89
|
+
* promotion is backed by its artifact. Never throws, and never permits: a file
|
|
90
|
+
* that is absent, unreadable, or unparseable is a finding, not a warning —
|
|
91
|
+
* "cannot read the evidence" must not read as "the evidence is good".
|
|
92
|
+
*/
|
|
93
|
+
export declare function missingArtifacts(checks: readonly PromotionArtifactCheck[], projectRoot: string): string[];
|
|
37
94
|
export type FeedbackMemory = {
|
|
38
95
|
/** File basename (without `.md`). */
|
|
39
96
|
name: string;
|
|
@@ -59,6 +116,43 @@ export type UnpromotedFeedbackEntry = {
|
|
|
59
116
|
path: string;
|
|
60
117
|
reason: string;
|
|
61
118
|
};
|
|
119
|
+
/**
|
|
120
|
+
* rid 2026-09-14-gate-h-promotion (classify slice) — the "not to be promoted"
|
|
121
|
+
* declaration.
|
|
122
|
+
*
|
|
123
|
+
* The gate used to know only `has artifact` / `has no artifact`, so a memory that
|
|
124
|
+
* prescribes no action could never pass: promoting it registers a SOP whose only
|
|
125
|
+
* gate is "the source file still exists", which asserts nothing about behaviour.
|
|
126
|
+
* That is a permanent false positive — the old vacuity defect facing the other way.
|
|
127
|
+
*
|
|
128
|
+
* The declaration closes it, but it must not become a way to silence the gate.
|
|
129
|
+
* It differs from the refused grandfather channel (`promotedAt` older than this
|
|
130
|
+
* rule) in that a grandfather exemption is a property of a memory's AGE: every
|
|
131
|
+
* legacy memory has it, it says nothing about content, and nobody has to assert
|
|
132
|
+
* or defend it. This is a bounded claim about the memory's CONTENT:
|
|
133
|
+
*
|
|
134
|
+
* 1. The code is drawn from a closed vocabulary — free text cannot be used.
|
|
135
|
+
* 2. Each code binds to a predicate over the memory's own frontmatter, which
|
|
136
|
+
* the gate recomputes. The declaration may only RESTATE what the memory
|
|
137
|
+
* already says; it cannot introduce a new fact.
|
|
138
|
+
* 3. A reason string is required (the `closedAt` escape hatch beside it has none).
|
|
139
|
+
* 4. Coexisting with a promotion marker is a contradiction and fails, so the
|
|
140
|
+
* channel cannot be used to bury a promotion whose artifact is missing.
|
|
141
|
+
* 5. Exempted memories stay REPORTED via `listPromotionExempt`, so the
|
|
142
|
+
* unpromoted count never drops silently.
|
|
143
|
+
*/
|
|
144
|
+
export declare const NOT_TO_PROMOTE_CODES: readonly ["non-actionable", "closed-slice-note"];
|
|
145
|
+
export type NotToPromoteCode = (typeof NOT_TO_PROMOTE_CODES)[number];
|
|
146
|
+
export type NotToPromoteRead = {
|
|
147
|
+
kind: 'none';
|
|
148
|
+
} | {
|
|
149
|
+
kind: 'valid';
|
|
150
|
+
code: NotToPromoteCode;
|
|
151
|
+
reason: string;
|
|
152
|
+
} | {
|
|
153
|
+
kind: 'invalid';
|
|
154
|
+
reason: string;
|
|
155
|
+
};
|
|
62
156
|
/**
|
|
63
157
|
* Parse a single `.peaks/memory/<file>.md` into a FeedbackMemory, or
|
|
64
158
|
* `null` when the file is missing / unreadable / not a feedback memory.
|
|
@@ -78,6 +172,26 @@ export declare function parseFeedbackMemory(filePath: string): FeedbackMemory |
|
|
|
78
172
|
export declare function listUnpromotedFeedback(opts: {
|
|
79
173
|
projectRoot: string;
|
|
80
174
|
}): UnpromotedFeedbackEntry[];
|
|
175
|
+
/**
|
|
176
|
+
* Read the memory's not-to-promote declaration. `invalid` is returned rather than
|
|
177
|
+
* `none` when the fields are present but unusable, so the gate fails with a reason
|
|
178
|
+
* instead of quietly treating a malformed declaration as "no declaration".
|
|
179
|
+
*/
|
|
180
|
+
export declare function readNotToPromote(filePath: string): NotToPromoteRead;
|
|
181
|
+
export type PromotionExemptEntry = {
|
|
182
|
+
name: string;
|
|
183
|
+
path: string;
|
|
184
|
+
code: NotToPromoteCode;
|
|
185
|
+
reason: string;
|
|
186
|
+
};
|
|
187
|
+
/**
|
|
188
|
+
* The feedback memories that declare themselves out of the gate. Exposed so the
|
|
189
|
+
* gate can REPORT them: an exemption nobody can see is the vacuity this whole
|
|
190
|
+
* channel is required to avoid. Never throws.
|
|
191
|
+
*/
|
|
192
|
+
export declare function listPromotionExempt(opts: {
|
|
193
|
+
projectRoot: string;
|
|
194
|
+
}): PromotionExemptEntry[];
|
|
81
195
|
/**
|
|
82
196
|
* Generate the code stub for a given layer. Returns a Markdown
|
|
83
197
|
* snippet the LLM / human can paste into the appropriate file. Pure
|
|
@@ -97,7 +211,16 @@ export type FeedbackPromoteEnvelope = {
|
|
|
97
211
|
feedbackPath: string;
|
|
98
212
|
layer: PromotionLayer;
|
|
99
213
|
layerDetail: string;
|
|
214
|
+
/**
|
|
215
|
+
* Files this call actually wrote. rid 2026-09-14-gate-h-promotion: this used
|
|
216
|
+
* to be the stub's *targets* — paths the command never wrote yet printed as
|
|
217
|
+
* `Generated files:` — which is the defect the Gate H rework exists to remove.
|
|
218
|
+
*/
|
|
100
219
|
generatedFiles: string[];
|
|
220
|
+
/** The artifact(s) `layer` requires before the promotion means anything. */
|
|
221
|
+
requiredArtifacts: string[];
|
|
222
|
+
/** `false` when `requiredArtifacts` are not all present — the marker is then a claim without evidence. */
|
|
223
|
+
effective: boolean;
|
|
101
224
|
snippet: string;
|
|
102
225
|
promotedAt: string;
|
|
103
226
|
promotedBy: string;
|
|
@@ -119,5 +242,5 @@ export declare function promoteFeedback(opts: {
|
|
|
119
242
|
sessionId: string;
|
|
120
243
|
projectRoot: string;
|
|
121
244
|
dryRun?: boolean;
|
|
122
|
-
}): FeedbackPromoteEnvelope
|
|
245
|
+
}): Promise<FeedbackPromoteEnvelope>;
|
|
123
246
|
export declare function isPromotionLayer(value: string): value is PromotionLayer;
|