@ai-sdlc/orchestrator 0.5.0 → 0.9.0
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/dist/action-enforcement.d.ts +26 -0
- package/dist/action-enforcement.js +70 -0
- package/dist/admission-composite.d.ts +67 -0
- package/dist/admission-composite.js +158 -0
- package/dist/admission-enrichment.d.ts +142 -0
- package/dist/admission-enrichment.js +331 -0
- package/dist/admission-hc.d.ts +62 -0
- package/dist/admission-hc.js +83 -0
- package/dist/admission-score.d.ts +148 -0
- package/dist/admission-score.js +237 -0
- package/dist/analysis/analyzer.js +3 -2
- package/dist/analysis/convention-detector.d.ts +85 -2
- package/dist/analysis/convention-detector.js +375 -70
- package/dist/analysis/diff-analyzer.d.ts +33 -0
- package/dist/analysis/diff-analyzer.js +122 -0
- package/dist/analysis/hotspot-analyzer.js +3 -1
- package/dist/analysis/index.d.ts +2 -1
- package/dist/analysis/index.js +2 -1
- package/dist/artifacts/index.d.ts +65 -0
- package/dist/artifacts/index.js +142 -0
- package/dist/backlog-adapter.d.ts +121 -0
- package/dist/backlog-adapter.js +438 -0
- package/dist/calibration.d.ts +43 -0
- package/dist/calibration.js +76 -0
- package/dist/cli/commands/agents.d.ts +24 -0
- package/dist/cli/commands/agents.js +66 -1
- package/dist/cli/commands/git-remote.d.ts +49 -0
- package/dist/cli/commands/git-remote.js +91 -0
- package/dist/cli/commands/health.d.ts +4 -0
- package/dist/cli/commands/health.js +13 -1
- package/dist/cli/commands/init.d.ts +26 -0
- package/dist/cli/commands/init.js +164 -22
- package/dist/cli/commands/mcp-setup.d.ts +31 -0
- package/dist/cli/commands/mcp-setup.js +78 -8
- package/dist/cli/formatters/table.js +15 -2
- package/dist/cli/index.d.ts +14 -1
- package/dist/cli/index.js +81 -20
- package/dist/cli/versions.d.ts +57 -0
- package/dist/cli/versions.js +128 -0
- package/dist/code-area-classifier.d.ts +21 -0
- package/dist/code-area-classifier.js +48 -0
- package/dist/config.d.ts +33 -1
- package/dist/config.js +78 -8
- package/dist/cycle-utils.d.ts +51 -0
- package/dist/cycle-utils.js +77 -0
- package/dist/database/adapters/external.d.ts +24 -0
- package/dist/database/adapters/external.js +80 -0
- package/dist/database/adapters/neon.d.ts +41 -0
- package/dist/database/adapters/neon.js +98 -0
- package/dist/database/adapters/pg-snapshot-restore.d.ts +28 -0
- package/dist/database/adapters/pg-snapshot-restore.js +68 -0
- package/dist/database/adapters/sqlite-copy.d.ts +32 -0
- package/dist/database/adapters/sqlite-copy.js +145 -0
- package/dist/database/connection-injection.d.ts +35 -0
- package/dist/database/connection-injection.js +93 -0
- package/dist/database/index.d.ts +12 -0
- package/dist/database/index.js +23 -0
- package/dist/database/registry.d.ts +13 -0
- package/dist/database/registry.js +27 -0
- package/dist/database/topology.d.ts +52 -0
- package/dist/database/topology.js +44 -0
- package/dist/database/types.d.ts +89 -0
- package/dist/database/types.js +26 -0
- package/dist/defaults.d.ts +5 -0
- package/dist/defaults.js +5 -0
- package/dist/design-authority.d.ts +40 -0
- package/dist/design-authority.js +71 -0
- package/dist/design-lookahead.d.ts +64 -0
- package/dist/design-lookahead.js +86 -0
- package/dist/design-quality-trend.d.ts +87 -0
- package/dist/design-quality-trend.js +190 -0
- package/dist/design-system-context.d.ts +46 -0
- package/dist/design-system-context.js +80 -0
- package/dist/design-system-correction-loop.d.ts +64 -0
- package/dist/design-system-correction-loop.js +128 -0
- package/dist/design-system-metrics.d.ts +61 -0
- package/dist/design-system-metrics.js +104 -0
- package/dist/design-system-stewardship.d.ts +22 -0
- package/dist/design-system-stewardship.js +85 -0
- package/dist/design-system-validation.d.ts +37 -0
- package/dist/design-system-validation.js +88 -0
- package/dist/dispatch/index.d.ts +4 -0
- package/dist/dispatch/index.js +4 -0
- package/dist/dispatch/merge-gate.d.ts +46 -0
- package/dist/dispatch/merge-gate.js +90 -0
- package/dist/dispatch/requeue.d.ts +57 -0
- package/dist/dispatch/requeue.js +131 -0
- package/dist/dispatch/worker-pool.d.ts +62 -0
- package/dist/dispatch/worker-pool.js +60 -0
- package/dist/execute.d.ts +40 -0
- package/dist/execute.js +295 -30
- package/dist/fix-ci.js +40 -3
- package/dist/fix-review.d.ts +66 -0
- package/dist/fix-review.js +448 -0
- package/dist/harness/adapters/claude-code.d.ts +29 -0
- package/dist/harness/adapters/claude-code.js +191 -0
- package/dist/harness/adapters/codex.d.ts +25 -0
- package/dist/harness/adapters/codex.js +61 -0
- package/dist/harness/independence.d.ts +51 -0
- package/dist/harness/independence.js +67 -0
- package/dist/harness/index.d.ts +14 -0
- package/dist/harness/index.js +20 -0
- package/dist/harness/registry.d.ts +17 -0
- package/dist/harness/registry.js +31 -0
- package/dist/harness/types.d.ts +123 -0
- package/dist/harness/types.js +8 -0
- package/dist/harness/version-probe.d.ts +14 -0
- package/dist/harness/version-probe.js +123 -0
- package/dist/index.d.ts +39 -5
- package/dist/index.js +48 -3
- package/dist/models/classifier.d.ts +76 -0
- package/dist/models/classifier.js +221 -0
- package/dist/models/index.d.ts +3 -0
- package/dist/models/index.js +3 -0
- package/dist/models/registry.d.ts +97 -0
- package/dist/models/registry.js +173 -0
- package/dist/pillar-breakdown.d.ts +63 -0
- package/dist/pillar-breakdown.js +153 -0
- package/dist/pipeline-cycle-detector.d.ts +70 -0
- package/dist/pipeline-cycle-detector.js +111 -0
- package/dist/priority.d.ts +7 -76
- package/dist/priority.js +18 -5
- package/dist/review-meta.d.ts +65 -0
- package/dist/review-meta.js +149 -0
- package/dist/review.d.ts +35 -0
- package/dist/review.js +84 -0
- package/dist/runners/claude-code-sdk.d.ts +31 -0
- package/dist/runners/claude-code-sdk.js +238 -0
- package/dist/runners/claude-code.d.ts +3 -0
- package/dist/runners/claude-code.js +319 -73
- package/dist/runners/codex.js +4 -1
- package/dist/runners/copilot.js +4 -1
- package/dist/runners/cursor.js +4 -1
- package/dist/runners/git-utils.d.ts +81 -0
- package/dist/runners/git-utils.js +201 -0
- package/dist/runners/index.d.ts +5 -1
- package/dist/runners/index.js +4 -0
- package/dist/runners/review-agent.d.ts +67 -0
- package/dist/runners/review-agent.js +350 -0
- package/dist/runners/runner-registry.js +10 -0
- package/dist/runners/sdk-review-runner.d.ts +65 -0
- package/dist/runners/sdk-review-runner.js +185 -0
- package/dist/runners/security-triage.d.ts +20 -4
- package/dist/runners/security-triage.js +42 -14
- package/dist/runners/types.d.ts +25 -0
- package/dist/runtime/attestations.d.ts +274 -0
- package/dist/runtime/attestations.js +460 -0
- package/dist/runtime/git-env.d.ts +53 -0
- package/dist/runtime/git-env.js +60 -0
- package/dist/runtime/index.d.ts +7 -0
- package/dist/runtime/index.js +7 -0
- package/dist/runtime/parallelism-flag.d.ts +10 -0
- package/dist/runtime/parallelism-flag.js +18 -0
- package/dist/runtime/port-allocator.d.ts +21 -0
- package/dist/runtime/port-allocator.js +66 -0
- package/dist/runtime/worktree-pool.d.ts +86 -0
- package/dist/runtime/worktree-pool.js +204 -0
- package/dist/runtime/worktree.d.ts +25 -0
- package/dist/runtime/worktree.js +111 -0
- package/dist/sa-scoring/auto-calibrate.d.ts +69 -0
- package/dist/sa-scoring/auto-calibrate.js +107 -0
- package/dist/sa-scoring/c1-sa2-computable.d.ts +42 -0
- package/dist/sa-scoring/c1-sa2-computable.js +59 -0
- package/dist/sa-scoring/composite.d.ts +107 -0
- package/dist/sa-scoring/composite.js +139 -0
- package/dist/sa-scoring/depparse-client.d.ts +79 -0
- package/dist/sa-scoring/depparse-client.js +187 -0
- package/dist/sa-scoring/did-compiler.d.ts +122 -0
- package/dist/sa-scoring/did-compiler.js +286 -0
- package/dist/sa-scoring/drift-monitor.d.ts +84 -0
- package/dist/sa-scoring/drift-monitor.js +186 -0
- package/dist/sa-scoring/exemplar-bank.d.ts +78 -0
- package/dist/sa-scoring/exemplar-bank.js +154 -0
- package/dist/sa-scoring/feedback-store.d.ts +100 -0
- package/dist/sa-scoring/feedback-store.js +156 -0
- package/dist/sa-scoring/index.d.ts +71 -0
- package/dist/sa-scoring/index.js +158 -0
- package/dist/sa-scoring/layer1-deterministic.d.ts +115 -0
- package/dist/sa-scoring/layer1-deterministic.js +298 -0
- package/dist/sa-scoring/layer2-structural.d.ts +71 -0
- package/dist/sa-scoring/layer2-structural.js +151 -0
- package/dist/sa-scoring/layer3-llm.d.ts +86 -0
- package/dist/sa-scoring/layer3-llm.js +282 -0
- package/dist/sa-scoring/rescore-orchestrator.d.ts +52 -0
- package/dist/sa-scoring/rescore-orchestrator.js +47 -0
- package/dist/scheduling/burn-down.d.ts +27 -0
- package/dist/scheduling/burn-down.js +43 -0
- package/dist/scheduling/calibration.d.ts +42 -0
- package/dist/scheduling/calibration.js +150 -0
- package/dist/scheduling/index.d.ts +8 -0
- package/dist/scheduling/index.js +8 -0
- package/dist/scheduling/ledger.d.ts +59 -0
- package/dist/scheduling/ledger.js +216 -0
- package/dist/scheduling/off-peak.d.ts +27 -0
- package/dist/scheduling/off-peak.js +112 -0
- package/dist/scheduling/schedule-decision.d.ts +39 -0
- package/dist/scheduling/schedule-decision.js +89 -0
- package/dist/scheduling/tier-analysis.d.ts +47 -0
- package/dist/scheduling/tier-analysis.js +81 -0
- package/dist/scheduling/types.d.ts +140 -0
- package/dist/scheduling/types.js +11 -0
- package/dist/shared.d.ts +13 -0
- package/dist/shared.js +32 -0
- package/dist/state/index.d.ts +1 -1
- package/dist/state/schema.d.ts +6 -1
- package/dist/state/schema.js +276 -1
- package/dist/state/store.d.ts +71 -1
- package/dist/state/store.js +464 -7
- package/dist/state/types.d.ts +174 -0
- package/dist/types.d.ts +1 -1
- package/dist/validate-agent-output.js +4 -1
- package/dist/watch.js +6 -0
- package/dist/workflow-patterns/artifact-writer.d.ts +16 -0
- package/dist/workflow-patterns/artifact-writer.js +34 -0
- package/dist/workflow-patterns/classifiers.d.ts +10 -0
- package/dist/workflow-patterns/classifiers.js +72 -0
- package/dist/workflow-patterns/detector.d.ts +27 -0
- package/dist/workflow-patterns/detector.js +186 -0
- package/dist/workflow-patterns/index.d.ts +8 -0
- package/dist/workflow-patterns/index.js +7 -0
- package/dist/workflow-patterns/proposal-generator.d.ts +15 -0
- package/dist/workflow-patterns/proposal-generator.js +183 -0
- package/dist/workflow-patterns/telemetry-ingest.d.ts +27 -0
- package/dist/workflow-patterns/telemetry-ingest.js +103 -0
- package/dist/workflow-patterns/types.d.ts +61 -0
- package/dist/workflow-patterns/types.js +11 -0
- package/package.json +2 -2
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SA Exemplar Bank loader (RFC-0008 §B.6.4, §B.10.2).
|
|
3
|
+
*
|
|
4
|
+
* Loads labeled SA-scoring exemplars from `.ai-sdlc/sa-exemplars.yaml`
|
|
5
|
+
* for Phase 2a shadow-mode precision tracking and Phase 2b progression
|
|
6
|
+
* gating. A Phase-2b-ready bank needs ≥ 5 exemplars including at least
|
|
7
|
+
* one true-positive AND one false-positive per dimension — without
|
|
8
|
+
* precision evidence on both sides, the structural layer can't be
|
|
9
|
+
* trusted to contribute to ranking.
|
|
10
|
+
*/
|
|
11
|
+
import type { SaDimension } from '../state/types.js';
|
|
12
|
+
export type ExemplarType = 'true-positive' | 'false-positive' | 'true-negative' | 'false-negative';
|
|
13
|
+
export interface ExemplarIssue {
|
|
14
|
+
title: string;
|
|
15
|
+
body: string;
|
|
16
|
+
}
|
|
17
|
+
export interface Layer1Expected {
|
|
18
|
+
hardGated?: boolean;
|
|
19
|
+
coreViolationCount?: number;
|
|
20
|
+
evolvingViolationCount?: number;
|
|
21
|
+
scopeGate?: Record<string, unknown>;
|
|
22
|
+
constraintViolations?: Record<string, unknown>;
|
|
23
|
+
antiPatternHits?: Record<string, unknown>;
|
|
24
|
+
}
|
|
25
|
+
export interface Layer2Expected {
|
|
26
|
+
domainRelevance?: number;
|
|
27
|
+
overallCoverage?: number;
|
|
28
|
+
[key: string]: unknown;
|
|
29
|
+
}
|
|
30
|
+
export interface Layer3Expected {
|
|
31
|
+
domainIntent?: number;
|
|
32
|
+
principleAlignment?: number;
|
|
33
|
+
reasoning?: string;
|
|
34
|
+
[key: string]: unknown;
|
|
35
|
+
}
|
|
36
|
+
export interface SaExemplar {
|
|
37
|
+
id: string;
|
|
38
|
+
dimension: SaDimension;
|
|
39
|
+
type: ExemplarType;
|
|
40
|
+
issue: ExemplarIssue;
|
|
41
|
+
layer1Expected?: Layer1Expected;
|
|
42
|
+
layer2Expected?: Layer2Expected;
|
|
43
|
+
layer3Expected?: Layer3Expected;
|
|
44
|
+
verdict: string;
|
|
45
|
+
principle?: string;
|
|
46
|
+
notes?: string;
|
|
47
|
+
}
|
|
48
|
+
export interface SaExemplarBank {
|
|
49
|
+
sa1: SaExemplar[];
|
|
50
|
+
sa2: SaExemplar[];
|
|
51
|
+
}
|
|
52
|
+
export declare const EMPTY_BANK: SaExemplarBank;
|
|
53
|
+
/**
|
|
54
|
+
* Load and partition exemplars by dimension. Returns an empty bank
|
|
55
|
+
* when the file is missing — Phase 2a can run without any exemplars;
|
|
56
|
+
* `validatePhase2bExemplars` gates progression.
|
|
57
|
+
*/
|
|
58
|
+
export declare function loadExemplarBank(filePath: string): SaExemplarBank;
|
|
59
|
+
export interface ExemplarGap {
|
|
60
|
+
dimension: SaDimension | 'overall';
|
|
61
|
+
reason: string;
|
|
62
|
+
}
|
|
63
|
+
export interface ExemplarReadinessResult {
|
|
64
|
+
ready: boolean;
|
|
65
|
+
gaps: ExemplarGap[];
|
|
66
|
+
}
|
|
67
|
+
export declare const MIN_TOTAL_EXEMPLARS = 5;
|
|
68
|
+
export declare function validatePhase2bExemplars(bank: SaExemplarBank): ExemplarReadinessResult;
|
|
69
|
+
export interface LayerPrecision {
|
|
70
|
+
truePositives: number;
|
|
71
|
+
falsePositives: number;
|
|
72
|
+
trueNegatives: number;
|
|
73
|
+
falseNegatives: number;
|
|
74
|
+
precision: number;
|
|
75
|
+
recall: number;
|
|
76
|
+
}
|
|
77
|
+
export declare function computeLayerPrecision(exemplars: readonly SaExemplar[]): LayerPrecision;
|
|
78
|
+
//# sourceMappingURL=exemplar-bank.d.ts.map
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SA Exemplar Bank loader (RFC-0008 §B.6.4, §B.10.2).
|
|
3
|
+
*
|
|
4
|
+
* Loads labeled SA-scoring exemplars from `.ai-sdlc/sa-exemplars.yaml`
|
|
5
|
+
* for Phase 2a shadow-mode precision tracking and Phase 2b progression
|
|
6
|
+
* gating. A Phase-2b-ready bank needs ≥ 5 exemplars including at least
|
|
7
|
+
* one true-positive AND one false-positive per dimension — without
|
|
8
|
+
* precision evidence on both sides, the structural layer can't be
|
|
9
|
+
* trusted to contribute to ranking.
|
|
10
|
+
*/
|
|
11
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
12
|
+
import { parse as parseYaml } from 'yaml';
|
|
13
|
+
// ── Loader ──────────────────────────────────────────────────────────
|
|
14
|
+
export const EMPTY_BANK = Object.freeze({
|
|
15
|
+
sa1: [],
|
|
16
|
+
sa2: [],
|
|
17
|
+
});
|
|
18
|
+
/**
|
|
19
|
+
* Load and partition exemplars by dimension. Returns an empty bank
|
|
20
|
+
* when the file is missing — Phase 2a can run without any exemplars;
|
|
21
|
+
* `validatePhase2bExemplars` gates progression.
|
|
22
|
+
*/
|
|
23
|
+
export function loadExemplarBank(filePath) {
|
|
24
|
+
if (!existsSync(filePath))
|
|
25
|
+
return { sa1: [], sa2: [] };
|
|
26
|
+
const raw = readFileSync(filePath, 'utf-8');
|
|
27
|
+
let doc;
|
|
28
|
+
try {
|
|
29
|
+
doc = parseYaml(raw);
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
throw new Error(`Failed to parse SA exemplar bank at ${filePath}: ${err.message}`);
|
|
33
|
+
}
|
|
34
|
+
if (!doc || typeof doc !== 'object' || !('exemplars' in doc)) {
|
|
35
|
+
throw new Error(`SA exemplar bank ${filePath} must contain a top-level "exemplars" array`);
|
|
36
|
+
}
|
|
37
|
+
const list = doc.exemplars;
|
|
38
|
+
if (!Array.isArray(list)) {
|
|
39
|
+
throw new Error(`SA exemplar bank ${filePath}: "exemplars" must be an array`);
|
|
40
|
+
}
|
|
41
|
+
const sa1 = [];
|
|
42
|
+
const sa2 = [];
|
|
43
|
+
for (const entry of list) {
|
|
44
|
+
const ex = validateExemplar(entry);
|
|
45
|
+
if (ex.dimension === 'SA-1')
|
|
46
|
+
sa1.push(ex);
|
|
47
|
+
else
|
|
48
|
+
sa2.push(ex);
|
|
49
|
+
}
|
|
50
|
+
return { sa1, sa2 };
|
|
51
|
+
}
|
|
52
|
+
function validateExemplar(entry) {
|
|
53
|
+
if (!entry || typeof entry !== 'object') {
|
|
54
|
+
throw new Error(`Exemplar must be an object`);
|
|
55
|
+
}
|
|
56
|
+
const e = entry;
|
|
57
|
+
const id = requireString(e, 'id');
|
|
58
|
+
const dimension = requireEnum(e, 'dimension', ['SA-1', 'SA-2']);
|
|
59
|
+
const type = requireEnum(e, 'type', [
|
|
60
|
+
'true-positive',
|
|
61
|
+
'false-positive',
|
|
62
|
+
'true-negative',
|
|
63
|
+
'false-negative',
|
|
64
|
+
]);
|
|
65
|
+
const issueVal = e.issue;
|
|
66
|
+
if (!issueVal || typeof issueVal !== 'object') {
|
|
67
|
+
throw new Error(`Exemplar "${id}" missing required "issue" object`);
|
|
68
|
+
}
|
|
69
|
+
const issueObj = issueVal;
|
|
70
|
+
const issue = {
|
|
71
|
+
title: requireString(issueObj, 'title'),
|
|
72
|
+
body: requireString(issueObj, 'body'),
|
|
73
|
+
};
|
|
74
|
+
const verdict = requireString(e, 'verdict');
|
|
75
|
+
return {
|
|
76
|
+
id,
|
|
77
|
+
dimension,
|
|
78
|
+
type,
|
|
79
|
+
issue,
|
|
80
|
+
layer1Expected: e.layer1Expected,
|
|
81
|
+
layer2Expected: e.layer2Expected,
|
|
82
|
+
layer3Expected: e.layer3Expected,
|
|
83
|
+
verdict,
|
|
84
|
+
principle: typeof e.principle === 'string' ? e.principle : undefined,
|
|
85
|
+
notes: typeof e.notes === 'string' ? e.notes : undefined,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function requireString(obj, key) {
|
|
89
|
+
const v = obj[key];
|
|
90
|
+
if (typeof v !== 'string' || v.length === 0) {
|
|
91
|
+
throw new Error(`Field "${key}" must be a non-empty string`);
|
|
92
|
+
}
|
|
93
|
+
return v;
|
|
94
|
+
}
|
|
95
|
+
function requireEnum(obj, key, options) {
|
|
96
|
+
const v = obj[key];
|
|
97
|
+
if (typeof v !== 'string' || !options.includes(v)) {
|
|
98
|
+
throw new Error(`Field "${key}" must be one of: ${options.join(', ')}`);
|
|
99
|
+
}
|
|
100
|
+
return v;
|
|
101
|
+
}
|
|
102
|
+
export const MIN_TOTAL_EXEMPLARS = 5;
|
|
103
|
+
export function validatePhase2bExemplars(bank) {
|
|
104
|
+
const gaps = [];
|
|
105
|
+
const total = bank.sa1.length + bank.sa2.length;
|
|
106
|
+
if (total < MIN_TOTAL_EXEMPLARS) {
|
|
107
|
+
gaps.push({
|
|
108
|
+
dimension: 'overall',
|
|
109
|
+
reason: `Need ≥${MIN_TOTAL_EXEMPLARS} exemplars total (have ${total})`,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
for (const dimension of ['SA-1', 'SA-2']) {
|
|
113
|
+
const slice = dimension === 'SA-1' ? bank.sa1 : bank.sa2;
|
|
114
|
+
if (slice.length === 0) {
|
|
115
|
+
gaps.push({
|
|
116
|
+
dimension,
|
|
117
|
+
reason: `Need ≥1 exemplar for ${dimension}`,
|
|
118
|
+
});
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
const hasTP = slice.some((e) => e.type === 'true-positive');
|
|
122
|
+
const hasFP = slice.some((e) => e.type === 'false-positive');
|
|
123
|
+
if (!hasTP) {
|
|
124
|
+
gaps.push({
|
|
125
|
+
dimension,
|
|
126
|
+
reason: `${dimension} needs ≥1 true-positive exemplar`,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
if (!hasFP) {
|
|
130
|
+
gaps.push({
|
|
131
|
+
dimension,
|
|
132
|
+
reason: `${dimension} needs ≥1 false-positive exemplar (regression tracking)`,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return { ready: gaps.length === 0, gaps };
|
|
137
|
+
}
|
|
138
|
+
export function computeLayerPrecision(exemplars) {
|
|
139
|
+
const tp = exemplars.filter((e) => e.type === 'true-positive').length;
|
|
140
|
+
const fp = exemplars.filter((e) => e.type === 'false-positive').length;
|
|
141
|
+
const tn = exemplars.filter((e) => e.type === 'true-negative').length;
|
|
142
|
+
const fn = exemplars.filter((e) => e.type === 'false-negative').length;
|
|
143
|
+
const precision = tp + fp === 0 ? 0 : tp / (tp + fp);
|
|
144
|
+
const recall = tp + fn === 0 ? 0 : tp / (tp + fn);
|
|
145
|
+
return {
|
|
146
|
+
truePositives: tp,
|
|
147
|
+
falsePositives: fp,
|
|
148
|
+
trueNegatives: tn,
|
|
149
|
+
falseNegatives: fn,
|
|
150
|
+
precision,
|
|
151
|
+
recall,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=exemplar-bank.js.map
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SA feedback store (RFC-0008 Addendum B §B.8).
|
|
3
|
+
*
|
|
4
|
+
* Wraps the `did_feedback_events` table and exposes precision
|
|
5
|
+
* aggregations used by the feedback flywheel (AISDLC-65 Cκ
|
|
6
|
+
* calibration, AISDLC-66 phase-weight auto-calibration).
|
|
7
|
+
*
|
|
8
|
+
* Signal semantics (§B.8.1):
|
|
9
|
+
* accept — admitted item was correctly scored (true-positive on positive path)
|
|
10
|
+
* dismiss — admitted item should NOT have been (false-positive)
|
|
11
|
+
* escalate — scored too low; should have been ranked higher (false-negative)
|
|
12
|
+
* override — HC_override bypass triggered; feedback auto-emitted
|
|
13
|
+
*/
|
|
14
|
+
import type { StateStore } from '../state/store.js';
|
|
15
|
+
import type { DidFeedbackEventRecord, FeedbackSignal, SaDimension } from '../state/types.js';
|
|
16
|
+
export interface RecordFeedbackInput {
|
|
17
|
+
didName: string;
|
|
18
|
+
issueNumber: number;
|
|
19
|
+
dimension: SaDimension;
|
|
20
|
+
signal: FeedbackSignal;
|
|
21
|
+
principal?: string;
|
|
22
|
+
/** Optional category label — e.g. product/design/engineering pillar, or custom. */
|
|
23
|
+
category?: string;
|
|
24
|
+
/** Layer 2 structural score at admission time (for precision tracking). */
|
|
25
|
+
structuralScore?: number;
|
|
26
|
+
/** Layer 3 LLM score at admission time. */
|
|
27
|
+
llmScore?: number;
|
|
28
|
+
/** Composite SA score. */
|
|
29
|
+
compositeScore?: number;
|
|
30
|
+
notes?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface PrecisionWindow {
|
|
33
|
+
/** ISO timestamp — filter feedback events newer than this. */
|
|
34
|
+
since?: string;
|
|
35
|
+
/** Filter to one dimension. */
|
|
36
|
+
dimension?: SaDimension;
|
|
37
|
+
}
|
|
38
|
+
export interface PrecisionResult {
|
|
39
|
+
/** Sample size after filters. */
|
|
40
|
+
sampleSize: number;
|
|
41
|
+
/** Directionally-correct count (accept/escalate when score was high, dismiss when score was low). */
|
|
42
|
+
correct: number;
|
|
43
|
+
/** Precision = correct / sampleSize. 0 when sampleSize=0. */
|
|
44
|
+
precision: number;
|
|
45
|
+
}
|
|
46
|
+
export interface CategoryFalsePositive {
|
|
47
|
+
category: string;
|
|
48
|
+
sampleSize: number;
|
|
49
|
+
falsePositiveCount: number;
|
|
50
|
+
falsePositiveRate: number;
|
|
51
|
+
}
|
|
52
|
+
export declare class SAFeedbackStore {
|
|
53
|
+
private readonly store;
|
|
54
|
+
constructor(store: StateStore);
|
|
55
|
+
record(input: RecordFeedbackInput): number;
|
|
56
|
+
list(window?: PrecisionWindow): DidFeedbackEventRecord[];
|
|
57
|
+
/**
|
|
58
|
+
* Directional correctness of the Layer 2 structural score.
|
|
59
|
+
*
|
|
60
|
+
* signal=accept + structural ≥ 0.5 → correct (high score → accepted)
|
|
61
|
+
* signal=accept + structural < 0.5 → incorrect (low score → accepted anyway, underconfident)
|
|
62
|
+
* signal=dismiss + structural ≥ 0.5 → incorrect (high score → dismissed, overconfident)
|
|
63
|
+
* signal=dismiss + structural < 0.5 → correct (low score → dismissed)
|
|
64
|
+
* signal=escalate + structural < 0.5 → correct (low score → escalated, was underscored)
|
|
65
|
+
* signal=escalate + structural ≥ 0.5 → incorrect (high score → escalated? unusual)
|
|
66
|
+
* signal=override → EXCLUDED (bypass, not a judgement on structural)
|
|
67
|
+
*/
|
|
68
|
+
structuralPrecision(window?: PrecisionWindow): PrecisionResult;
|
|
69
|
+
llmPrecision(window?: PrecisionWindow): PrecisionResult;
|
|
70
|
+
/**
|
|
71
|
+
* Categories with the highest false-positive rates — i.e. where
|
|
72
|
+
* `dismiss` signals dominate `accept`. Phase 3 calibration
|
|
73
|
+
* (AISDLC-66) should prioritise tuning these.
|
|
74
|
+
*/
|
|
75
|
+
highFalsePositiveCategories(window?: PrecisionWindow, minSampleSize?: number): CategoryFalsePositive[];
|
|
76
|
+
private computeDirectionalPrecision;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Map a GitHub label addition event to a feedback signal, or
|
|
80
|
+
* undefined if the label isn't one of our SA signals.
|
|
81
|
+
*/
|
|
82
|
+
export declare function classifyLabel(label: string): FeedbackSignal | undefined;
|
|
83
|
+
export declare const SA_FEEDBACK_LABELS: readonly string[];
|
|
84
|
+
export interface OverrideFeedbackInput {
|
|
85
|
+
didName: string;
|
|
86
|
+
issueNumber: number;
|
|
87
|
+
/** The override reason from `PriorityScore.override.reason`. */
|
|
88
|
+
reason?: string;
|
|
89
|
+
/** Principal who invoked the override, if known. */
|
|
90
|
+
principal?: string;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Emit an `override` feedback row when an admission score carries an
|
|
94
|
+
* `override` bypass. Safe no-op when `override` is undefined — callers
|
|
95
|
+
* can invoke unconditionally.
|
|
96
|
+
*/
|
|
97
|
+
export declare function recordOverrideFeedback(feedback: SAFeedbackStore, override: {
|
|
98
|
+
reason?: string;
|
|
99
|
+
} | undefined, context: OverrideFeedbackInput): void;
|
|
100
|
+
//# sourceMappingURL=feedback-store.d.ts.map
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SA feedback store (RFC-0008 Addendum B §B.8).
|
|
3
|
+
*
|
|
4
|
+
* Wraps the `did_feedback_events` table and exposes precision
|
|
5
|
+
* aggregations used by the feedback flywheel (AISDLC-65 Cκ
|
|
6
|
+
* calibration, AISDLC-66 phase-weight auto-calibration).
|
|
7
|
+
*
|
|
8
|
+
* Signal semantics (§B.8.1):
|
|
9
|
+
* accept — admitted item was correctly scored (true-positive on positive path)
|
|
10
|
+
* dismiss — admitted item should NOT have been (false-positive)
|
|
11
|
+
* escalate — scored too low; should have been ranked higher (false-negative)
|
|
12
|
+
* override — HC_override bypass triggered; feedback auto-emitted
|
|
13
|
+
*/
|
|
14
|
+
// Directional-correctness threshold: score ≥ this counts as "high" for
|
|
15
|
+
// dismiss-signal evaluation (a high-scored item that got dismissed was
|
|
16
|
+
// over-scored, so directionally incorrect).
|
|
17
|
+
const HIGH_SCORE_THRESHOLD = 0.5;
|
|
18
|
+
// ── Store ────────────────────────────────────────────────────────────
|
|
19
|
+
export class SAFeedbackStore {
|
|
20
|
+
store;
|
|
21
|
+
constructor(store) {
|
|
22
|
+
this.store = store;
|
|
23
|
+
}
|
|
24
|
+
record(input) {
|
|
25
|
+
return this.store.recordDidFeedback({
|
|
26
|
+
didName: input.didName,
|
|
27
|
+
issueNumber: input.issueNumber,
|
|
28
|
+
dimension: input.dimension,
|
|
29
|
+
signal: input.signal,
|
|
30
|
+
principal: input.principal,
|
|
31
|
+
category: input.category,
|
|
32
|
+
structuralScore: input.structuralScore,
|
|
33
|
+
llmScore: input.llmScore,
|
|
34
|
+
compositeScore: input.compositeScore,
|
|
35
|
+
notes: input.notes,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
list(window = {}) {
|
|
39
|
+
return this.store.getDidFeedbackEvents({
|
|
40
|
+
dimension: window.dimension,
|
|
41
|
+
since: window.since,
|
|
42
|
+
limit: 5000,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Directional correctness of the Layer 2 structural score.
|
|
47
|
+
*
|
|
48
|
+
* signal=accept + structural ≥ 0.5 → correct (high score → accepted)
|
|
49
|
+
* signal=accept + structural < 0.5 → incorrect (low score → accepted anyway, underconfident)
|
|
50
|
+
* signal=dismiss + structural ≥ 0.5 → incorrect (high score → dismissed, overconfident)
|
|
51
|
+
* signal=dismiss + structural < 0.5 → correct (low score → dismissed)
|
|
52
|
+
* signal=escalate + structural < 0.5 → correct (low score → escalated, was underscored)
|
|
53
|
+
* signal=escalate + structural ≥ 0.5 → incorrect (high score → escalated? unusual)
|
|
54
|
+
* signal=override → EXCLUDED (bypass, not a judgement on structural)
|
|
55
|
+
*/
|
|
56
|
+
structuralPrecision(window = {}) {
|
|
57
|
+
return this.computeDirectionalPrecision(window, (e) => e.structuralScore);
|
|
58
|
+
}
|
|
59
|
+
llmPrecision(window = {}) {
|
|
60
|
+
return this.computeDirectionalPrecision(window, (e) => e.llmScore);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Categories with the highest false-positive rates — i.e. where
|
|
64
|
+
* `dismiss` signals dominate `accept`. Phase 3 calibration
|
|
65
|
+
* (AISDLC-66) should prioritise tuning these.
|
|
66
|
+
*/
|
|
67
|
+
highFalsePositiveCategories(window = {}, minSampleSize = 3) {
|
|
68
|
+
const events = this.list(window).filter((e) => e.category);
|
|
69
|
+
const grouped = new Map();
|
|
70
|
+
for (const e of events) {
|
|
71
|
+
const key = e.category;
|
|
72
|
+
const bucket = grouped.get(key) ?? { accept: 0, dismiss: 0 };
|
|
73
|
+
if (e.signal === 'accept')
|
|
74
|
+
bucket.accept++;
|
|
75
|
+
else if (e.signal === 'dismiss')
|
|
76
|
+
bucket.dismiss++;
|
|
77
|
+
grouped.set(key, bucket);
|
|
78
|
+
}
|
|
79
|
+
const rows = [];
|
|
80
|
+
for (const [category, counts] of grouped) {
|
|
81
|
+
const sampleSize = counts.accept + counts.dismiss;
|
|
82
|
+
if (sampleSize < minSampleSize)
|
|
83
|
+
continue;
|
|
84
|
+
rows.push({
|
|
85
|
+
category,
|
|
86
|
+
sampleSize,
|
|
87
|
+
falsePositiveCount: counts.dismiss,
|
|
88
|
+
falsePositiveRate: sampleSize === 0 ? 0 : counts.dismiss / sampleSize,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
rows.sort((a, b) => b.falsePositiveRate - a.falsePositiveRate);
|
|
92
|
+
return rows;
|
|
93
|
+
}
|
|
94
|
+
computeDirectionalPrecision(window, scoreOf) {
|
|
95
|
+
const events = this.list(window);
|
|
96
|
+
let sampleSize = 0;
|
|
97
|
+
let correct = 0;
|
|
98
|
+
for (const e of events) {
|
|
99
|
+
if (e.signal === 'override')
|
|
100
|
+
continue;
|
|
101
|
+
const score = scoreOf(e);
|
|
102
|
+
if (score === undefined)
|
|
103
|
+
continue;
|
|
104
|
+
sampleSize++;
|
|
105
|
+
const high = score >= HIGH_SCORE_THRESHOLD;
|
|
106
|
+
const directionallyCorrect = (e.signal === 'accept' && high) ||
|
|
107
|
+
(e.signal === 'dismiss' && !high) ||
|
|
108
|
+
(e.signal === 'escalate' && !high);
|
|
109
|
+
if (directionallyCorrect)
|
|
110
|
+
correct++;
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
sampleSize,
|
|
114
|
+
correct,
|
|
115
|
+
precision: sampleSize === 0 ? 0 : correct / sampleSize,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// ── Signal classification ────────────────────────────────────────────
|
|
120
|
+
const SA_LABELS = Object.freeze({
|
|
121
|
+
'sa/accept': 'accept',
|
|
122
|
+
'sa/dismiss': 'dismiss',
|
|
123
|
+
'sa/escalate': 'escalate',
|
|
124
|
+
});
|
|
125
|
+
/**
|
|
126
|
+
* Map a GitHub label addition event to a feedback signal, or
|
|
127
|
+
* undefined if the label isn't one of our SA signals.
|
|
128
|
+
*/
|
|
129
|
+
export function classifyLabel(label) {
|
|
130
|
+
return SA_LABELS[label.toLowerCase()];
|
|
131
|
+
}
|
|
132
|
+
export const SA_FEEDBACK_LABELS = Object.freeze([
|
|
133
|
+
'sa/accept',
|
|
134
|
+
'sa/dismiss',
|
|
135
|
+
'sa/escalate',
|
|
136
|
+
]);
|
|
137
|
+
/**
|
|
138
|
+
* Emit an `override` feedback row when an admission score carries an
|
|
139
|
+
* `override` bypass. Safe no-op when `override` is undefined — callers
|
|
140
|
+
* can invoke unconditionally.
|
|
141
|
+
*/
|
|
142
|
+
export function recordOverrideFeedback(feedback, override, context) {
|
|
143
|
+
if (!override)
|
|
144
|
+
return;
|
|
145
|
+
feedback.record({
|
|
146
|
+
didName: context.didName,
|
|
147
|
+
issueNumber: context.issueNumber,
|
|
148
|
+
// Override is a bypass of the whole SA score; by convention we
|
|
149
|
+
// record against SA-1 (the dimension that gates admission).
|
|
150
|
+
dimension: 'SA-1',
|
|
151
|
+
signal: 'override',
|
|
152
|
+
principal: context.principal,
|
|
153
|
+
notes: override.reason ?? context.reason,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=feedback-store.js.map
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SA scoring orchestration (RFC-0008 Addendum B §B.7.3).
|
|
3
|
+
*
|
|
4
|
+
* `scoreSoulAlignment(input, deps)` runs the three-layer SA scorer end
|
|
5
|
+
* to end with phase-aware short-circuits:
|
|
6
|
+
*
|
|
7
|
+
* - Layer 1 hard gate → skips Layer 2/3, SA-1 forced to 0
|
|
8
|
+
* - Phase 2a shadow → all layers computed + persisted, but
|
|
9
|
+
* shadowMode=true signals caller to use
|
|
10
|
+
* label-based soulAlignment in ranking
|
|
11
|
+
* - Phase 2b/2c/3 → sa1 replaces label-based soulAlignment
|
|
12
|
+
*
|
|
13
|
+
* Persists one `did_scoring_events` row per dimension so operators can
|
|
14
|
+
* query per-dimension precision by phase.
|
|
15
|
+
*/
|
|
16
|
+
import type { DesignIntentDocument, DesignSystemBinding } from '@ai-sdlc/reference';
|
|
17
|
+
import type { StateStore } from '../state/store.js';
|
|
18
|
+
import { type CompiledDid } from './did-compiler.js';
|
|
19
|
+
import type { DepparseClient } from './depparse-client.js';
|
|
20
|
+
import type { LLMClient } from './layer3-llm.js';
|
|
21
|
+
import { type DeterministicScoringResult } from './layer1-deterministic.js';
|
|
22
|
+
import { type DomainRelevanceResult, type PrincipleCoverageVector } from './layer2-structural.js';
|
|
23
|
+
import { type LLMScoringResult } from './layer3-llm.js';
|
|
24
|
+
import { type PhaseWeights, type SaPhase, type SoulAlignmentResult } from './composite.js';
|
|
25
|
+
export interface ScoreSoulAlignmentInput {
|
|
26
|
+
issueText: string;
|
|
27
|
+
did: DesignIntentDocument;
|
|
28
|
+
dsb?: DesignSystemBinding;
|
|
29
|
+
phase: SaPhase;
|
|
30
|
+
calibratedWeights?: PhaseWeights;
|
|
31
|
+
/** Observed metrics keyed by signal metric name (for Layer 1). */
|
|
32
|
+
observedMetrics?: Record<string, number>;
|
|
33
|
+
/** Issue number for audit trail — used when persisting did_scoring_events. */
|
|
34
|
+
issueNumber?: number;
|
|
35
|
+
}
|
|
36
|
+
export interface ScoreSoulAlignmentDeps {
|
|
37
|
+
depparse: DepparseClient;
|
|
38
|
+
llm: LLMClient;
|
|
39
|
+
stateStore?: StateStore;
|
|
40
|
+
/** Pre-compiled DID (skip recompilation). */
|
|
41
|
+
compiledDid?: CompiledDid;
|
|
42
|
+
}
|
|
43
|
+
export interface SoulAlignmentScoringResult {
|
|
44
|
+
/** Final composite SA-1/SA-2 (respecting shadow mode). */
|
|
45
|
+
sa1: number;
|
|
46
|
+
sa2: number;
|
|
47
|
+
/** Full composite result including per-layer contributions. */
|
|
48
|
+
composite: SoulAlignmentResult;
|
|
49
|
+
/** Raw Layer 1 output (useful for preVerifiedSummary). */
|
|
50
|
+
layer1: DeterministicScoringResult;
|
|
51
|
+
/** Raw Layer 2 outputs. Always computed (even in shadow mode). */
|
|
52
|
+
layer2: {
|
|
53
|
+
domainRelevance: DomainRelevanceResult;
|
|
54
|
+
principleCoverage: PrincipleCoverageVector;
|
|
55
|
+
};
|
|
56
|
+
/** Raw Layer 3 output. Absent when Layer 1 hard-gated (skipped). */
|
|
57
|
+
layer3?: LLMScoringResult;
|
|
58
|
+
/** True when Phase 2a — caller must use label-based soulAlignment. */
|
|
59
|
+
shadowMode: boolean;
|
|
60
|
+
/** Audit — phase + weights used. */
|
|
61
|
+
phase: SaPhase;
|
|
62
|
+
weights: PhaseWeights;
|
|
63
|
+
}
|
|
64
|
+
export declare function scoreSoulAlignment(input: ScoreSoulAlignmentInput, deps: ScoreSoulAlignmentDeps): Promise<SoulAlignmentScoringResult>;
|
|
65
|
+
/**
|
|
66
|
+
* Resolve the soulAlignment value to feed the admission composite
|
|
67
|
+
* based on phase + SA result. In shadow mode (2a), returns undefined
|
|
68
|
+
* so the caller falls back to the label-based heuristic.
|
|
69
|
+
*/
|
|
70
|
+
export declare function resolveSoulAlignmentOverride(result: SoulAlignmentScoringResult): number | undefined;
|
|
71
|
+
//# sourceMappingURL=index.d.ts.map
|