@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.
Files changed (227) hide show
  1. package/dist/action-enforcement.d.ts +26 -0
  2. package/dist/action-enforcement.js +70 -0
  3. package/dist/admission-composite.d.ts +67 -0
  4. package/dist/admission-composite.js +158 -0
  5. package/dist/admission-enrichment.d.ts +142 -0
  6. package/dist/admission-enrichment.js +331 -0
  7. package/dist/admission-hc.d.ts +62 -0
  8. package/dist/admission-hc.js +83 -0
  9. package/dist/admission-score.d.ts +148 -0
  10. package/dist/admission-score.js +237 -0
  11. package/dist/analysis/analyzer.js +3 -2
  12. package/dist/analysis/convention-detector.d.ts +85 -2
  13. package/dist/analysis/convention-detector.js +375 -70
  14. package/dist/analysis/diff-analyzer.d.ts +33 -0
  15. package/dist/analysis/diff-analyzer.js +122 -0
  16. package/dist/analysis/hotspot-analyzer.js +3 -1
  17. package/dist/analysis/index.d.ts +2 -1
  18. package/dist/analysis/index.js +2 -1
  19. package/dist/artifacts/index.d.ts +65 -0
  20. package/dist/artifacts/index.js +142 -0
  21. package/dist/backlog-adapter.d.ts +121 -0
  22. package/dist/backlog-adapter.js +438 -0
  23. package/dist/calibration.d.ts +43 -0
  24. package/dist/calibration.js +76 -0
  25. package/dist/cli/commands/agents.d.ts +24 -0
  26. package/dist/cli/commands/agents.js +66 -1
  27. package/dist/cli/commands/git-remote.d.ts +49 -0
  28. package/dist/cli/commands/git-remote.js +91 -0
  29. package/dist/cli/commands/health.d.ts +4 -0
  30. package/dist/cli/commands/health.js +13 -1
  31. package/dist/cli/commands/init.d.ts +26 -0
  32. package/dist/cli/commands/init.js +164 -22
  33. package/dist/cli/commands/mcp-setup.d.ts +31 -0
  34. package/dist/cli/commands/mcp-setup.js +78 -8
  35. package/dist/cli/formatters/table.js +15 -2
  36. package/dist/cli/index.d.ts +14 -1
  37. package/dist/cli/index.js +81 -20
  38. package/dist/cli/versions.d.ts +57 -0
  39. package/dist/cli/versions.js +128 -0
  40. package/dist/code-area-classifier.d.ts +21 -0
  41. package/dist/code-area-classifier.js +48 -0
  42. package/dist/config.d.ts +33 -1
  43. package/dist/config.js +78 -8
  44. package/dist/cycle-utils.d.ts +51 -0
  45. package/dist/cycle-utils.js +77 -0
  46. package/dist/database/adapters/external.d.ts +24 -0
  47. package/dist/database/adapters/external.js +80 -0
  48. package/dist/database/adapters/neon.d.ts +41 -0
  49. package/dist/database/adapters/neon.js +98 -0
  50. package/dist/database/adapters/pg-snapshot-restore.d.ts +28 -0
  51. package/dist/database/adapters/pg-snapshot-restore.js +68 -0
  52. package/dist/database/adapters/sqlite-copy.d.ts +32 -0
  53. package/dist/database/adapters/sqlite-copy.js +145 -0
  54. package/dist/database/connection-injection.d.ts +35 -0
  55. package/dist/database/connection-injection.js +93 -0
  56. package/dist/database/index.d.ts +12 -0
  57. package/dist/database/index.js +23 -0
  58. package/dist/database/registry.d.ts +13 -0
  59. package/dist/database/registry.js +27 -0
  60. package/dist/database/topology.d.ts +52 -0
  61. package/dist/database/topology.js +44 -0
  62. package/dist/database/types.d.ts +89 -0
  63. package/dist/database/types.js +26 -0
  64. package/dist/defaults.d.ts +5 -0
  65. package/dist/defaults.js +5 -0
  66. package/dist/design-authority.d.ts +40 -0
  67. package/dist/design-authority.js +71 -0
  68. package/dist/design-lookahead.d.ts +64 -0
  69. package/dist/design-lookahead.js +86 -0
  70. package/dist/design-quality-trend.d.ts +87 -0
  71. package/dist/design-quality-trend.js +190 -0
  72. package/dist/design-system-context.d.ts +46 -0
  73. package/dist/design-system-context.js +80 -0
  74. package/dist/design-system-correction-loop.d.ts +64 -0
  75. package/dist/design-system-correction-loop.js +128 -0
  76. package/dist/design-system-metrics.d.ts +61 -0
  77. package/dist/design-system-metrics.js +104 -0
  78. package/dist/design-system-stewardship.d.ts +22 -0
  79. package/dist/design-system-stewardship.js +85 -0
  80. package/dist/design-system-validation.d.ts +37 -0
  81. package/dist/design-system-validation.js +88 -0
  82. package/dist/dispatch/index.d.ts +4 -0
  83. package/dist/dispatch/index.js +4 -0
  84. package/dist/dispatch/merge-gate.d.ts +46 -0
  85. package/dist/dispatch/merge-gate.js +90 -0
  86. package/dist/dispatch/requeue.d.ts +57 -0
  87. package/dist/dispatch/requeue.js +131 -0
  88. package/dist/dispatch/worker-pool.d.ts +62 -0
  89. package/dist/dispatch/worker-pool.js +60 -0
  90. package/dist/execute.d.ts +40 -0
  91. package/dist/execute.js +295 -30
  92. package/dist/fix-ci.js +40 -3
  93. package/dist/fix-review.d.ts +66 -0
  94. package/dist/fix-review.js +448 -0
  95. package/dist/harness/adapters/claude-code.d.ts +29 -0
  96. package/dist/harness/adapters/claude-code.js +191 -0
  97. package/dist/harness/adapters/codex.d.ts +25 -0
  98. package/dist/harness/adapters/codex.js +61 -0
  99. package/dist/harness/independence.d.ts +51 -0
  100. package/dist/harness/independence.js +67 -0
  101. package/dist/harness/index.d.ts +14 -0
  102. package/dist/harness/index.js +20 -0
  103. package/dist/harness/registry.d.ts +17 -0
  104. package/dist/harness/registry.js +31 -0
  105. package/dist/harness/types.d.ts +123 -0
  106. package/dist/harness/types.js +8 -0
  107. package/dist/harness/version-probe.d.ts +14 -0
  108. package/dist/harness/version-probe.js +123 -0
  109. package/dist/index.d.ts +39 -5
  110. package/dist/index.js +48 -3
  111. package/dist/models/classifier.d.ts +76 -0
  112. package/dist/models/classifier.js +221 -0
  113. package/dist/models/index.d.ts +3 -0
  114. package/dist/models/index.js +3 -0
  115. package/dist/models/registry.d.ts +97 -0
  116. package/dist/models/registry.js +173 -0
  117. package/dist/pillar-breakdown.d.ts +63 -0
  118. package/dist/pillar-breakdown.js +153 -0
  119. package/dist/pipeline-cycle-detector.d.ts +70 -0
  120. package/dist/pipeline-cycle-detector.js +111 -0
  121. package/dist/priority.d.ts +7 -76
  122. package/dist/priority.js +18 -5
  123. package/dist/review-meta.d.ts +65 -0
  124. package/dist/review-meta.js +149 -0
  125. package/dist/review.d.ts +35 -0
  126. package/dist/review.js +84 -0
  127. package/dist/runners/claude-code-sdk.d.ts +31 -0
  128. package/dist/runners/claude-code-sdk.js +238 -0
  129. package/dist/runners/claude-code.d.ts +3 -0
  130. package/dist/runners/claude-code.js +319 -73
  131. package/dist/runners/codex.js +4 -1
  132. package/dist/runners/copilot.js +4 -1
  133. package/dist/runners/cursor.js +4 -1
  134. package/dist/runners/git-utils.d.ts +81 -0
  135. package/dist/runners/git-utils.js +201 -0
  136. package/dist/runners/index.d.ts +5 -1
  137. package/dist/runners/index.js +4 -0
  138. package/dist/runners/review-agent.d.ts +67 -0
  139. package/dist/runners/review-agent.js +350 -0
  140. package/dist/runners/runner-registry.js +10 -0
  141. package/dist/runners/sdk-review-runner.d.ts +65 -0
  142. package/dist/runners/sdk-review-runner.js +185 -0
  143. package/dist/runners/security-triage.d.ts +20 -4
  144. package/dist/runners/security-triage.js +42 -14
  145. package/dist/runners/types.d.ts +25 -0
  146. package/dist/runtime/attestations.d.ts +274 -0
  147. package/dist/runtime/attestations.js +460 -0
  148. package/dist/runtime/git-env.d.ts +53 -0
  149. package/dist/runtime/git-env.js +60 -0
  150. package/dist/runtime/index.d.ts +7 -0
  151. package/dist/runtime/index.js +7 -0
  152. package/dist/runtime/parallelism-flag.d.ts +10 -0
  153. package/dist/runtime/parallelism-flag.js +18 -0
  154. package/dist/runtime/port-allocator.d.ts +21 -0
  155. package/dist/runtime/port-allocator.js +66 -0
  156. package/dist/runtime/worktree-pool.d.ts +86 -0
  157. package/dist/runtime/worktree-pool.js +204 -0
  158. package/dist/runtime/worktree.d.ts +25 -0
  159. package/dist/runtime/worktree.js +111 -0
  160. package/dist/sa-scoring/auto-calibrate.d.ts +69 -0
  161. package/dist/sa-scoring/auto-calibrate.js +107 -0
  162. package/dist/sa-scoring/c1-sa2-computable.d.ts +42 -0
  163. package/dist/sa-scoring/c1-sa2-computable.js +59 -0
  164. package/dist/sa-scoring/composite.d.ts +107 -0
  165. package/dist/sa-scoring/composite.js +139 -0
  166. package/dist/sa-scoring/depparse-client.d.ts +79 -0
  167. package/dist/sa-scoring/depparse-client.js +187 -0
  168. package/dist/sa-scoring/did-compiler.d.ts +122 -0
  169. package/dist/sa-scoring/did-compiler.js +286 -0
  170. package/dist/sa-scoring/drift-monitor.d.ts +84 -0
  171. package/dist/sa-scoring/drift-monitor.js +186 -0
  172. package/dist/sa-scoring/exemplar-bank.d.ts +78 -0
  173. package/dist/sa-scoring/exemplar-bank.js +154 -0
  174. package/dist/sa-scoring/feedback-store.d.ts +100 -0
  175. package/dist/sa-scoring/feedback-store.js +156 -0
  176. package/dist/sa-scoring/index.d.ts +71 -0
  177. package/dist/sa-scoring/index.js +158 -0
  178. package/dist/sa-scoring/layer1-deterministic.d.ts +115 -0
  179. package/dist/sa-scoring/layer1-deterministic.js +298 -0
  180. package/dist/sa-scoring/layer2-structural.d.ts +71 -0
  181. package/dist/sa-scoring/layer2-structural.js +151 -0
  182. package/dist/sa-scoring/layer3-llm.d.ts +86 -0
  183. package/dist/sa-scoring/layer3-llm.js +282 -0
  184. package/dist/sa-scoring/rescore-orchestrator.d.ts +52 -0
  185. package/dist/sa-scoring/rescore-orchestrator.js +47 -0
  186. package/dist/scheduling/burn-down.d.ts +27 -0
  187. package/dist/scheduling/burn-down.js +43 -0
  188. package/dist/scheduling/calibration.d.ts +42 -0
  189. package/dist/scheduling/calibration.js +150 -0
  190. package/dist/scheduling/index.d.ts +8 -0
  191. package/dist/scheduling/index.js +8 -0
  192. package/dist/scheduling/ledger.d.ts +59 -0
  193. package/dist/scheduling/ledger.js +216 -0
  194. package/dist/scheduling/off-peak.d.ts +27 -0
  195. package/dist/scheduling/off-peak.js +112 -0
  196. package/dist/scheduling/schedule-decision.d.ts +39 -0
  197. package/dist/scheduling/schedule-decision.js +89 -0
  198. package/dist/scheduling/tier-analysis.d.ts +47 -0
  199. package/dist/scheduling/tier-analysis.js +81 -0
  200. package/dist/scheduling/types.d.ts +140 -0
  201. package/dist/scheduling/types.js +11 -0
  202. package/dist/shared.d.ts +13 -0
  203. package/dist/shared.js +32 -0
  204. package/dist/state/index.d.ts +1 -1
  205. package/dist/state/schema.d.ts +6 -1
  206. package/dist/state/schema.js +276 -1
  207. package/dist/state/store.d.ts +71 -1
  208. package/dist/state/store.js +464 -7
  209. package/dist/state/types.d.ts +174 -0
  210. package/dist/types.d.ts +1 -1
  211. package/dist/validate-agent-output.js +4 -1
  212. package/dist/watch.js +6 -0
  213. package/dist/workflow-patterns/artifact-writer.d.ts +16 -0
  214. package/dist/workflow-patterns/artifact-writer.js +34 -0
  215. package/dist/workflow-patterns/classifiers.d.ts +10 -0
  216. package/dist/workflow-patterns/classifiers.js +72 -0
  217. package/dist/workflow-patterns/detector.d.ts +27 -0
  218. package/dist/workflow-patterns/detector.js +186 -0
  219. package/dist/workflow-patterns/index.d.ts +8 -0
  220. package/dist/workflow-patterns/index.js +7 -0
  221. package/dist/workflow-patterns/proposal-generator.d.ts +15 -0
  222. package/dist/workflow-patterns/proposal-generator.js +183 -0
  223. package/dist/workflow-patterns/telemetry-ingest.d.ts +27 -0
  224. package/dist/workflow-patterns/telemetry-ingest.js +103 -0
  225. package/dist/workflow-patterns/types.d.ts +61 -0
  226. package/dist/workflow-patterns/types.js +11 -0
  227. 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