@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,122 @@
1
+ /**
2
+ * DID compilation pipeline (RFC-0008 Addendum B §B.3.3).
3
+ *
4
+ * Consumes a `DesignIntentDocument` and produces six deterministic
5
+ * artifacts consumed by the three-layer SA scorer:
6
+ *
7
+ * 1. scopeLists — flattened in/out scope synonyms
8
+ * 2. constraintRules — detection patterns per soul constraint
9
+ * 3. antiPatternLists — product + per-principle + voice + visual
10
+ * 4. measurableSignals — principle signals + visual constraints
11
+ * 5. bm25Corpus — SA-1 corpus (mission + experientialTargets,
12
+ * core fields weighted 2×, evolving 1×)
13
+ * 6. principleCorpora — one SA-2 corpus per designPrinciple
14
+ *
15
+ * The output is stable given identical input (canonical JSON + sha256
16
+ * hash) so `did_compiled_artifacts.source_hash` can be used to detect
17
+ * DID changes cheaply.
18
+ */
19
+ import type { Constraint, DesignIntentDocument, IdentityClass, MeasurableOperator } from '@ai-sdlc/reference';
20
+ export interface CompiledScopeEntry {
21
+ label: string;
22
+ synonyms: string[];
23
+ identityClass: IdentityClass;
24
+ }
25
+ export interface CompiledScopeLists {
26
+ inScope: CompiledScopeEntry[];
27
+ outOfScope: CompiledScopeEntry[];
28
+ }
29
+ export interface CompiledConstraintRule {
30
+ id: string;
31
+ concept: string;
32
+ relationship: Constraint['relationship'];
33
+ detectionPatterns: string[];
34
+ identityClass: IdentityClass;
35
+ }
36
+ export interface CompiledAntiPattern {
37
+ id: string;
38
+ label: string;
39
+ detectionPatterns: string[];
40
+ identityClass: IdentityClass;
41
+ }
42
+ export interface CompiledAntiPatternLists {
43
+ product: CompiledAntiPattern[];
44
+ /** Keyed by `designPrinciple.id`. */
45
+ perPrinciple: Record<string, CompiledAntiPattern[]>;
46
+ voice: CompiledAntiPattern[];
47
+ visual: CompiledAntiPattern[];
48
+ }
49
+ export interface CompiledMeasurableSignal {
50
+ id: string;
51
+ metric: string;
52
+ threshold: number;
53
+ operator: MeasurableOperator;
54
+ scope?: string;
55
+ /** Present when derived from a specific principle's signals list. */
56
+ sourcePrinciple?: string;
57
+ /** Present when derived from a visual constraint rule. */
58
+ sourceVisual?: string;
59
+ identityClass: IdentityClass;
60
+ }
61
+ export interface Bm25Document {
62
+ id: string;
63
+ tokens: string[];
64
+ /** 2 when derived from `identityClass: 'core'`, else 1. */
65
+ weight: number;
66
+ }
67
+ export interface Bm25Corpus {
68
+ documents: Bm25Document[];
69
+ }
70
+ export type PrincipleCorpora = Record<string, Bm25Corpus>;
71
+ export interface CompiledDid {
72
+ didName: string;
73
+ namespace?: string;
74
+ sourceHash: string;
75
+ scopeLists: CompiledScopeLists;
76
+ constraintRules: CompiledConstraintRule[];
77
+ antiPatternLists: CompiledAntiPatternLists;
78
+ measurableSignals: CompiledMeasurableSignal[];
79
+ bm25Corpus: Bm25Corpus;
80
+ principleCorpora: PrincipleCorpora;
81
+ }
82
+ export declare function tokenize(text: string): string[];
83
+ /**
84
+ * Stable JSON: keys sorted alphabetically at every depth. Matches the
85
+ * Python json.dumps(sort_keys=True) output so cross-language consumers
86
+ * agree on the source hash.
87
+ */
88
+ export declare function canonicalJson(value: unknown): string;
89
+ export declare function hashDidSpec(did: DesignIntentDocument): string;
90
+ export declare function compileDid(did: DesignIntentDocument): CompiledDid;
91
+ export interface ReadinessResult {
92
+ ready: boolean;
93
+ gaps: string[];
94
+ }
95
+ /**
96
+ * Enforce the §B.10.2 minimums a DID must meet before Phase-2b scoring
97
+ * is safe to enable. Returns a list of gaps; empty ⇒ ready.
98
+ */
99
+ export declare function validatePhase2bReadiness(compiled: CompiledDid): ReadinessResult;
100
+ export declare function serializeForStore(compiled: CompiledDid): {
101
+ didName: string;
102
+ namespace?: string;
103
+ sourceHash: string;
104
+ scopeListsJson: string;
105
+ constraintRulesJson: string;
106
+ antiPatternListsJson: string;
107
+ measurableSignalsJson: string;
108
+ bm25CorpusBlob: Buffer;
109
+ principleCorporaBlob: Buffer;
110
+ };
111
+ export declare function deserializeFromStore(record: {
112
+ didName: string;
113
+ namespace?: string;
114
+ sourceHash: string;
115
+ scopeListsJson?: string;
116
+ constraintRulesJson?: string;
117
+ antiPatternListsJson?: string;
118
+ measurableSignalsJson?: string;
119
+ bm25CorpusBlob?: Buffer;
120
+ principleCorporaBlob?: Buffer;
121
+ }): CompiledDid;
122
+ //# sourceMappingURL=did-compiler.d.ts.map
@@ -0,0 +1,286 @@
1
+ /**
2
+ * DID compilation pipeline (RFC-0008 Addendum B §B.3.3).
3
+ *
4
+ * Consumes a `DesignIntentDocument` and produces six deterministic
5
+ * artifacts consumed by the three-layer SA scorer:
6
+ *
7
+ * 1. scopeLists — flattened in/out scope synonyms
8
+ * 2. constraintRules — detection patterns per soul constraint
9
+ * 3. antiPatternLists — product + per-principle + voice + visual
10
+ * 4. measurableSignals — principle signals + visual constraints
11
+ * 5. bm25Corpus — SA-1 corpus (mission + experientialTargets,
12
+ * core fields weighted 2×, evolving 1×)
13
+ * 6. principleCorpora — one SA-2 corpus per designPrinciple
14
+ *
15
+ * The output is stable given identical input (canonical JSON + sha256
16
+ * hash) so `did_compiled_artifacts.source_hash` can be used to detect
17
+ * DID changes cheaply.
18
+ */
19
+ import { createHash } from 'node:crypto';
20
+ // ── Tokenization ─────────────────────────────────────────────────────
21
+ const STOPWORDS = new Set([
22
+ 'the',
23
+ 'and',
24
+ 'for',
25
+ 'are',
26
+ 'with',
27
+ 'from',
28
+ 'that',
29
+ 'this',
30
+ 'will',
31
+ 'have',
32
+ 'has',
33
+ 'been',
34
+ 'were',
35
+ 'your',
36
+ 'our',
37
+ 'their',
38
+ 'they',
39
+ 'must',
40
+ 'may',
41
+ 'not',
42
+ 'into',
43
+ 'onto',
44
+ 'than',
45
+ 'but',
46
+ 'any',
47
+ 'all',
48
+ ]);
49
+ export function tokenize(text) {
50
+ return text
51
+ .toLowerCase()
52
+ .replace(/[^a-z0-9\s]/g, ' ')
53
+ .split(/\s+/)
54
+ .filter((t) => t.length >= 3 && !STOPWORDS.has(t));
55
+ }
56
+ // ── Canonical JSON + hash ────────────────────────────────────────────
57
+ /**
58
+ * Stable JSON: keys sorted alphabetically at every depth. Matches the
59
+ * Python json.dumps(sort_keys=True) output so cross-language consumers
60
+ * agree on the source hash.
61
+ */
62
+ export function canonicalJson(value) {
63
+ return JSON.stringify(value, (_key, v) => {
64
+ if (v && typeof v === 'object' && !Array.isArray(v)) {
65
+ const sorted = {};
66
+ for (const k of Object.keys(v).sort()) {
67
+ sorted[k] = v[k];
68
+ }
69
+ return sorted;
70
+ }
71
+ return v;
72
+ });
73
+ }
74
+ export function hashDidSpec(did) {
75
+ return createHash('sha256').update(canonicalJson(did.spec)).digest('hex');
76
+ }
77
+ // ── Helpers ──────────────────────────────────────────────────────────
78
+ function ic(input) {
79
+ return input?.identityClass ?? 'evolving';
80
+ }
81
+ function compileScopeEntry(term) {
82
+ return {
83
+ label: term.label,
84
+ synonyms: term.synonyms ? Array.from(new Set(term.synonyms)) : [],
85
+ identityClass: ic(term),
86
+ };
87
+ }
88
+ function compileConstraint(c) {
89
+ return {
90
+ id: c.id,
91
+ concept: c.concept,
92
+ relationship: c.relationship,
93
+ detectionPatterns: [...c.detectionPatterns],
94
+ identityClass: ic(c),
95
+ };
96
+ }
97
+ function compileAntiPattern(a) {
98
+ return {
99
+ id: a.id,
100
+ label: a.label,
101
+ detectionPatterns: [...a.detectionPatterns],
102
+ identityClass: ic(a),
103
+ };
104
+ }
105
+ function buildBm25Document(id, text, identityClass) {
106
+ const tokens = tokenize(text);
107
+ if (tokens.length === 0)
108
+ return undefined;
109
+ return { id, tokens, weight: identityClass === 'core' ? 2 : 1 };
110
+ }
111
+ // ── Compilation entry point ──────────────────────────────────────────
112
+ export function compileDid(did) {
113
+ const spec = did.spec;
114
+ // 1. Scope lists
115
+ const scopeLists = {
116
+ inScope: (spec.soulPurpose.scopeBoundaries?.inScope ?? []).map(compileScopeEntry),
117
+ outOfScope: (spec.soulPurpose.scopeBoundaries?.outOfScope ?? []).map(compileScopeEntry),
118
+ };
119
+ // 2. Constraint rules
120
+ const constraintRules = (spec.soulPurpose.constraints ?? []).map(compileConstraint);
121
+ // 3. Anti-pattern lists
122
+ const productAntiPatterns = (spec.soulPurpose.antiPatterns ?? []).map(compileAntiPattern);
123
+ const perPrinciple = {};
124
+ for (const p of spec.soulPurpose.designPrinciples) {
125
+ perPrinciple[p.id] = (p.antiPatterns ?? []).map(compileAntiPattern);
126
+ }
127
+ const voiceAntiPatterns = (spec.brandIdentity?.voiceAntiPatterns ?? []).map(compileAntiPattern);
128
+ const visualAntiPatterns = (spec.brandIdentity?.visualIdentity?.visualAntiPatterns ?? []).map(compileAntiPattern);
129
+ const antiPatternLists = {
130
+ product: productAntiPatterns,
131
+ perPrinciple,
132
+ voice: voiceAntiPatterns,
133
+ visual: visualAntiPatterns,
134
+ };
135
+ // 4. Measurable signals (principle + visual)
136
+ const measurableSignals = [];
137
+ for (const principle of spec.soulPurpose.designPrinciples) {
138
+ for (const sig of principle.measurableSignals ?? []) {
139
+ measurableSignals.push({
140
+ id: sig.id,
141
+ metric: sig.metric,
142
+ threshold: sig.threshold,
143
+ operator: sig.operator,
144
+ scope: sig.scope,
145
+ sourcePrinciple: principle.id,
146
+ identityClass: sig.identityClass ?? principle.identityClass ?? 'evolving',
147
+ });
148
+ }
149
+ }
150
+ for (const vc of spec.brandIdentity?.visualIdentity?.visualConstraints ?? []) {
151
+ measurableSignals.push(compileVisualConstraintSignal(vc));
152
+ }
153
+ // 5. SA-1 BM25 corpus (mission + experientialTargets)
154
+ const bm25Docs = [];
155
+ const missionDoc = buildBm25Document('mission', spec.soulPurpose.mission.value, ic(spec.soulPurpose.mission));
156
+ if (missionDoc)
157
+ bm25Docs.push(missionDoc);
158
+ for (const [name, target] of Object.entries(spec.experientialTargets ?? {})) {
159
+ if (!target)
160
+ continue;
161
+ const doc = buildBm25Document(`experientialTargets.${name}`, JSON.stringify(target), ic(target));
162
+ if (doc)
163
+ bm25Docs.push(doc);
164
+ }
165
+ const bm25Corpus = { documents: bm25Docs };
166
+ // 6. SA-2 principle corpora
167
+ const principleCorpora = {};
168
+ for (const principle of spec.soulPurpose.designPrinciples) {
169
+ principleCorpora[principle.id] = buildPrincipleCorpus(principle);
170
+ }
171
+ return {
172
+ didName: did.metadata.name,
173
+ namespace: did.metadata.namespace,
174
+ sourceHash: hashDidSpec(did),
175
+ scopeLists,
176
+ constraintRules,
177
+ antiPatternLists,
178
+ measurableSignals,
179
+ bm25Corpus,
180
+ principleCorpora,
181
+ };
182
+ }
183
+ function compileVisualConstraintSignal(vc) {
184
+ return {
185
+ id: vc.id,
186
+ metric: vc.rule.metric,
187
+ threshold: vc.rule.threshold,
188
+ operator: vc.rule.operator,
189
+ sourceVisual: vc.id,
190
+ identityClass: ic(vc),
191
+ };
192
+ }
193
+ function buildPrincipleCorpus(principle) {
194
+ const pic = principle.identityClass ?? 'evolving';
195
+ const docs = [];
196
+ const nameDoc = buildBm25Document(`${principle.id}.name`, principle.name, pic);
197
+ if (nameDoc)
198
+ docs.push(nameDoc);
199
+ const descDoc = buildBm25Document(`${principle.id}.description`, principle.description, pic);
200
+ if (descDoc)
201
+ docs.push(descDoc);
202
+ for (const sig of principle.measurableSignals ?? []) {
203
+ const doc = buildBm25Document(`${principle.id}.signal.${sig.id}`, `${sig.metric} ${sig.operator} ${sig.threshold}`, sig.identityClass ?? pic);
204
+ if (doc)
205
+ docs.push(doc);
206
+ }
207
+ for (const ap of principle.antiPatterns ?? []) {
208
+ const doc = buildBm25Document(`${principle.id}.antipattern.${ap.id}`, `${ap.label} ${ap.detectionPatterns.join(' ')}`, ap.identityClass ?? pic);
209
+ if (doc)
210
+ docs.push(doc);
211
+ }
212
+ return { documents: docs };
213
+ }
214
+ /**
215
+ * Enforce the §B.10.2 minimums a DID must meet before Phase-2b scoring
216
+ * is safe to enable. Returns a list of gaps; empty ⇒ ready.
217
+ */
218
+ export function validatePhase2bReadiness(compiled) {
219
+ const gaps = [];
220
+ // ≥ 2 constraints with ≥ 3 detection patterns
221
+ const qualifyingConstraints = compiled.constraintRules.filter((c) => c.detectionPatterns.length >= 3);
222
+ if (qualifyingConstraints.length < 2) {
223
+ gaps.push(`Need ≥2 constraints with ≥3 detection patterns each (got ${qualifyingConstraints.length})`);
224
+ }
225
+ // ≥ 3 outOfScope entries with ≥ 2 synonyms
226
+ const outOfScopeOk = compiled.scopeLists.outOfScope.filter((s) => s.synonyms.length >= 2);
227
+ if (outOfScopeOk.length < 3) {
228
+ gaps.push(`Need ≥3 outOfScope entries with ≥2 synonyms each (got ${outOfScopeOk.length})`);
229
+ }
230
+ // ≥ 3 product-level antiPatterns with ≥ 3 patterns
231
+ const productAntiOk = compiled.antiPatternLists.product.filter((a) => a.detectionPatterns.length >= 3);
232
+ if (productAntiOk.length < 3) {
233
+ gaps.push(`Need ≥3 product-level antiPatterns with ≥3 detection patterns each (got ${productAntiOk.length})`);
234
+ }
235
+ // ≥ 1 principle with ≥ 2 antiPatterns having ≥ 2 patterns each
236
+ const principlesWithAnti = Object.entries(compiled.antiPatternLists.perPrinciple).filter(([, list]) => list.filter((a) => a.detectionPatterns.length >= 2).length >= 2);
237
+ if (principlesWithAnti.length < 1) {
238
+ gaps.push('Need ≥1 designPrinciple with ≥2 antiPatterns (each with ≥2 detection patterns)');
239
+ }
240
+ // ≥ 2 voice antiPatterns with ≥ 2 patterns
241
+ const voiceOk = compiled.antiPatternLists.voice.filter((a) => a.detectionPatterns.length >= 2);
242
+ if (voiceOk.length < 2) {
243
+ gaps.push(`Need ≥2 voice antiPatterns with ≥2 detection patterns each (got ${voiceOk.length})`);
244
+ }
245
+ // ≥ 2 visual antiPatterns with ≥ 2 patterns
246
+ const visualOk = compiled.antiPatternLists.visual.filter((a) => a.detectionPatterns.length >= 2);
247
+ if (visualOk.length < 2) {
248
+ gaps.push(`Need ≥2 visual antiPatterns with ≥2 detection patterns each (got ${visualOk.length})`);
249
+ }
250
+ return { ready: gaps.length === 0, gaps };
251
+ }
252
+ // ── State-store serialization helpers ────────────────────────────────
253
+ export function serializeForStore(compiled) {
254
+ return {
255
+ didName: compiled.didName,
256
+ namespace: compiled.namespace,
257
+ sourceHash: compiled.sourceHash,
258
+ scopeListsJson: canonicalJson(compiled.scopeLists),
259
+ constraintRulesJson: canonicalJson(compiled.constraintRules),
260
+ antiPatternListsJson: canonicalJson(compiled.antiPatternLists),
261
+ measurableSignalsJson: canonicalJson(compiled.measurableSignals),
262
+ bm25CorpusBlob: Buffer.from(canonicalJson(compiled.bm25Corpus), 'utf-8'),
263
+ principleCorporaBlob: Buffer.from(canonicalJson(compiled.principleCorpora), 'utf-8'),
264
+ };
265
+ }
266
+ export function deserializeFromStore(record) {
267
+ const parse = (json, fallback) => json ? JSON.parse(json) : fallback;
268
+ const parseBuffer = (buf, fallback) => buf ? JSON.parse(buf.toString('utf-8')) : fallback;
269
+ return {
270
+ didName: record.didName,
271
+ namespace: record.namespace,
272
+ sourceHash: record.sourceHash,
273
+ scopeLists: parse(record.scopeListsJson, { inScope: [], outOfScope: [] }),
274
+ constraintRules: parse(record.constraintRulesJson, []),
275
+ antiPatternLists: parse(record.antiPatternListsJson, {
276
+ product: [],
277
+ perPrinciple: {},
278
+ voice: [],
279
+ visual: [],
280
+ }),
281
+ measurableSignals: parse(record.measurableSignalsJson, []),
282
+ bm25Corpus: parseBuffer(record.bm25CorpusBlob, { documents: [] }),
283
+ principleCorpora: parseBuffer(record.principleCorporaBlob, {}),
284
+ };
285
+ }
286
+ //# sourceMappingURL=did-compiler.js.map
@@ -0,0 +1,84 @@
1
+ /**
2
+ * SA Drift Monitor (RFC-0008 Addendum B §B.9.2).
3
+ *
4
+ * Periodically inspects `did_scoring_events` and triggers a
5
+ * `SoulDriftDetected` event when SA-1 or SA-2 rolling statistics
6
+ * indicate the scorer is drifting. Separately reports structural vs
7
+ * LLM means so operators can tell whether the drift is coming from
8
+ * the product (DID review needed) or the LLM layer (exemplar bank
9
+ * recalibration needed).
10
+ *
11
+ * Trigger (per dimension):
12
+ * mean < 0.4 OR stddev > 0.15 — for 3 consecutive windows
13
+ *
14
+ * Hysteresis: caller supplies `lastTriggerAt`; the monitor won't fire
15
+ * again within `recoveryMs` (default 7d) of the previous emission.
16
+ */
17
+ import type { DesignSystemBinding } from '@ai-sdlc/reference';
18
+ import type { StateStore } from '../state/store.js';
19
+ import type { DidScoringEventRecord, SaDimension } from '../state/types.js';
20
+ export declare const DEFAULT_MEAN_THRESHOLD = 0.4;
21
+ export declare const DEFAULT_STDDEV_THRESHOLD = 0.15;
22
+ /** Number of consecutive windows that must violate to trigger. */
23
+ export declare const DEFAULT_CONSECUTIVE_WINDOWS = 3;
24
+ export declare const DEFAULT_WINDOW_DAYS = 30;
25
+ export declare const DEFAULT_RECOVERY_MS: number;
26
+ export interface WindowStats {
27
+ count: number;
28
+ mean: number;
29
+ stddev: number;
30
+ structuralMean: number;
31
+ llmMean: number;
32
+ deterministicFlags: number;
33
+ /** Inclusive window range — [start, end) in ms. */
34
+ windowStartMs: number;
35
+ windowEndMs: number;
36
+ /** True if this window violated the drift threshold. */
37
+ violates: boolean;
38
+ }
39
+ export type DriftTrend = 'increasing' | 'decreasing' | 'stable';
40
+ export interface SoulDriftDetectedEvent {
41
+ type: 'SoulDriftDetected';
42
+ dimension: SaDimension;
43
+ rollingMean: number;
44
+ rollingStdDev: number;
45
+ sprintsInViolation: number;
46
+ trend: DriftTrend;
47
+ driftSource: {
48
+ deterministicFlags: number;
49
+ structuralScoreMean: number;
50
+ llmScoreMean: number;
51
+ note: string;
52
+ };
53
+ notifiedPrincipals: string[];
54
+ triggeredAt: string;
55
+ }
56
+ export declare function mean(values: readonly number[]): number;
57
+ export declare function stddev(values: readonly number[]): number;
58
+ export declare function computeWindowStats(events: readonly DidScoringEventRecord[], windowStartMs: number, windowEndMs: number, meanThreshold: number, stddevThreshold: number): WindowStats;
59
+ export declare function computeTrend(windows: readonly WindowStats[]): DriftTrend;
60
+ export declare function describeDriftSource(windows: readonly WindowStats[]): {
61
+ structuralScoreMean: number;
62
+ llmScoreMean: number;
63
+ note: string;
64
+ deterministicFlags: number;
65
+ };
66
+ export interface DriftDetectorDeps {
67
+ stateStore: StateStore;
68
+ /** Resolve the DSB that owns this DID (for notification principals). */
69
+ getBinding?: (dimension: SaDimension) => DesignSystemBinding | undefined;
70
+ /** Previous trigger timestamp (for hysteresis). */
71
+ getLastTriggerAt?: (dimension: SaDimension) => string | undefined;
72
+ /** Clock injection for tests. */
73
+ now?: () => number;
74
+ /** Configuration overrides. */
75
+ config?: Partial<{
76
+ meanThreshold: number;
77
+ stddevThreshold: number;
78
+ consecutiveWindows: number;
79
+ windowDays: number;
80
+ recoveryMs: number;
81
+ }>;
82
+ }
83
+ export declare function detectSoulDrift(dimension: SaDimension, deps: DriftDetectorDeps): SoulDriftDetectedEvent | undefined;
84
+ //# sourceMappingURL=drift-monitor.d.ts.map
@@ -0,0 +1,186 @@
1
+ /**
2
+ * SA Drift Monitor (RFC-0008 Addendum B §B.9.2).
3
+ *
4
+ * Periodically inspects `did_scoring_events` and triggers a
5
+ * `SoulDriftDetected` event when SA-1 or SA-2 rolling statistics
6
+ * indicate the scorer is drifting. Separately reports structural vs
7
+ * LLM means so operators can tell whether the drift is coming from
8
+ * the product (DID review needed) or the LLM layer (exemplar bank
9
+ * recalibration needed).
10
+ *
11
+ * Trigger (per dimension):
12
+ * mean < 0.4 OR stddev > 0.15 — for 3 consecutive windows
13
+ *
14
+ * Hysteresis: caller supplies `lastTriggerAt`; the monitor won't fire
15
+ * again within `recoveryMs` (default 7d) of the previous emission.
16
+ */
17
+ // ── Constants ───────────────────────────────────────────────────────
18
+ export const DEFAULT_MEAN_THRESHOLD = 0.4;
19
+ export const DEFAULT_STDDEV_THRESHOLD = 0.15;
20
+ /** Number of consecutive windows that must violate to trigger. */
21
+ export const DEFAULT_CONSECUTIVE_WINDOWS = 3;
22
+ export const DEFAULT_WINDOW_DAYS = 30;
23
+ export const DEFAULT_RECOVERY_MS = 7 * 24 * 60 * 60 * 1000;
24
+ // ── Pure helpers ────────────────────────────────────────────────────
25
+ export function mean(values) {
26
+ if (values.length === 0)
27
+ return 0;
28
+ return values.reduce((a, b) => a + b, 0) / values.length;
29
+ }
30
+ export function stddev(values) {
31
+ if (values.length === 0)
32
+ return 0;
33
+ const m = mean(values);
34
+ const variance = values.reduce((acc, v) => acc + (v - m) * (v - m), 0) / values.length;
35
+ return Math.sqrt(variance);
36
+ }
37
+ export function computeWindowStats(events, windowStartMs, windowEndMs, meanThreshold, stddevThreshold) {
38
+ const composites = [];
39
+ const structural = [];
40
+ const llm = [];
41
+ let deterministicFlags = 0;
42
+ for (const e of events) {
43
+ if (typeof e.compositeScore === 'number')
44
+ composites.push(e.compositeScore);
45
+ if (e.layer2ResultJson) {
46
+ try {
47
+ const parsed = JSON.parse(e.layer2ResultJson);
48
+ if (typeof parsed.score === 'number')
49
+ structural.push(parsed.score);
50
+ }
51
+ catch {
52
+ // ignore malformed entry
53
+ }
54
+ }
55
+ if (e.layer3ResultJson) {
56
+ try {
57
+ const parsed = JSON.parse(e.layer3ResultJson);
58
+ const candidate = parsed.domainIntent ?? parsed.principleAlignment;
59
+ if (typeof candidate === 'number')
60
+ llm.push(candidate);
61
+ }
62
+ catch {
63
+ // ignore malformed entry
64
+ }
65
+ }
66
+ if (e.layer1ResultJson) {
67
+ try {
68
+ const parsed = JSON.parse(e.layer1ResultJson);
69
+ if (parsed.hardGated)
70
+ deterministicFlags++;
71
+ }
72
+ catch {
73
+ // ignore malformed entry
74
+ }
75
+ }
76
+ }
77
+ const m = mean(composites);
78
+ const s = stddev(composites);
79
+ return {
80
+ count: composites.length,
81
+ mean: m,
82
+ stddev: s,
83
+ structuralMean: mean(structural),
84
+ llmMean: mean(llm),
85
+ deterministicFlags,
86
+ windowStartMs,
87
+ windowEndMs,
88
+ violates: composites.length > 0 && (m < meanThreshold || s > stddevThreshold),
89
+ };
90
+ }
91
+ export function computeTrend(windows) {
92
+ if (windows.length < 2)
93
+ return 'stable';
94
+ // Windows ordered newest-first; trend is the direction of mean over time.
95
+ const newestMean = windows[0].mean;
96
+ const oldestMean = windows[windows.length - 1].mean;
97
+ const delta = newestMean - oldestMean;
98
+ if (Math.abs(delta) < 0.05)
99
+ return 'stable';
100
+ return delta > 0 ? 'increasing' : 'decreasing';
101
+ }
102
+ export function describeDriftSource(windows) {
103
+ const structural = mean(windows.map((w) => w.structuralMean));
104
+ const llm = mean(windows.map((w) => w.llmMean));
105
+ const flags = windows.reduce((acc, w) => acc + w.deterministicFlags, 0);
106
+ let note;
107
+ if (llm < structural - 0.15)
108
+ note = 'LLM-layer drift (consider recalibrating exemplar bank)';
109
+ else if (structural < llm - 0.15)
110
+ note = 'Structural drift (review DID corpus + compile artifacts)';
111
+ else if (flags > 0)
112
+ note = `Mixed drift with ${flags} hard-gated events (review DID scope)`;
113
+ else
114
+ note = 'Uniform drift across layers (product review recommended)';
115
+ return {
116
+ structuralScoreMean: structural,
117
+ llmScoreMean: llm,
118
+ note,
119
+ deterministicFlags: flags,
120
+ };
121
+ }
122
+ export function detectSoulDrift(dimension, deps) {
123
+ const cfg = deps.config ?? {};
124
+ const meanThreshold = cfg.meanThreshold ?? DEFAULT_MEAN_THRESHOLD;
125
+ const stddevThreshold = cfg.stddevThreshold ?? DEFAULT_STDDEV_THRESHOLD;
126
+ const consecutive = cfg.consecutiveWindows ?? DEFAULT_CONSECUTIVE_WINDOWS;
127
+ const windowDays = cfg.windowDays ?? DEFAULT_WINDOW_DAYS;
128
+ const recoveryMs = cfg.recoveryMs ?? DEFAULT_RECOVERY_MS;
129
+ const nowMs = (deps.now ?? (() => Date.now()))();
130
+ // Hysteresis
131
+ const lastTriggerAt = deps.getLastTriggerAt?.(dimension);
132
+ if (lastTriggerAt) {
133
+ const lastMs = Date.parse(lastTriggerAt);
134
+ if (!Number.isNaN(lastMs) && nowMs - lastMs < recoveryMs)
135
+ return undefined;
136
+ }
137
+ const windowMs = windowDays * 24 * 60 * 60 * 1000;
138
+ const earliestMs = nowMs - consecutive * windowMs;
139
+ // Fetch events for the combined range.
140
+ const since = new Date(earliestMs).toISOString();
141
+ const events = deps.stateStore.getDidScoringEvents({
142
+ saDimension: dimension,
143
+ since,
144
+ limit: 10_000,
145
+ });
146
+ // Partition into consecutive windows (newest → oldest).
147
+ const windows = [];
148
+ for (let i = 0; i < consecutive; i++) {
149
+ const endMs = nowMs - i * windowMs;
150
+ const startMs = endMs - windowMs;
151
+ const inWindow = events.filter((e) => {
152
+ const t = e.createdAt ? Date.parse(e.createdAt) : NaN;
153
+ return !Number.isNaN(t) && t >= startMs && t < endMs;
154
+ });
155
+ windows.push(computeWindowStats(inWindow, startMs, endMs, meanThreshold, stddevThreshold));
156
+ }
157
+ const allViolate = windows.every((w) => w.violates);
158
+ if (!allViolate)
159
+ return undefined;
160
+ // All windows violated — fire the event.
161
+ const compositeMeans = windows.map((w) => w.mean);
162
+ const compositeStddevs = windows.map((w) => w.stddev);
163
+ const rollingMean = mean(compositeMeans);
164
+ const rollingStdDev = mean(compositeStddevs);
165
+ const trend = computeTrend(windows);
166
+ const driftSource = describeDriftSource(windows);
167
+ const binding = deps.getBinding?.(dimension);
168
+ const principals = binding ? collectPrincipals(binding) : [];
169
+ return {
170
+ type: 'SoulDriftDetected',
171
+ dimension,
172
+ rollingMean,
173
+ rollingStdDev,
174
+ sprintsInViolation: consecutive,
175
+ trend,
176
+ driftSource,
177
+ notifiedPrincipals: principals,
178
+ triggeredAt: new Date(nowMs).toISOString(),
179
+ };
180
+ }
181
+ function collectPrincipals(binding) {
182
+ const design = binding.spec.stewardship.designAuthority.principals;
183
+ const engineering = binding.spec.stewardship.engineeringAuthority.principals;
184
+ return Array.from(new Set([...design, ...engineering]));
185
+ }
186
+ //# sourceMappingURL=drift-monitor.js.map