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
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
*
|
|
9
9
|
* File budget: ≤ 400 lines (rid-006 split).
|
|
10
10
|
*/
|
|
11
|
-
import type
|
|
11
|
+
import { type ArtifactPrerequisite, type RequestType } from '../artifacts/artifact-prerequisites.js';
|
|
12
|
+
import type { RequestArtifactRole, RequestArtifactState } from '../artifacts/request-artifact-service.js';
|
|
12
13
|
import type { PipelineGate } from './pipeline-verify-types.js';
|
|
13
14
|
export declare function extractState(markdown: string): string;
|
|
14
15
|
/**
|
|
@@ -24,6 +25,38 @@ export declare function findRequestFile(projectRoot: string, role: string, rid:
|
|
|
24
25
|
content: string;
|
|
25
26
|
sessionId: string;
|
|
26
27
|
} | null>;
|
|
28
|
+
/**
|
|
29
|
+
* Where the CURRENT contract puts one gate's evidence.
|
|
30
|
+
*
|
|
31
|
+
* The contract is the `artifact-prerequisites.ts` table — the table
|
|
32
|
+
* `peaks request transition` enforces and the one the peaks-qa prose
|
|
33
|
+
* contract mirrors (`skills/bee/peaks-qa/references/qa-transition-gates.md`).
|
|
34
|
+
* `probeName` is the location this checker probes today. The contract's
|
|
35
|
+
* `relativePath` is returned first, then its accepted historical locations in
|
|
36
|
+
* the table's own declared order — `legacyRelativePath`, then
|
|
37
|
+
* `legacyRelativePaths` (the same composition `artifact-prerequisites.ts`' own
|
|
38
|
+
* resolver performs, so a hit here and a hit there land on the same file).
|
|
39
|
+
*
|
|
40
|
+
* Returns `null` when the contract carries no prerequisite for that name at
|
|
41
|
+
* all — the artifact was retired and no gate may fail on it.
|
|
42
|
+
*
|
|
43
|
+
* This lookup exists because hard-coding the paths here is what let
|
|
44
|
+
* `verify-pipeline` fall a minor release behind the gate: it kept demanding
|
|
45
|
+
* `rd/security-review.md` as mandatory after the table had demoted that file
|
|
46
|
+
* to a `legacyRelativePath` fallback of `audit/security.md`, and it kept
|
|
47
|
+
* demanding `qa/security-findings-<rid>.md` / `qa/performance-findings-<rid>.md`
|
|
48
|
+
* after the v2.11.0 D1/D4 trim dropped them from `qa:verdict-issued`
|
|
49
|
+
* altogether (rid 2026-09-14-verify-pipeline-contract-drift).
|
|
50
|
+
*/
|
|
51
|
+
/**
|
|
52
|
+
* The prerequisite the contract carries for `probeName`, or null when it
|
|
53
|
+
* carries none. `contractEvidencePaths` is this object's path projection; the
|
|
54
|
+
* BODY contract (`mustContain` / `mustContainAny` / `headingMustContain`) rides
|
|
55
|
+
* on the same object, which is why `contractBodyViolations` below consults it
|
|
56
|
+
* instead of probing for the file's existence alone.
|
|
57
|
+
*/
|
|
58
|
+
export declare function contractPrerequisite(role: RequestArtifactRole, state: RequestArtifactState, requestType: RequestType, probeName: string): ArtifactPrerequisite | null;
|
|
59
|
+
export declare function contractEvidencePaths(role: RequestArtifactRole, state: RequestArtifactState, requestType: RequestType, probeName: string): string[] | null;
|
|
27
60
|
export declare function rdGatesForType(requestType: RequestType): PipelineGate[];
|
|
28
61
|
export declare function qaGatesForType(requestType: RequestType): PipelineGate[];
|
|
29
62
|
export declare const RD_QA_HANDOFF_STATES: Set<string>;
|
|
@@ -33,15 +66,19 @@ export interface CanonicalPathTracker {
|
|
|
33
66
|
anyEvidenceResolved: boolean;
|
|
34
67
|
allResolvedPathsCanonical: boolean;
|
|
35
68
|
}
|
|
36
|
-
/** Resolve RD evidence files (
|
|
69
|
+
/** Resolve RD evidence files (prd-handoff / bug-analysis / code-review / security-review / perf-baseline)
|
|
37
70
|
* by probing canonical + legacy paths for each gate. Mutates the gate.passed /
|
|
38
71
|
* gate.detail fields in place and pushes violations / nextActions. Returns the
|
|
39
72
|
* updated tracker. Verbatim-move from `pipeline-verify-service.ts`. */
|
|
40
|
-
export declare function resolveRdEvidencePaths(gates: PipelineGate[], rdEvidenceDir: string, projectRoot: string, violations: string[], nextActions: string[], tracker: CanonicalPathTracker): CanonicalPathTracker;
|
|
41
|
-
/** Resolve QA evidence files (test-cases / test-report
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
|
|
73
|
+
export declare function resolveRdEvidencePaths(gates: PipelineGate[], rdEvidenceDir: string, projectRoot: string, rid: string, requestType: RequestType, violations: string[], nextActions: string[], tracker: CanonicalPathTracker): CanonicalPathTracker;
|
|
74
|
+
/** Resolve QA evidence files (test-cases / test-report) by probing canonical
|
|
75
|
+
* + legacy paths for each gate. Mutates the gate.passed / gate.detail fields
|
|
76
|
+
* in place and pushes violations / nextActions. Returns the updated tracker.
|
|
77
|
+
* Verbatim-move from `pipeline-verify-service.ts`.
|
|
78
|
+
*
|
|
79
|
+
* The `security-findings` / `performance-findings` branches that used to live
|
|
80
|
+
* here are gone: those artifacts are not in any `qa:verdict-issued` table
|
|
81
|
+
* (v2.11.0 D1/D4), and the evidence they stood for is resolved on the RD side
|
|
82
|
+
* at the contract's own paths (`audit/security-<rid>.md`,
|
|
83
|
+
* `audit/perf-<rid>.md`). */
|
|
84
|
+
export declare function resolveQaEvidencePaths(gates: PipelineGate[], projectRoot: string, rdEvidenceDir: string, requestType: RequestType, rid: string, violations: string[], nextActions: string[], tracker: CanonicalPathTracker): CanonicalPathTracker;
|
|
@@ -8,17 +8,13 @@
|
|
|
8
8
|
*
|
|
9
9
|
* File budget: ≤ 400 lines (rid-006 split).
|
|
10
10
|
*/
|
|
11
|
-
import { existsSync } from 'node:fs';
|
|
11
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
12
12
|
import { join } from 'node:path';
|
|
13
|
+
import { getPrerequisitesFor, prerequisiteBodyViolations } from '../artifacts/artifact-prerequisites.js';
|
|
13
14
|
import { showRequestArtifact } from '../artifacts/request-artifact-service.js';
|
|
14
|
-
import {
|
|
15
|
+
import { readArtifactState } from '../artifacts/request-artifact-state-helpers.js';
|
|
15
16
|
export function extractState(markdown) {
|
|
16
|
-
|
|
17
|
-
const match = /^-\s*state:\s*(.+?)\s*$/.exec(rawLine.trim());
|
|
18
|
-
if (match?.[1])
|
|
19
|
-
return match[1];
|
|
20
|
-
}
|
|
21
|
-
return 'unknown';
|
|
17
|
+
return readArtifactState(markdown) ?? 'unknown';
|
|
22
18
|
}
|
|
23
19
|
/**
|
|
24
20
|
* As of slice 2026-06-05-change-id-as-unit-of-work, the file's durable
|
|
@@ -32,24 +28,102 @@ export async function findRequestFile(projectRoot, role, rid) {
|
|
|
32
28
|
const artifact = await showRequestArtifact({ projectRoot, role: role, requestId: rid });
|
|
33
29
|
if (artifact === null)
|
|
34
30
|
return null;
|
|
35
|
-
// Slice 2026-06-28-code-mode-bypass-fix (defect #3)
|
|
36
|
-
// `showRequestArtifact`
|
|
37
|
-
// as `sessionId
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
31
|
+
// Slice 2026-06-28-code-mode-bypass-fix (defect #3) used to strip a
|
|
32
|
+
// `_runtime/` prefix here, because `showRequestArtifact` then returned the
|
|
33
|
+
// FULL SCOPE (`_runtime/<sid>`) as `sessionId`. Repair R5 removed that
|
|
34
|
+
// round-trip at its source: `readSummary` now builds the summary from the
|
|
35
|
+
// already-resolved directory and the bare id, so `sessionId` is the bare id
|
|
36
|
+
// and the prefix strip could never fire. It was removed by repair R7 rather
|
|
37
|
+
// than left in place as dead code with a comment asserting a behaviour its
|
|
38
|
+
// callee no longer has — an artifact claiming something the code does not do
|
|
39
|
+
// is the defect class this line of work exists to remove.
|
|
40
|
+
return { path: artifact.path, content: artifact.content, sessionId: artifact.sessionId };
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Where the CURRENT contract puts one gate's evidence.
|
|
44
|
+
*
|
|
45
|
+
* The contract is the `artifact-prerequisites.ts` table — the table
|
|
46
|
+
* `peaks request transition` enforces and the one the peaks-qa prose
|
|
47
|
+
* contract mirrors (`skills/bee/peaks-qa/references/qa-transition-gates.md`).
|
|
48
|
+
* `probeName` is the location this checker probes today. The contract's
|
|
49
|
+
* `relativePath` is returned first, then its accepted historical locations in
|
|
50
|
+
* the table's own declared order — `legacyRelativePath`, then
|
|
51
|
+
* `legacyRelativePaths` (the same composition `artifact-prerequisites.ts`' own
|
|
52
|
+
* resolver performs, so a hit here and a hit there land on the same file).
|
|
53
|
+
*
|
|
54
|
+
* Returns `null` when the contract carries no prerequisite for that name at
|
|
55
|
+
* all — the artifact was retired and no gate may fail on it.
|
|
56
|
+
*
|
|
57
|
+
* This lookup exists because hard-coding the paths here is what let
|
|
58
|
+
* `verify-pipeline` fall a minor release behind the gate: it kept demanding
|
|
59
|
+
* `rd/security-review.md` as mandatory after the table had demoted that file
|
|
60
|
+
* to a `legacyRelativePath` fallback of `audit/security.md`, and it kept
|
|
61
|
+
* demanding `qa/security-findings-<rid>.md` / `qa/performance-findings-<rid>.md`
|
|
62
|
+
* after the v2.11.0 D1/D4 trim dropped them from `qa:verdict-issued`
|
|
63
|
+
* altogether (rid 2026-09-14-verify-pipeline-contract-drift).
|
|
64
|
+
*/
|
|
65
|
+
/**
|
|
66
|
+
* The prerequisite the contract carries for `probeName`, or null when it
|
|
67
|
+
* carries none. `contractEvidencePaths` is this object's path projection; the
|
|
68
|
+
* BODY contract (`mustContain` / `mustContainAny` / `headingMustContain`) rides
|
|
69
|
+
* on the same object, which is why `contractBodyViolations` below consults it
|
|
70
|
+
* instead of probing for the file's existence alone.
|
|
71
|
+
*/
|
|
72
|
+
export function contractPrerequisite(role, state, requestType, probeName) {
|
|
73
|
+
for (const prereq of getPrerequisitesFor(role, state, requestType)) {
|
|
74
|
+
const legacyPaths = [
|
|
75
|
+
...(prereq.legacyRelativePath !== undefined ? [prereq.legacyRelativePath] : []),
|
|
76
|
+
...(prereq.legacyRelativePaths ?? [])
|
|
77
|
+
];
|
|
78
|
+
if (prereq.relativePath !== probeName && !legacyPaths.includes(probeName))
|
|
79
|
+
continue;
|
|
80
|
+
return prereq;
|
|
44
81
|
}
|
|
45
|
-
return
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
export function contractEvidencePaths(role, state, requestType, probeName) {
|
|
85
|
+
const prereq = contractPrerequisite(role, state, requestType, probeName);
|
|
86
|
+
if (prereq === null)
|
|
87
|
+
return null;
|
|
88
|
+
const legacyPaths = [
|
|
89
|
+
...(prereq.legacyRelativePath !== undefined ? [prereq.legacyRelativePath] : []),
|
|
90
|
+
...(prereq.legacyRelativePaths ?? [])
|
|
91
|
+
];
|
|
92
|
+
return [prereq.relativePath, ...legacyPaths.filter((legacy) => legacy !== prereq.relativePath)];
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The contract's BODY checks for the prerequisite named by `probeName`, applied
|
|
96
|
+
* to the file that resolved. Empty when the name matches nothing in the table or
|
|
97
|
+
* the table pins no body markers for it.
|
|
98
|
+
*
|
|
99
|
+
* `existsSync` alone made this checker laxer than the contract it claims to
|
|
100
|
+
* derive from: it reported `prd-handoff passed = true` for a handoff carrying
|
|
101
|
+
* `schemaVersion: 1` and no `sha256:` line — the exact file
|
|
102
|
+
* `AUDIT_REQUIRES_HANDOFF` refuses at `rd:qa-handoff`. Deriving the *path* from
|
|
103
|
+
* the table while dropping the table's *body* contract is the drift this module
|
|
104
|
+
* exists to remove, so the check goes through the table's own implementation
|
|
105
|
+
* (`prerequisiteBodyViolations`) rather than a second copy of the markers.
|
|
106
|
+
*/
|
|
107
|
+
function contractBodyViolations(role, state, requestType, probeName, absolutePath) {
|
|
108
|
+
const prerequisite = contractPrerequisite(role, state, requestType, probeName);
|
|
109
|
+
if (prerequisite === null)
|
|
110
|
+
return [];
|
|
111
|
+
return prerequisiteBodyViolations(prerequisite, readFileSync(absolutePath, 'utf8'));
|
|
46
112
|
}
|
|
47
113
|
export function rdGatesForType(requestType) {
|
|
48
114
|
const gates = [
|
|
49
115
|
{ name: 'rd-request-exists', description: 'RD request artifact created', passed: false, detail: '' }
|
|
50
116
|
];
|
|
51
|
-
|
|
52
|
-
|
|
117
|
+
// This slot used to pin a `tech-doc` gate to `rd/tech-doc.md`, an artifact
|
|
118
|
+
// v2.11.0 Group A retired and the table no longer carries — so the gate
|
|
119
|
+
// failed trees `peaks request transition` accepts (QA repair cycle 1). The
|
|
120
|
+
// design / scope record the contract DOES name at `rd:qa-handoff` is
|
|
121
|
+
// `prd/handoff.md`, so that is the gate: table-derived, like the others.
|
|
122
|
+
// (`rd/tech-doc.md` still has two non-gate readers — final-review's evidence
|
|
123
|
+
// budget and the resume detector. Neither is served by failing a compliant
|
|
124
|
+
// slice.)
|
|
125
|
+
if (contractEvidencePaths('rd', 'qa-handoff', requestType, 'prd/handoff.md') !== null) {
|
|
126
|
+
gates.push({ name: 'prd-handoff', description: 'PRD handoff capsule (approved scope + non-goals)', passed: false, detail: '' });
|
|
53
127
|
}
|
|
54
128
|
if (requestType === 'bugfix') {
|
|
55
129
|
gates.push({ name: 'bug-analysis', description: 'Bug root-cause analysis', passed: false, detail: '' });
|
|
@@ -60,6 +134,17 @@ export function rdGatesForType(requestType) {
|
|
|
60
134
|
if (requestType === 'feature' || requestType === 'refactor' || requestType === 'bugfix' || requestType === 'config') {
|
|
61
135
|
gates.push({ name: 'security-review', description: 'Security review evidence', passed: false, detail: '' });
|
|
62
136
|
}
|
|
137
|
+
// The perf evidence the contract requires at `rd:qa-handoff` is `AUDIT_PERF`
|
|
138
|
+
// — `audit/perf-<rid>.md`, with `audit/perf.md` and `rd/perf-baseline.md` as
|
|
139
|
+
// its two declared legacy tiers (rid-scoped since slice
|
|
140
|
+
// `2026-09-14-audit-artifact-rid-scoping`). This gate used to live on the QA
|
|
141
|
+
// side as `performance-findings`, checking `qa/performance-findings-<rid>.md`
|
|
142
|
+
// — a path the v2.11.0 D1/D4 trim dropped. Relocating the gate (rather than
|
|
143
|
+
// deleting it) is what keeps AC3's control meaningful: perf evidence that is
|
|
144
|
+
// genuinely absent must still fail.
|
|
145
|
+
if (contractEvidencePaths('rd', 'qa-handoff', requestType, 'rd/perf-baseline.md') !== null) {
|
|
146
|
+
gates.push({ name: 'perf-baseline', description: 'Performance audit evidence', passed: false, detail: '' });
|
|
147
|
+
}
|
|
63
148
|
return gates;
|
|
64
149
|
}
|
|
65
150
|
export function qaGatesForType(requestType) {
|
|
@@ -70,124 +155,157 @@ export function qaGatesForType(requestType) {
|
|
|
70
155
|
gates.push({ name: 'test-cases', description: 'QA test cases', passed: false, detail: '' });
|
|
71
156
|
gates.push({ name: 'test-report', description: 'QA test report with execution results', passed: false, detail: '' });
|
|
72
157
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
158
|
+
// The `security-findings` / `performance-findings` gates were removed here
|
|
159
|
+
// (rid 2026-09-14-verify-pipeline-contract-drift). peaks-qa does not own
|
|
160
|
+
// security review or performance review — `peaks-qa/SKILL.md` says so, and
|
|
161
|
+
// the v2.11.0 D1/D4 trim dropped both from every `qa:verdict-issued` table.
|
|
162
|
+
// The evidence they used to demand is now checked on the RD side, at the
|
|
163
|
+
// paths the contract actually names (`audit/security-<rid>.md`,
|
|
164
|
+
// `audit/perf-<rid>.md`; rid-scoped since slice
|
|
165
|
+
// `2026-09-14-audit-artifact-rid-scoping`, with `audit/security.md` /
|
|
166
|
+
// `audit/perf.md` and `rd/security-review.md` / `rd/perf-baseline.md` as the
|
|
167
|
+
// declared legacy tiers behind them).
|
|
79
168
|
return gates;
|
|
80
169
|
}
|
|
81
170
|
export const RD_QA_HANDOFF_STATES = new Set(['qa-handoff', 'handed-off', 'implemented']);
|
|
82
171
|
export const QA_COMPLETE_STATES = new Set(['verdict-issued']);
|
|
83
|
-
/** Resolve RD evidence files (
|
|
172
|
+
/** Resolve RD evidence files (prd-handoff / bug-analysis / code-review / security-review / perf-baseline)
|
|
84
173
|
* by probing canonical + legacy paths for each gate. Mutates the gate.passed /
|
|
85
174
|
* gate.detail fields in place and pushes violations / nextActions. Returns the
|
|
86
175
|
* updated tracker. Verbatim-move from `pipeline-verify-service.ts`. */
|
|
87
|
-
export function resolveRdEvidencePaths(gates, rdEvidenceDir, projectRoot, violations, nextActions, tracker) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
176
|
+
export function resolveRdEvidencePaths(gates, rdEvidenceDir, projectRoot, rid, requestType, violations, nextActions, tracker) {
|
|
177
|
+
// The path each gate is about. The contract is consulted first — see
|
|
178
|
+
// `contractEvidencePaths`; `rdGatesForType` only builds a gate when the
|
|
179
|
+
// contract carries its prerequisite, so this is the name the gate stands
|
|
180
|
+
// for, not a second path table.
|
|
181
|
+
const RD_EVIDENCE_PROBE = {
|
|
182
|
+
'prd-handoff': 'prd/handoff.md',
|
|
183
|
+
'bug-analysis': 'rd/bug-analysis.md',
|
|
184
|
+
'code-review': 'rd/code-review.md',
|
|
185
|
+
'security-review': 'rd/security-review.md',
|
|
186
|
+
'perf-baseline': 'rd/perf-baseline.md'
|
|
93
187
|
};
|
|
94
188
|
let anyEvidenceResolved = tracker.anyEvidenceResolved;
|
|
95
189
|
let allResolvedPathsCanonical = tracker.allResolvedPathsCanonical;
|
|
96
190
|
for (const gate of gates.slice(1)) {
|
|
97
|
-
const
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
191
|
+
const probeName = RD_EVIDENCE_PROBE[gate.name];
|
|
192
|
+
const relativePaths = (contractEvidencePaths('rd', 'qa-handoff', requestType, probeName) ?? [probeName])
|
|
193
|
+
.map((relative) => relative.replace('<rid>', rid));
|
|
194
|
+
const primaryRelativePath = relativePaths[0];
|
|
195
|
+
const canonicalPath = join(projectRoot, '.peaks', '_runtime', rdEvidenceDir, primaryRelativePath);
|
|
196
|
+
// Two independent axes, kept apart because they mean different things:
|
|
197
|
+
// - `legacyRoot` — the file sits under a pre-F3 session home
|
|
198
|
+
// (`.peaks/<sid>/…` or `.peaks/_runtime/change/<sid>/…`). That is a
|
|
199
|
+
// misplaced write, and it is the DEPRECATION violation's subject.
|
|
200
|
+
// - `legacyForm` — the file sits under one of the contract's declared
|
|
201
|
+
// `legacyRelativePaths`. The contract accepts those on purpose ("a
|
|
202
|
+
// legacy hit must keep the gate open, or existing sessions would fail
|
|
203
|
+
// the transition on upgrade"), so this is reported through
|
|
204
|
+
// `acceptedForm` and is *not* a violation.
|
|
205
|
+
const candidates = [];
|
|
206
|
+
for (const relative of relativePaths) {
|
|
207
|
+
const legacyForm = relative !== primaryRelativePath;
|
|
208
|
+
for (const [root, legacyRoot] of [
|
|
209
|
+
[join(projectRoot, '.peaks', '_runtime', rdEvidenceDir), false],
|
|
210
|
+
[join(projectRoot, '.peaks', rdEvidenceDir), true],
|
|
211
|
+
[join(projectRoot, '.peaks', '_runtime', 'change', rdEvidenceDir), true]
|
|
212
|
+
]) {
|
|
213
|
+
candidates.push({ path: join(root, relative), legacyRoot, legacyForm });
|
|
108
214
|
}
|
|
109
215
|
}
|
|
110
|
-
|
|
216
|
+
const hit = candidates.find((candidate) => existsSync(candidate.path));
|
|
217
|
+
if (hit !== undefined) {
|
|
111
218
|
anyEvidenceResolved = true;
|
|
112
|
-
if (
|
|
219
|
+
if (hit.legacyRoot)
|
|
113
220
|
allResolvedPathsCanonical = false;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
221
|
+
// Existing is not the contract — the file must also satisfy the body
|
|
222
|
+
// markers the table pins for this prereq (see `contractBodyViolations`).
|
|
223
|
+
const bodyViolations = contractBodyViolations('rd', 'qa-handoff', requestType, probeName, hit.path);
|
|
224
|
+
if (bodyViolations.length > 0) {
|
|
225
|
+
gate.passed = false;
|
|
226
|
+
gate.detail = `${hit.path} — ${bodyViolations.join('; ')}`;
|
|
227
|
+
violations.push(`RD evidence does not satisfy the contract: ${gate.description} (${primaryRelativePath}) — ${bodyViolations.join('; ')}`);
|
|
228
|
+
nextActions.push(`Fix .peaks/_runtime/${rdEvidenceDir}/${primaryRelativePath} to satisfy the contract: ${bodyViolations.join('; ')}`);
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
gate.passed = true;
|
|
232
|
+
gate.detail = hit.path
|
|
233
|
+
+ (hit.legacyRoot ? ' [DEPRECATION_LEGACY_PATH_USED]' : '')
|
|
234
|
+
+ (hit.legacyForm ? ' [LEGACY_EVIDENCE_PATH]' : '');
|
|
235
|
+
}
|
|
236
|
+
if (hit.legacyRoot) {
|
|
237
|
+
violations.push(`DEPRECATION_LEGACY_PATH_USED: ${hit.path} — move the file into .peaks/_runtime/${rdEvidenceDir}/${primaryRelativePath} (the canonical location) so subsequent runs resolve on the canonical path. The legacy \`peaks workspace migrate-change-scope\` helper was removed in v2.19.0; use \`peaks workspace migrate\` to relocate misplaced content.`);
|
|
118
238
|
}
|
|
119
239
|
}
|
|
120
240
|
else {
|
|
121
241
|
gate.detail = `missing: ${canonicalPath}`;
|
|
122
|
-
violations.push(`RD evidence missing: ${gate.description} (${
|
|
123
|
-
nextActions.push(`Create .peaks/_runtime/${rdEvidenceDir}
|
|
242
|
+
violations.push(`RD evidence missing: ${gate.description} (${primaryRelativePath})`);
|
|
243
|
+
nextActions.push(`Create .peaks/_runtime/${rdEvidenceDir}/${primaryRelativePath}`);
|
|
124
244
|
}
|
|
125
245
|
}
|
|
126
246
|
return { anyEvidenceResolved, allResolvedPathsCanonical };
|
|
127
247
|
}
|
|
128
|
-
/** Resolve QA evidence files (test-cases / test-report
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
248
|
+
/** Resolve QA evidence files (test-cases / test-report) by probing canonical
|
|
249
|
+
* + legacy paths for each gate. Mutates the gate.passed / gate.detail fields
|
|
250
|
+
* in place and pushes violations / nextActions. Returns the updated tracker.
|
|
251
|
+
* Verbatim-move from `pipeline-verify-service.ts`.
|
|
252
|
+
*
|
|
253
|
+
* The `security-findings` / `performance-findings` branches that used to live
|
|
254
|
+
* here are gone: those artifacts are not in any `qa:verdict-issued` table
|
|
255
|
+
* (v2.11.0 D1/D4), and the evidence they stood for is resolved on the RD side
|
|
256
|
+
* at the contract's own paths (`audit/security-<rid>.md`,
|
|
257
|
+
* `audit/perf-<rid>.md`). */
|
|
258
|
+
export function resolveQaEvidencePaths(gates, projectRoot, rdEvidenceDir, requestType, rid, violations, nextActions, tracker) {
|
|
259
|
+
const QA_EVIDENCE_PROBE = {
|
|
260
|
+
'test-cases': 'qa/test-cases/<rid>.md',
|
|
261
|
+
'test-report': 'qa/test-reports/<rid>.md'
|
|
140
262
|
};
|
|
141
263
|
let anyEvidenceResolved = tracker.anyEvidenceResolved;
|
|
142
264
|
let allResolvedPathsCanonical = tracker.allResolvedPathsCanonical;
|
|
143
265
|
for (const gate of gates.slice(1)) {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
else {
|
|
158
|
-
gate.detail = `missing: ${resolved.path}`;
|
|
159
|
-
violations.push(`QA evidence missing: ${gate.description} (${resolved.path})`);
|
|
160
|
-
nextActions.push(`Create ${resolved.path} (or use the legacy non-suffixed form during the 1-minor-release back-compat window)`);
|
|
161
|
-
}
|
|
162
|
-
continue;
|
|
163
|
-
}
|
|
164
|
-
const fileName = QA_EVIDENCE_FILE[gate.name];
|
|
165
|
-
const canonicalQaPath = join(projectRoot, '.peaks', '_runtime', rdEvidenceDir, 'qa', fileName);
|
|
166
|
-
const legacyMisplacedQaPath = join(projectRoot, '.peaks', rdEvidenceDir, 'qa', fileName);
|
|
167
|
-
const legacyChangeAxisQaPath = join(projectRoot, '.peaks', '_runtime', 'change', rdEvidenceDir, 'qa', fileName);
|
|
168
|
-
let resolvedQaPath = null;
|
|
169
|
-
let usedLegacyQa = false;
|
|
170
|
-
for (const candidate of [canonicalQaPath, legacyMisplacedQaPath, legacyChangeAxisQaPath]) {
|
|
171
|
-
if (existsSync(candidate)) {
|
|
172
|
-
resolvedQaPath = candidate;
|
|
173
|
-
usedLegacyQa = candidate !== canonicalQaPath;
|
|
174
|
-
break;
|
|
266
|
+
const probeName = QA_EVIDENCE_PROBE[gate.name];
|
|
267
|
+
const relativePaths = (contractEvidencePaths('qa', 'verdict-issued', requestType, probeName) ?? [probeName])
|
|
268
|
+
.map((relative) => relative.replace('<rid>', rid));
|
|
269
|
+
const primaryRelativePath = relativePaths[0];
|
|
270
|
+
const canonicalQaPath = join(projectRoot, '.peaks', '_runtime', rdEvidenceDir, primaryRelativePath);
|
|
271
|
+
const candidates = [];
|
|
272
|
+
for (const relative of relativePaths) {
|
|
273
|
+
for (const [root, legacyRoot] of [
|
|
274
|
+
[join(projectRoot, '.peaks', '_runtime', rdEvidenceDir), false],
|
|
275
|
+
[join(projectRoot, '.peaks', rdEvidenceDir), true],
|
|
276
|
+
[join(projectRoot, '.peaks', '_runtime', 'change', rdEvidenceDir), true]
|
|
277
|
+
]) {
|
|
278
|
+
candidates.push({ path: join(root, relative), legacyRoot });
|
|
175
279
|
}
|
|
176
280
|
}
|
|
177
|
-
|
|
281
|
+
const hit = candidates.find((candidate) => existsSync(candidate.path));
|
|
282
|
+
if (hit !== undefined) {
|
|
178
283
|
anyEvidenceResolved = true;
|
|
179
|
-
if (
|
|
284
|
+
if (hit.legacyRoot)
|
|
180
285
|
allResolvedPathsCanonical = false;
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
286
|
+
// Same contract check as the RD side: the `test-report` / `test-cases`
|
|
287
|
+
// prereqs pin `## Test execution` / `## Test cases` / `test(`, and a
|
|
288
|
+
// checker that only probed existence reported green on files the
|
|
289
|
+
// `qa:verdict-issued` gate rejects.
|
|
290
|
+
const bodyViolations = contractBodyViolations('qa', 'verdict-issued', requestType, probeName, hit.path);
|
|
291
|
+
if (bodyViolations.length > 0) {
|
|
292
|
+
gate.passed = false;
|
|
293
|
+
gate.detail = `${hit.path} — ${bodyViolations.join('; ')}`;
|
|
294
|
+
violations.push(`QA evidence does not satisfy the contract: ${gate.description} (${primaryRelativePath}) — ${bodyViolations.join('; ')}`);
|
|
295
|
+
nextActions.push(`Fix .peaks/_runtime/${rdEvidenceDir}/${primaryRelativePath} to satisfy the contract: ${bodyViolations.join('; ')}`);
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
gate.passed = true;
|
|
299
|
+
gate.detail = hit.path + (hit.legacyRoot ? ' [DEPRECATION_LEGACY_PATH_USED]' : '');
|
|
300
|
+
}
|
|
301
|
+
if (hit.legacyRoot) {
|
|
302
|
+
violations.push(`DEPRECATION_LEGACY_PATH_USED: ${hit.path} — move the file into .peaks/_runtime/${rdEvidenceDir}/${primaryRelativePath} (the canonical location) so subsequent runs resolve on the canonical path. The legacy \`peaks workspace migrate-change-scope\` helper was removed in v2.19.0; use \`peaks workspace migrate\` to relocate misplaced content.`);
|
|
185
303
|
}
|
|
186
304
|
}
|
|
187
305
|
else {
|
|
188
306
|
gate.detail = `missing: ${canonicalQaPath}`;
|
|
189
|
-
violations.push(`QA evidence missing: ${gate.description} (${
|
|
190
|
-
nextActions.push(`Create .peaks/_runtime/${rdEvidenceDir}
|
|
307
|
+
violations.push(`QA evidence missing: ${gate.description} (${primaryRelativePath})`);
|
|
308
|
+
nextActions.push(`Create .peaks/_runtime/${rdEvidenceDir}/${primaryRelativePath}`);
|
|
191
309
|
}
|
|
192
310
|
}
|
|
193
311
|
return { anyEvidenceResolved, allResolvedPathsCanonical };
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* returns a structured `PipelineVerification` envelope. Type
|
|
6
6
|
* declarations live in `pipeline-verify-types.ts`; private gate
|
|
7
7
|
* helpers (`rdGatesForType`, `qaGatesForType`, `extractState`,
|
|
8
|
-
* `findRequestFile`, the
|
|
8
|
+
* `findRequestFile`, the RD/QA handoff
|
|
9
9
|
* state sets, and the RD/QA evidence path probes) live in
|
|
10
10
|
* `pipeline-verify-gate-support.ts`. The re-export shim at the
|
|
11
11
|
* bottom preserves the original public surface so existing import
|