peaks-loop 4.0.48 → 4.0.50
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/dist/cli/commands/audit-commands.js +1 -0
- package/dist/cli/commands/baseline-commands.js +163 -25
- package/dist/cli/commands/compact-command.js +1 -3
- package/dist/cli/commands/core/skill-command.js +53 -4
- package/dist/cli/commands/core/standards-command.d.ts +24 -0
- package/dist/cli/commands/core/standards-command.js +74 -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 +55 -38
- package/dist/cli/commands/loop-eval-commands.js +22 -6
- package/dist/cli/commands/share-commands.js +37 -11
- package/dist/cli/commands/slice-integrate-commands.js +17 -0
- package/dist/cli/commands/web-commands.js +8 -1
- package/dist/cli/commands/workflow-lifecycle-commands.d.ts +6 -0
- package/dist/cli/commands/workflow-lifecycle-commands.js +64 -3
- package/dist/services/adapter/adapter.d.ts +30 -0
- package/dist/services/adapter/auto-adapter.d.ts +13 -0
- package/dist/services/adapter/claude-adapter.js +12 -0
- package/dist/services/adapter/codex-adapter.d.ts +12 -0
- package/dist/services/adapter/codex-adapter.js +12 -0
- package/dist/services/adapter/copilot-adapter.d.ts +12 -0
- package/dist/services/adapter/copilot-adapter.js +12 -0
- package/dist/services/artifacts/artifact-prerequisites.js +10 -0
- package/dist/services/artifacts/request-artifact-service.js +59 -38
- package/dist/services/audit/backing-detector.d.ts +25 -7
- package/dist/services/audit/backing-detector.js +33 -17
- package/dist/services/audit/enforcer-liveness.d.ts +12 -0
- package/dist/services/audit/enforcer-liveness.js +100 -0
- package/dist/services/audit/enforcers/active-skill-resolver.js +14 -1
- package/dist/services/audit/enforcers/lint-catalog-governance.d.ts +23 -11
- package/dist/services/audit/enforcers/lint-catalog-governance.js +10 -14
- package/dist/services/audit/enforcers/lint-rd-handoff-coverage.d.ts +5 -15
- package/dist/services/audit/enforcers/lint-rd-handoff-coverage.js +94 -25
- package/dist/services/audit/enforcers/lint-style.d.ts +9 -1
- package/dist/services/audit/enforcers/lint-style.js +38 -2
- package/dist/services/audit/prose-ratio-calculator.d.ts +28 -17
- package/dist/services/audit/prose-ratio-calculator.js +25 -18
- package/dist/services/audit/red-line-catalog-p2-a.js +1 -1
- package/dist/services/audit/red-lines-service.js +51 -7
- package/dist/services/capability-audit-service/independent-checker.d.ts +15 -0
- package/dist/services/capability-audit-service/independent-checker.js +140 -0
- package/dist/services/capability-audit-service/index.d.ts +3 -1
- package/dist/services/capability-audit-service/index.js +1 -0
- package/dist/services/capability-audit-service/runner.d.ts +17 -13
- package/dist/services/capability-audit-service/runner.js +76 -15
- package/dist/services/capability-audit-service/types.d.ts +48 -0
- package/dist/services/capability-guard-runner/contracts/J01.js +21 -22
- package/dist/services/capability-guard-runner/contracts/J02.d.ts +1 -1
- package/dist/services/capability-guard-runner/contracts/J02.js +114 -28
- package/dist/services/capability-guard-runner/contracts/J03.d.ts +13 -0
- package/dist/services/capability-guard-runner/contracts/J03.js +72 -21
- package/dist/services/capability-guard-runner/contracts/J04.d.ts +6 -0
- package/dist/services/capability-guard-runner/contracts/J04.js +65 -32
- package/dist/services/capability-guard-runner/contracts/J05.js +118 -16
- package/dist/services/capability-guard-runner/contracts/J06.d.ts +14 -0
- package/dist/services/capability-guard-runner/contracts/J06.js +57 -39
- package/dist/services/capability-guard-runner/contracts/J07.d.ts +9 -0
- package/dist/services/capability-guard-runner/contracts/J07.js +76 -47
- package/dist/services/capability-guard-runner/contracts/J08.d.ts +11 -0
- package/dist/services/capability-guard-runner/contracts/J08.js +66 -39
- package/dist/services/capability-guard-runner/contracts/J09.d.ts +13 -0
- package/dist/services/capability-guard-runner/contracts/J09.js +95 -39
- package/dist/services/capability-guard-runner/contracts/J10.d.ts +12 -0
- package/dist/services/capability-guard-runner/contracts/J10.js +69 -35
- package/dist/services/capability-guard-runner/contracts/J11.d.ts +8 -0
- package/dist/services/capability-guard-runner/contracts/J11.js +73 -33
- package/dist/services/capability-guard-runner/contracts/J12.d.ts +12 -0
- package/dist/services/capability-guard-runner/contracts/J12.js +66 -30
- package/dist/services/capability-guard-runner/contracts/J13.d.ts +11 -0
- package/dist/services/capability-guard-runner/contracts/J13.js +62 -40
- package/dist/services/capability-guard-runner/contracts/J14.d.ts +11 -0
- package/dist/services/capability-guard-runner/contracts/J14.js +60 -31
- package/dist/services/capability-guard-runner/contracts/J15.d.ts +11 -0
- package/dist/services/capability-guard-runner/contracts/J15.js +70 -35
- package/dist/services/capability-guard-runner/contracts/_shared.d.ts +24 -0
- package/dist/services/capability-guard-runner/contracts/_shared.js +67 -0
- package/dist/services/capability-guard-runner/registry.d.ts +5 -0
- package/dist/services/capability-guard-runner/registry.js +140 -0
- package/dist/services/capability-guard-runner/runner.d.ts +26 -0
- package/dist/services/capability-guard-runner/runner.js +63 -6
- package/dist/services/code/auto-compact-lifecycle.d.ts +75 -0
- package/dist/services/code/auto-compact-lifecycle.js +65 -16
- package/dist/services/code/auto-compact-modes.d.ts +13 -2
- package/dist/services/code/auto-compact-modes.js +20 -4
- package/dist/services/code/auto-compact-orchestrator.js +119 -19
- package/dist/services/code/compact-event-settle.d.ts +20 -8
- package/dist/services/code/compact-event-settle.js +21 -0
- package/dist/services/code/post-compact-detector.js +20 -11
- package/dist/services/code/step-08-gate.js +21 -6
- package/dist/services/compact-statusline/compact-statusline-service.js +56 -22
- package/dist/services/config/config-safety.js +11 -9
- package/dist/services/context/auto-compact-types.d.ts +20 -2
- 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/pre-post-diff.js +10 -2
- package/dist/services/job/job-progress-store.js +18 -3
- 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 +11 -4
- package/dist/services/observability/observability-service.js +16 -3
- package/dist/services/prd/handoff-service.js +43 -0
- package/dist/services/qa/qa-business-review-state.js +19 -5
- package/dist/services/sc/sc-service.d.ts +8 -0
- package/dist/services/sc/sc-service.js +8 -1
- package/dist/services/scan/api-diff-types.js +20 -2
- package/dist/services/security/safe-settings-path.js +19 -1
- package/dist/services/session/getSessionDir.d.ts +33 -0
- package/dist/services/session/getSessionDir.js +60 -0
- package/dist/services/skill/skill-search-service.d.ts +3 -3
- package/dist/services/slice/slice-review-state.js +19 -4
- package/dist/services/standards/loop-engineering-lint.d.ts +1 -1
- package/dist/services/standards/loop-engineering-lint.js +6 -0
- package/dist/services/web/daemon-registry.js +27 -2
- package/dist/services/workflow/pipeline-verify-gate-support.js +10 -11
- package/dist/services/workflow/pipeline-verify-service.d.ts +1 -1
- package/dist/services/workflow/pipeline-verify-service.js +23 -10
- package/dist/services/workflow/pipeline-verify-types.d.ts +5 -3
- package/dist/services/workspace/claude-settings-template.d.ts +53 -37
- package/dist/services/workspace/claude-settings-template.js +105 -83
- package/dist/services/workspace/generated-artifacts-stamp.d.ts +119 -0
- package/dist/services/workspace/generated-artifacts-stamp.js +167 -0
- package/dist/services/workspace/workspace-claude-settings-materializer.d.ts +8 -0
- package/dist/services/workspace/workspace-claude-settings-materializer.js +38 -3
- package/dist/services/workspace/workspace-service.js +11 -1
- package/dist/shared/fs-utils.d.ts +26 -0
- package/dist/shared/fs-utils.js +35 -0
- package/dist/shared/runtime-root.d.ts +73 -0
- package/dist/shared/runtime-root.js +77 -0
- package/package.json +9 -7
- package/scripts/copy-templates.mjs +0 -12
- package/scripts/install-skills.mjs +154 -53
- package/skills/bee/peaks-qa/SKILL.md +0 -1
- package/skills/bee/peaks-rd/SKILL.md +0 -1
- package/skills/peaks-code/SKILL.md +12 -10
- package/skills/peaks-code/references/periodic-checkpoint.md +2 -2
- package/skills/peaks-code/references/runbook.md +3 -0
- package/skills/peaks-code/references/session-overload-signal-index.md +4 -2
- package/skills/peaks-code/references/startup-sequence.md +2 -2
- package/skills/peaks-code/references/step-0-8-gate.md +1 -1
- package/skills/peaks-code/references/sub-agent-dispatch.md +19 -19
- package/dist/cli/commands/context-builder-commands.d.ts +0 -11
- package/dist/cli/commands/context-builder-commands.js +0 -85
- package/dist/services/hooks/write-gate.js +0 -111
- package/skills/bee/peaks-prd/references/command-migration.md +0 -3
- package/skills/bee/peaks-qa/references/command-migration.md +0 -3
- package/skills/bee/peaks-rd/references/command-migration.md +0 -3
- package/skills/bee/peaks-sc/references/command-migration.md +0 -3
- package/skills/bee/peaks-txt/references/command-migration.md +0 -3
- package/skills/bee/peaks-ui/references/command-migration.md +0 -3
- package/skills/peaks-code/references/command-migration.md +0 -3
|
@@ -3,11 +3,24 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Two enforcers: catalog size must grow to ≥ 40 (the P2-a target),
|
|
5
5
|
* and the prose-only ratio must stay ≤ 7% (per spec §10.2 L2
|
|
6
|
-
* acceptance
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* acceptance). Both fire on the state the classifier produced — no
|
|
7
|
+
* extra file scan beyond what the audit already did.
|
|
8
|
+
*
|
|
9
|
+
* C6 of the 2026-09-15 diagnosis: this gate and
|
|
10
|
+
* `prose-ratio-calculator.computeProseRatio` (behind
|
|
11
|
+
* `peaks audit prose-ratio`) are the two "prose-only ratio" gates, and
|
|
12
|
+
* they used to disagree. The calculator excluded `informational` rows
|
|
13
|
+
* from its numerator; this one excluded `informational` rows from *its*
|
|
14
|
+
* numerator by a different route (red-lines-service passed it a
|
|
15
|
+
* `proseOnlyCount` that had already been filtered). Both now measure the
|
|
16
|
+
* same quantity, identically defined:
|
|
17
|
+
*
|
|
18
|
+
* numerator = rows classified `backing === 'prose-only'`, all of them
|
|
19
|
+
* denominator = every classified row (`entries.length`)
|
|
20
|
+
*
|
|
21
|
+
* They still carry different *thresholds* — 7% here, 5% as the
|
|
22
|
+
* `peaks audit prose-ratio` default — which is a policy difference, not
|
|
23
|
+
* an accounting one. Nothing redefines what is being counted.
|
|
11
24
|
*/
|
|
12
25
|
import type { LintHit } from './lint-style.js';
|
|
13
26
|
export declare const CATALOG_SIZE_TARGET = 40;
|
|
@@ -22,11 +35,10 @@ export interface CatalogProseOnlyRatio {
|
|
|
22
35
|
}
|
|
23
36
|
export declare function lintCatalogSize(actualSize: number): readonly LintHit[];
|
|
24
37
|
/**
|
|
25
|
-
* Prose-only ratio:
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* `.peaks/memory/2026-06-27-prose-only-catalog-followup.md`).
|
|
38
|
+
* Prose-only ratio: rows the classifier tagged `prose-only`, divided by
|
|
39
|
+
* every row the classifier produced. `catalogSize` is a slight misnomer
|
|
40
|
+
* — it is `entries.length`, the count of classified rows, not the size of
|
|
41
|
+
* the hand-maintained catalog. Same numerator and denominator as
|
|
42
|
+
* `computeProseRatio`; see the module docstring above.
|
|
31
43
|
*/
|
|
32
44
|
export declare function lintCatalogProseOnlyRatio(catalogSize: number, proseOnlyCount: number): readonly LintHit[];
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
export const CATALOG_SIZE_TARGET = 40;
|
|
2
|
-
// v2.12.1
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// fidelity-001/002, mock-placement-001, resume-detection-001,
|
|
8
|
-
// pre-rd-scan-001, design-draft-confirm-001). Bumping the target to
|
|
9
|
-
// 7% acknowledges the reform while keeping the gate active.
|
|
2
|
+
// The v2.12.1 reform left this at 7% by demoting discovered advisory
|
|
3
|
+
// rows out of the numerator. S3 of the 2026-09-15 diagnosis-remediation
|
|
4
|
+
// job removed that demotion, so the observed ratio is 66% (101/153) —
|
|
5
|
+
// the gate now fires, which is the intended outcome: the number was
|
|
6
|
+
// always this bad, it was just being reported as 0%.
|
|
10
7
|
export const PROSE_ONLY_RATIO_TARGET = 0.07;
|
|
11
8
|
function syntheticHit(catalogId, rule, matched) {
|
|
12
9
|
// No specific file to point at — return a synthetic hit against
|
|
@@ -31,12 +28,11 @@ export function lintCatalogSize(actualSize) {
|
|
|
31
28
|
return [syntheticHit('rl-catalog-total-001', 'Catalog governance: catalog size must grow to ≥ 40 (L2.3 P2-a target)', `(catalog size ${actualSize} < target ${CATALOG_SIZE_TARGET})`)];
|
|
32
29
|
}
|
|
33
30
|
/**
|
|
34
|
-
* Prose-only ratio:
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* `.peaks/memory/2026-06-27-prose-only-catalog-followup.md`).
|
|
31
|
+
* Prose-only ratio: rows the classifier tagged `prose-only`, divided by
|
|
32
|
+
* every row the classifier produced. `catalogSize` is a slight misnomer
|
|
33
|
+
* — it is `entries.length`, the count of classified rows, not the size of
|
|
34
|
+
* the hand-maintained catalog. Same numerator and denominator as
|
|
35
|
+
* `computeProseRatio`; see the module docstring above.
|
|
40
36
|
*/
|
|
41
37
|
export function lintCatalogProseOnlyRatio(catalogSize, proseOnlyCount) {
|
|
42
38
|
if (catalogSize === 0)
|
|
@@ -1,18 +1,8 @@
|
|
|
1
|
+
import type { LintHit, SkillFile } from './lint-style.js';
|
|
1
2
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* - md-121 : "do not hand off to QA without [tech-doc.md]" (BLOCKING)
|
|
6
|
-
* - md-127 : "do not hand off to QA without a perf-baseline" (BLOCKING)
|
|
7
|
-
* - md-162 : "100% coverage target on testable files is meaningful"
|
|
8
|
-
*
|
|
9
|
-
* Two enforcers (handoff + coverage). The handoff enforcer
|
|
10
|
-
* checks for both 'tech-doc' and 'perf-baseline' handoff
|
|
11
|
-
* markers; the coverage enforcer checks for the 100%-target
|
|
12
|
-
* phrasing + the no-coverage-padding rule.
|
|
13
|
-
*
|
|
14
|
-
* scope: peaks-rd only.
|
|
3
|
+
* peaks-rd must not hand off to QA without a reviewable RD artifact. The
|
|
4
|
+
* check reads `.peaks/_runtime/<sessionId>/rd/requests/*.md`; it does not
|
|
5
|
+
* read the SKILL.md sentence that promises one.
|
|
15
6
|
*/
|
|
16
|
-
|
|
17
|
-
export declare function lintRdHandoffContract(skill: SkillFile): ReadonlyArray<LintHit>;
|
|
7
|
+
export declare function lintRdHandoffContract(skill: SkillFile, projectRoot: string): ReadonlyArray<LintHit>;
|
|
18
8
|
export declare function lintRdCoverageDiscipline(skill: SkillFile): ReadonlyArray<LintHit>;
|
|
@@ -1,17 +1,85 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* P2-b sweep 005 — peaks-rd handoff + coverage enforcers.
|
|
3
|
+
*
|
|
4
|
+
* Closes three peaks-rd discovered lines:
|
|
5
|
+
* - md-121 : "do not hand off to QA without [the RD artifact]" (BLOCKING)
|
|
6
|
+
* - md-127 : "do not hand off to QA without a perf-baseline" (BLOCKING)
|
|
7
|
+
* - md-162 : "100% coverage target on testable files is meaningful"
|
|
8
|
+
*
|
|
9
|
+
* A10 of the 2026-09-15 diagnosis — `lintRdHandoffContract` used to regex
|
|
10
|
+
* the peaks-rd SKILL.md prose for the sentence *"do not hand off to QA
|
|
11
|
+
* without this file"* and call that enforcement. It never opened a file
|
|
12
|
+
* under `.peaks/_runtime/`. The gate was verifying a sentence about the
|
|
13
|
+
* artifact instead of the artifact: the exact failure the 4.0.49 release
|
|
14
|
+
* note named, found alive inside the enforcer layer.
|
|
15
|
+
*
|
|
16
|
+
* It now reads `.peaks/_runtime/<sessionId>/rd/requests/*.md` — the
|
|
17
|
+
* artifact the sentence is about (contract:
|
|
18
|
+
* `skills/bee/peaks-rd/references/artifact-per-request.md`) — and reports
|
|
19
|
+
* when nothing is there. When no session binding can be resolved it
|
|
20
|
+
* reports nothing, matching the soft-pass convention of its sibling
|
|
21
|
+
* enforcers (`pre-rd-scan.ts`, `lint-audit-regression.ts`).
|
|
22
|
+
*
|
|
23
|
+
* `lintRdCoverageDiscipline` still reads the skill doc, because its rule
|
|
24
|
+
* *is* about what the skill doc declares. It is not a handoff gate.
|
|
25
|
+
*
|
|
26
|
+
* scope: peaks-rd only.
|
|
27
|
+
*/
|
|
28
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
29
|
+
import { join } from 'node:path';
|
|
3
30
|
const COVERAGE_TARGET = /100%\s*coverage target[^\n]*testable files/i;
|
|
4
31
|
const NO_PADDING = /must not write coverage-padding tests/i;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
32
|
+
/** The artifact whose absence makes an RD→QA handoff invalid. */
|
|
33
|
+
const RD_ARTIFACT_RELATIVE = '.peaks/_runtime';
|
|
34
|
+
/**
|
|
35
|
+
* Resolve the bound session id from `.peaks/_runtime/session.json`. Both
|
|
36
|
+
* key spellings are accepted: the file has shipped as `peakSessionId` and
|
|
37
|
+
* as `sessionId`, and reading only one silently disables the check.
|
|
38
|
+
*/
|
|
39
|
+
function resolveSessionBinding(projectRoot) {
|
|
40
|
+
const sessionJsonPath = join(projectRoot, '.peaks', '_runtime', 'session.json');
|
|
41
|
+
if (!existsSync(sessionJsonPath)) {
|
|
42
|
+
return { sessionId: null, reason: 'no .peaks/_runtime/session.json' };
|
|
43
|
+
}
|
|
44
|
+
let parsed;
|
|
45
|
+
try {
|
|
46
|
+
parsed = JSON.parse(readFileSync(sessionJsonPath, 'utf8'));
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
return { sessionId: null, reason: `session.json is not readable JSON (${String(error)})` };
|
|
50
|
+
}
|
|
51
|
+
if (typeof parsed !== 'object' || parsed === null) {
|
|
52
|
+
return { sessionId: null, reason: 'session.json is not an object' };
|
|
53
|
+
}
|
|
54
|
+
const record = parsed;
|
|
55
|
+
for (const key of ['sessionId', 'peakSessionId']) {
|
|
56
|
+
const value = record[key];
|
|
57
|
+
if (typeof value === 'string' && value.length > 0) {
|
|
58
|
+
return { sessionId: value, reason: `bound via session.json:${key}` };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return { sessionId: null, reason: 'session.json carries no session id' };
|
|
62
|
+
}
|
|
63
|
+
/** Count non-empty `*.md` files in `dir`. A missing dir counts as zero. */
|
|
64
|
+
function countNonEmptyArtifacts(dir) {
|
|
65
|
+
let names;
|
|
66
|
+
try {
|
|
67
|
+
names = readdirSync(dir).filter((name) => name.endsWith('.md'));
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return 0;
|
|
13
71
|
}
|
|
14
|
-
|
|
72
|
+
let count = 0;
|
|
73
|
+
for (const name of names) {
|
|
74
|
+
try {
|
|
75
|
+
if (statSync(join(dir, name)).size > 0)
|
|
76
|
+
count += 1;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return count;
|
|
15
83
|
}
|
|
16
84
|
function findCoverageContract(lines) {
|
|
17
85
|
let target = false;
|
|
@@ -24,26 +92,27 @@ function findCoverageContract(lines) {
|
|
|
24
92
|
}
|
|
25
93
|
return { target, noPadding };
|
|
26
94
|
}
|
|
27
|
-
|
|
95
|
+
/**
|
|
96
|
+
* peaks-rd must not hand off to QA without a reviewable RD artifact. The
|
|
97
|
+
* check reads `.peaks/_runtime/<sessionId>/rd/requests/*.md`; it does not
|
|
98
|
+
* read the SKILL.md sentence that promises one.
|
|
99
|
+
*/
|
|
100
|
+
export function lintRdHandoffContract(skill, projectRoot) {
|
|
28
101
|
if (skill.name !== 'peaks-rd')
|
|
29
102
|
return [];
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
103
|
+
const binding = resolveSessionBinding(projectRoot);
|
|
104
|
+
if (binding.sessionId === null)
|
|
105
|
+
return [];
|
|
106
|
+
const requestsDir = join(projectRoot, RD_ARTIFACT_RELATIVE, binding.sessionId, 'rd', 'requests');
|
|
107
|
+
const artifactCount = countNonEmptyArtifacts(requestsDir);
|
|
108
|
+
if (artifactCount > 0)
|
|
35
109
|
return [];
|
|
36
|
-
const missing = [];
|
|
37
|
-
if (!techDoc)
|
|
38
|
-
missing.push('tech-doc handoff BLOCKING');
|
|
39
|
-
if (!perfBaseline)
|
|
40
|
-
missing.push('perf-baseline handoff BLOCKING');
|
|
41
110
|
return [{
|
|
42
111
|
catalogId: 'rl-rd-handoff-contract-001',
|
|
43
|
-
rule: 'peaks-rd
|
|
44
|
-
file:
|
|
112
|
+
rule: 'peaks-rd must not hand off to QA without a non-empty RD artifact under rd/requests/',
|
|
113
|
+
file: requestsDir,
|
|
45
114
|
line: 1,
|
|
46
|
-
matchedText: `
|
|
115
|
+
matchedText: `no non-empty .md artifact under ${requestsDir} (${binding.reason})`,
|
|
47
116
|
}];
|
|
48
117
|
}
|
|
49
118
|
export function lintRdCoverageDiscipline(skill) {
|
|
@@ -14,7 +14,15 @@ export interface SkillFile {
|
|
|
14
14
|
export declare function readSkillFiles(skillsRoot: string, names: readonly string[]): readonly SkillFile[];
|
|
15
15
|
/** Theme A — section structure. Returns lint hits (positive = rule
|
|
16
16
|
* satisfied, so a missing heading fires the lint hit; downstream
|
|
17
|
-
* audit service decides whether to WARN or pass).
|
|
17
|
+
* audit service decides whether to WARN or pass).
|
|
18
|
+
*
|
|
19
|
+
* A11 of the 2026-09-15 diagnosis: the `Hard contracts` rule used to
|
|
20
|
+
* match the *heading text* and stop there. `peaks-perf-audit/SKILL.md`
|
|
21
|
+
* carries `## Hard contracts (BLOCKING)` — the word BLOCKING is in the
|
|
22
|
+
* heading — while every bullet beneath it is unmarked prose, so the
|
|
23
|
+
* audit counted a BLOCKING red line with no contract behind it. The
|
|
24
|
+
* rule now also requires at least one marker line inside the section
|
|
25
|
+
* body, which is what makes a contract visible to the classifier. */
|
|
18
26
|
export declare function lintSectionShape(skill: SkillFile): readonly LintHit[];
|
|
19
27
|
/**
|
|
20
28
|
* ASCII wireframe section-order check (spec §5.4 line 647).
|
|
@@ -45,13 +45,36 @@ function matchedText(lines, line) {
|
|
|
45
45
|
return '';
|
|
46
46
|
return (lines[line - 1] ?? '').trim();
|
|
47
47
|
}
|
|
48
|
+
/** A red-line marker that makes a contract line machine-visible. */
|
|
49
|
+
const CONTRACT_MARKER = /\b(MANDATORY|BLOCKING|MUST NOT|RED LINE)\b/;
|
|
50
|
+
/**
|
|
51
|
+
* Collect the body of the section that starts at `headingLine` (1-based):
|
|
52
|
+
* every line up to the next `## ` heading.
|
|
53
|
+
*/
|
|
54
|
+
function sectionBody(lines, headingLine) {
|
|
55
|
+
const body = [];
|
|
56
|
+
for (let i = headingLine; i < lines.length; i += 1) {
|
|
57
|
+
if (/^##\s/.test(lines[i] ?? ''))
|
|
58
|
+
break;
|
|
59
|
+
body.push(lines[i] ?? '');
|
|
60
|
+
}
|
|
61
|
+
return body;
|
|
62
|
+
}
|
|
48
63
|
/** Theme A — section structure. Returns lint hits (positive = rule
|
|
49
64
|
* satisfied, so a missing heading fires the lint hit; downstream
|
|
50
|
-
* audit service decides whether to WARN or pass).
|
|
65
|
+
* audit service decides whether to WARN or pass).
|
|
66
|
+
*
|
|
67
|
+
* A11 of the 2026-09-15 diagnosis: the `Hard contracts` rule used to
|
|
68
|
+
* match the *heading text* and stop there. `peaks-perf-audit/SKILL.md`
|
|
69
|
+
* carries `## Hard contracts (BLOCKING)` — the word BLOCKING is in the
|
|
70
|
+
* heading — while every bullet beneath it is unmarked prose, so the
|
|
71
|
+
* audit counted a BLOCKING red line with no contract behind it. The
|
|
72
|
+
* rule now also requires at least one marker line inside the section
|
|
73
|
+
* body, which is what makes a contract visible to the classifier. */
|
|
51
74
|
export function lintSectionShape(skill) {
|
|
52
75
|
const hits = [];
|
|
53
76
|
const rules = [
|
|
54
|
-
{ id: 'rl-section-hard-contracts-001', rule: 'Hard contracts for browser/IO surface', pattern: SECTION_HARD_CONTRACTS_HEADING },
|
|
77
|
+
{ id: 'rl-section-hard-contracts-001', rule: 'Hard contracts for browser/IO surface', pattern: SECTION_HARD_CONTRACTS_HEADING, requiresBodyMarker: true },
|
|
55
78
|
{ id: 'rl-section-mandatory-artifact-001', rule: 'Mandatory per-request artifact', pattern: SECTION_MANDATORY_HEADING },
|
|
56
79
|
{ id: 'rl-section-default-runbook-001', rule: 'Default runbook pointer', pattern: SECTION_DEFAULT_RUNBOOK_HEADING },
|
|
57
80
|
{ id: 'rl-section-gate-index-001', rule: 'Gate index', pattern: SECTION_GATE_INDEX_HEADING },
|
|
@@ -67,6 +90,19 @@ export function lintSectionShape(skill) {
|
|
|
67
90
|
line: 1,
|
|
68
91
|
matchedText: '(missing section)'
|
|
69
92
|
});
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (r.requiresBodyMarker === true) {
|
|
96
|
+
const declared = sectionBody(skill.lines, line).some((l) => CONTRACT_MARKER.test(l));
|
|
97
|
+
if (!declared) {
|
|
98
|
+
hits.push({
|
|
99
|
+
catalogId: r.id,
|
|
100
|
+
rule: r.rule,
|
|
101
|
+
file: skill.path,
|
|
102
|
+
line,
|
|
103
|
+
matchedText: `(heading at line ${line} but no BLOCKING/MANDATORY/MUST NOT/RED LINE line inside the section)`
|
|
104
|
+
});
|
|
105
|
+
}
|
|
70
106
|
}
|
|
71
107
|
}
|
|
72
108
|
return hits;
|
|
@@ -1,33 +1,44 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Prose-only ratio calculator — Slice C Group G3 (v2.14.0)
|
|
2
|
+
* Prose-only ratio calculator — Slice C Group G3 (v2.14.0),
|
|
3
|
+
* corrected in S3 of the 2026-09-15 diagnosis-remediation job.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* prose-only-catalog-followup.md`), an entry counts as prose-only
|
|
7
|
-
* only when BOTH:
|
|
8
|
-
* 1. `backing === 'prose-only'`
|
|
9
|
-
* 2. `informational !== true`
|
|
5
|
+
* An entry counts as prose-only when `backing === 'prose-only'`.
|
|
6
|
+
* Full stop. There is no second condition.
|
|
10
7
|
*
|
|
11
|
-
* The
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
8
|
+
* The pre-S3 version also required `informational !== true`, on the
|
|
9
|
+
* reasoning that auto-discovered advisory SKILL.md phrases are "not
|
|
10
|
+
* actionable red lines". Whatever the merits of that reading, the effect
|
|
11
|
+
* was to move 44 of 152 rows — 29% of the catalog — out of the
|
|
12
|
+
* denominator, so the gate reported `proseOnly: 0` while the same JSON
|
|
13
|
+
* carried 44 rows with `"backing": "prose-only"`. A metric whose
|
|
14
|
+
* denominator can be redefined by the code it measures is not a metric.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* `informational` survives as a triage label — `discoveredProseOnly`
|
|
17
|
+
* below counts those rows — but it no longer moves any number that the
|
|
18
|
+
* ratio is computed from. Expect the ratio to look much worse than it
|
|
19
|
+
* did; that is this correction working.
|
|
20
|
+
*
|
|
21
|
+
* Karpathy §2 simplicity: one exported function plus a thin calculator
|
|
22
|
+
* interface; no I/O.
|
|
19
23
|
*/
|
|
20
24
|
import type { RedLineEntry } from './types.js';
|
|
21
25
|
export interface ProseRatioResult {
|
|
22
|
-
/** Total
|
|
26
|
+
/** Total entries considered (entries.length) — the denominator, always. */
|
|
23
27
|
readonly totalRedLines: number;
|
|
24
28
|
/** Count of entries with backing === 'cli-backed'. */
|
|
25
29
|
readonly cliBacked: number;
|
|
26
30
|
/** Count of entries with backing === 'partial'. */
|
|
27
31
|
readonly partial: number;
|
|
28
|
-
/** Count of entries with backing === 'prose-only'
|
|
32
|
+
/** Count of entries with backing === 'prose-only'. THE numerator. */
|
|
29
33
|
readonly proseOnly: number;
|
|
30
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* Breakdown only — the subset of `proseOnly` that carries
|
|
36
|
+
* `informational: true` (auto-discovered advisory phrases with no
|
|
37
|
+
* catalog template). Included in `proseOnly`; changing it changes
|
|
38
|
+
* nothing about `proseOnly` or `ratio`.
|
|
39
|
+
*/
|
|
40
|
+
readonly discoveredProseOnly: number;
|
|
41
|
+
/** Count of entries with informational === true, whatever their backing. */
|
|
31
42
|
readonly informational: number;
|
|
32
43
|
/** proseOnly / totalRedLines. Returns 0 when totalRedLines === 0. */
|
|
33
44
|
readonly ratio: number;
|
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Prose-only ratio calculator — Slice C Group G3 (v2.14.0)
|
|
2
|
+
* Prose-only ratio calculator — Slice C Group G3 (v2.14.0),
|
|
3
|
+
* corrected in S3 of the 2026-09-15 diagnosis-remediation job.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* prose-only-catalog-followup.md`), an entry counts as prose-only
|
|
7
|
-
* only when BOTH:
|
|
8
|
-
* 1. `backing === 'prose-only'`
|
|
9
|
-
* 2. `informational !== true`
|
|
5
|
+
* An entry counts as prose-only when `backing === 'prose-only'`.
|
|
6
|
+
* Full stop. There is no second condition.
|
|
10
7
|
*
|
|
11
|
-
* The
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
8
|
+
* The pre-S3 version also required `informational !== true`, on the
|
|
9
|
+
* reasoning that auto-discovered advisory SKILL.md phrases are "not
|
|
10
|
+
* actionable red lines". Whatever the merits of that reading, the effect
|
|
11
|
+
* was to move 44 of 152 rows — 29% of the catalog — out of the
|
|
12
|
+
* denominator, so the gate reported `proseOnly: 0` while the same JSON
|
|
13
|
+
* carried 44 rows with `"backing": "prose-only"`. A metric whose
|
|
14
|
+
* denominator can be redefined by the code it measures is not a metric.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* `informational` survives as a triage label — `discoveredProseOnly`
|
|
17
|
+
* below counts those rows — but it no longer moves any number that the
|
|
18
|
+
* ratio is computed from. Expect the ratio to look much worse than it
|
|
19
|
+
* did; that is this correction working.
|
|
20
|
+
*
|
|
21
|
+
* Karpathy §2 simplicity: one exported function plus a thin calculator
|
|
22
|
+
* interface; no I/O.
|
|
19
23
|
*/
|
|
20
24
|
/** Default target: 5% (per A3.1). */
|
|
21
25
|
export const DEFAULT_PROSE_RATIO_TARGET = 0.05;
|
|
@@ -24,18 +28,20 @@ export function computeProseRatio(entries, options = {}) {
|
|
|
24
28
|
let cliBacked = 0;
|
|
25
29
|
let partial = 0;
|
|
26
30
|
let proseOnly = 0;
|
|
31
|
+
let discoveredProseOnly = 0;
|
|
27
32
|
let informational = 0;
|
|
28
33
|
for (const entry of entries) {
|
|
29
|
-
if (entry.informational === true)
|
|
34
|
+
if (entry.informational === true)
|
|
30
35
|
informational += 1;
|
|
31
|
-
continue;
|
|
32
|
-
}
|
|
33
36
|
if (entry.backing === 'cli-backed')
|
|
34
37
|
cliBacked += 1;
|
|
35
38
|
else if (entry.backing === 'partial')
|
|
36
39
|
partial += 1;
|
|
37
|
-
else if (entry.backing === 'prose-only')
|
|
40
|
+
else if (entry.backing === 'prose-only') {
|
|
38
41
|
proseOnly += 1;
|
|
42
|
+
if (entry.informational === true)
|
|
43
|
+
discoveredProseOnly += 1;
|
|
44
|
+
}
|
|
39
45
|
}
|
|
40
46
|
const totalRedLines = entries.length;
|
|
41
47
|
const ratio = totalRedLines === 0 ? 0 : proseOnly / totalRedLines;
|
|
@@ -44,6 +50,7 @@ export function computeProseRatio(entries, options = {}) {
|
|
|
44
50
|
cliBacked,
|
|
45
51
|
partial,
|
|
46
52
|
proseOnly,
|
|
53
|
+
discoveredProseOnly,
|
|
47
54
|
informational,
|
|
48
55
|
ratio,
|
|
49
56
|
target,
|
|
@@ -216,7 +216,7 @@ const PRD_ARTIFACT_HANDOFF = {
|
|
|
216
216
|
};
|
|
217
217
|
const RD_HANDOFF_CONTRACT = {
|
|
218
218
|
id: 'rl-rd-handoff-contract-001',
|
|
219
|
-
rule: 'peaks-rd
|
|
219
|
+
rule: 'peaks-rd must not hand off to QA without a non-empty RD artifact under rd/requests/',
|
|
220
220
|
markers: ['BLOCKING'],
|
|
221
221
|
phrases: ['do not hand off to qa without', 'tech-doc', 'perf-baseline'],
|
|
222
222
|
enforcerRef: 'src/services/audit/enforcers/lint-rd-handoff-coverage.ts',
|
|
@@ -16,6 +16,8 @@ import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
|
16
16
|
import { join } from 'node:path';
|
|
17
17
|
import { classifyFiles } from './classifier.js';
|
|
18
18
|
import { classifyBackingBatch } from './backing-detector.js';
|
|
19
|
+
import { computeLiveEnforcers } from './enforcer-liveness.js';
|
|
20
|
+
import { RED_LINE_CATALOG } from './red-line-catalog.js';
|
|
19
21
|
import { scanSkillsTree } from './scanners/skills-tree-scanner.js';
|
|
20
22
|
import { scanRulesTree } from './scanners/rules-tree-scanner.js';
|
|
21
23
|
import { scanOpenSpecTree } from './scanners/openspec-scanner.js';
|
|
@@ -28,6 +30,7 @@ import { readSkillFiles, lintSectionShape, lintSectionOrder, lintFrontmatterShap
|
|
|
28
30
|
import { lintRefPathResolves, lintNoBrokenMkdir, lintNoPwdSymlinkJumps, lintNoRelativeArchivePaths, } from './enforcers/lint-reference-integrity.js';
|
|
29
31
|
import { lintCliBackMandatorText, lintCliBackNoOrphanBlocking, lintCliBackNoOrphanMustNot, } from './enforcers/lint-cli-back.js';
|
|
30
32
|
import { lintNoFluff, lintNoClosingPrompt, lintStatusHeader, } from './enforcers/lint-output-style.js';
|
|
33
|
+
import { lintRdHandoffContract, lintRdCoverageDiscipline, } from './enforcers/lint-rd-handoff-coverage.js';
|
|
31
34
|
import { lintOpenSpecAcceptanceBullets, lintOpenSpecSpecReference,
|
|
32
35
|
// (Removed in v2.11.0 Group A: `lintTechDocPresenceShape`)
|
|
33
36
|
lintPeaksDoctorAcknowledged, } from './enforcers/lint-workflow-shape.js';
|
|
@@ -59,15 +62,16 @@ function tally(entries) {
|
|
|
59
62
|
let partial = 0;
|
|
60
63
|
let proseOnly = 0;
|
|
61
64
|
for (const entry of entries) {
|
|
62
|
-
//
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
//
|
|
65
|
+
// S3 of the 2026-09-15 diagnosis-remediation job: `informational` is a
|
|
66
|
+
// triage label, not a ratio input. The pre-S3 version skipped
|
|
67
|
+
// informational rows here too, which is how the audit came to report
|
|
68
|
+
// `proseOnly: 0` while carrying 44 `"backing": "prose-only"` rows in
|
|
69
|
+
// the same envelope. Every prose-only row is now counted.
|
|
66
70
|
if (entry.backing === 'cli-backed')
|
|
67
71
|
cliBacked++;
|
|
68
72
|
else if (entry.backing === 'partial')
|
|
69
73
|
partial++;
|
|
70
|
-
else if (
|
|
74
|
+
else if (entry.backing === 'prose-only')
|
|
71
75
|
proseOnly++;
|
|
72
76
|
}
|
|
73
77
|
return {
|
|
@@ -87,7 +91,12 @@ export function runRedLinesAudit(input) {
|
|
|
87
91
|
const openspec = scanOpenSpecTree({ projectRoot: input.projectRoot });
|
|
88
92
|
const fileInputs = buildFileInputs(skills, rules, openspec);
|
|
89
93
|
const classified = classifyFiles(fileInputs);
|
|
90
|
-
|
|
94
|
+
// A9: `cli-backed` requires a call site, not just a file on disk. The
|
|
95
|
+
// live set is computed once for the whole catalog; `null` means the
|
|
96
|
+
// project has no `src/` tree and liveness is undecidable, in which case
|
|
97
|
+
// the detector falls back to the pre-A9 "file exists" rule.
|
|
98
|
+
const liveness = computeLiveEnforcers(input.projectRoot, RED_LINE_CATALOG.map((entry) => entry.enforcerRef).filter((ref) => ref !== null));
|
|
99
|
+
const backed = classifyBackingBatch(classified.entries, input.projectRoot, liveness.live);
|
|
91
100
|
// Sub-agent-sid enforcer (Task 2): dogfoods Slice 0.5 sid-naming-guard.
|
|
92
101
|
const subAgentSids = findInvalidSubAgentSids(input.projectRoot);
|
|
93
102
|
const runtimeSids = findInvalidRuntimeSids(input.projectRoot);
|
|
@@ -97,7 +106,22 @@ export function runRedLinesAudit(input) {
|
|
|
97
106
|
...openspec.warnings,
|
|
98
107
|
...classified.warnings.map((message) => ({ file: '(classifier)', message })),
|
|
99
108
|
...backed.warnings.map((message) => ({ file: '(backing-detector)', message })),
|
|
109
|
+
...liveness.warnings.map((message) => ({ file: '(enforcer-liveness)', message })),
|
|
100
110
|
];
|
|
111
|
+
// A9: name every enforcer that was downgraded, so the drop in
|
|
112
|
+
// `cliBacked` is attributable rather than mysterious.
|
|
113
|
+
if (liveness.unknown) {
|
|
114
|
+
warnings.push({
|
|
115
|
+
file: '(enforcer-liveness)',
|
|
116
|
+
message: 'no src/ tree under the project root; enforcer liveness is undecidable, so `cli-backed` falls back to the pre-A9 "the enforcer file exists" rule',
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
for (const ref of backed.deadEnforcers) {
|
|
120
|
+
warnings.push({
|
|
121
|
+
file: ref,
|
|
122
|
+
message: 'enforcer file exists but nothing outside src/services/audit/enforcers/ imports it; red lines backed by it are counted as prose-only',
|
|
123
|
+
});
|
|
124
|
+
}
|
|
101
125
|
if (subAgentSids.scanned && subAgentSids.invalid.length > 0) {
|
|
102
126
|
for (const sid of subAgentSids.invalid) {
|
|
103
127
|
warnings.push({
|
|
@@ -261,15 +285,31 @@ export function runRedLinesAudit(input) {
|
|
|
261
285
|
const skillsRoot = join(input.projectRoot, 'skills');
|
|
262
286
|
if (existsSync(skillsRoot)) {
|
|
263
287
|
const skillNames = [];
|
|
288
|
+
const skippedRoots = [];
|
|
264
289
|
for (const entry of readdirSync(skillsRoot, { withFileTypes: true })) {
|
|
265
290
|
if (!entry.isDirectory())
|
|
266
291
|
continue;
|
|
267
292
|
if (entry.name.startsWith('.'))
|
|
268
293
|
continue;
|
|
269
|
-
if (!existsSync(join(skillsRoot, entry.name, 'SKILL.md')))
|
|
294
|
+
if (!existsSync(join(skillsRoot, entry.name, 'SKILL.md'))) {
|
|
295
|
+
skippedRoots.push(entry.name);
|
|
270
296
|
continue;
|
|
297
|
+
}
|
|
271
298
|
skillNames.push(entry.name);
|
|
272
299
|
}
|
|
300
|
+
// A11 scope limit, declared rather than left implicit: only skills
|
|
301
|
+
// with a top-level `SKILL.md` are linted. Nested roots such as
|
|
302
|
+
// `skills/bee/` hold real skills (`peaks-perf-audit`, `peaks-rd`, …)
|
|
303
|
+
// that the classifier DOES see (their markers appear as
|
|
304
|
+
// `rl-discovered-skills-bee-*` rows) but that every Theme A–G
|
|
305
|
+
// lint-style enforcer skips. A clean Theme A result therefore means
|
|
306
|
+
// "clean among the linted skills", not "clean everywhere".
|
|
307
|
+
for (const root of skippedRoots) {
|
|
308
|
+
warnings.push({
|
|
309
|
+
file: `skills/${root}`,
|
|
310
|
+
message: 'no top-level SKILL.md — this directory and every skill nested under it are skipped by all lint-style enforcers (including rl-section-hard-contracts-001); their red-line rows are unverified',
|
|
311
|
+
});
|
|
312
|
+
}
|
|
273
313
|
const skillFiles = readSkillFiles(skillsRoot, skillNames);
|
|
274
314
|
for (const skill of skillFiles) {
|
|
275
315
|
const refsDir = join(skillsRoot, skill.name, 'references');
|
|
@@ -291,6 +331,10 @@ export function runRedLinesAudit(input) {
|
|
|
291
331
|
...lintNoFluff(skill),
|
|
292
332
|
...lintNoClosingPrompt(skill),
|
|
293
333
|
...lintPeaksDoctorAcknowledged(skill),
|
|
334
|
+
// A10: the RD handoff gate reads the artifact, not the SKILL.md
|
|
335
|
+
// sentence that promises one. See lint-rd-handoff-coverage.ts.
|
|
336
|
+
...lintRdHandoffContract(skill, input.projectRoot),
|
|
337
|
+
...lintRdCoverageDiscipline(skill),
|
|
294
338
|
];
|
|
295
339
|
for (const hit of lintHits) {
|
|
296
340
|
enforcerFindings.push({
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type CapabilityBaselineRow } from '../capability-baseline/types.js';
|
|
2
|
+
import type { GuardContract, GuardRunResult } from '../capability-guard-runner/types.js';
|
|
3
|
+
import type { IndependentCheckResult } from './types.js';
|
|
4
|
+
export interface IndependentCheckInput {
|
|
5
|
+
readonly projectRoot: string;
|
|
6
|
+
readonly baselineRows: ReadonlyArray<CapabilityBaselineRow>;
|
|
7
|
+
readonly contracts: ReadonlyArray<GuardContract>;
|
|
8
|
+
readonly guardResults: ReadonlyArray<GuardRunResult>;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Run the credential-free independent evaluation. The verdict is `drifted`
|
|
12
|
+
* whenever a concrete deviation is observed — this function has no path that
|
|
13
|
+
* returns `consistent` without having checked.
|
|
14
|
+
*/
|
|
15
|
+
export declare function runIndependentCheck(input: IndependentCheckInput): IndependentCheckResult;
|