@ai-sdlc/orchestrator 0.6.0 → 0.10.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 (209) hide show
  1. package/dist/__test-helpers/git-env.d.ts +40 -0
  2. package/dist/__test-helpers/git-env.js +73 -0
  3. package/dist/admission-composite.d.ts +89 -0
  4. package/dist/admission-composite.js +241 -0
  5. package/dist/admission-enrichment.d.ts +142 -0
  6. package/dist/admission-enrichment.js +341 -0
  7. package/dist/admission-hc.d.ts +81 -0
  8. package/dist/admission-hc.js +94 -0
  9. package/dist/admission-score.d.ts +109 -5
  10. package/dist/admission-score.js +90 -11
  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 +74 -0
  28. package/dist/cli/commands/git-remote.js +170 -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-features.d.ts +208 -0
  32. package/dist/cli/commands/init-features.js +473 -0
  33. package/dist/cli/commands/init-templates.d.ts +104 -0
  34. package/dist/cli/commands/init-templates.js +399 -0
  35. package/dist/cli/commands/init.d.ts +48 -0
  36. package/dist/cli/commands/init.js +322 -23
  37. package/dist/cli/commands/mcp-setup.d.ts +31 -0
  38. package/dist/cli/commands/mcp-setup.js +78 -8
  39. package/dist/cli/formatters/table.js +15 -2
  40. package/dist/cli/index.d.ts +14 -1
  41. package/dist/cli/index.js +81 -20
  42. package/dist/cli/versions.d.ts +57 -0
  43. package/dist/cli/versions.js +128 -0
  44. package/dist/code-area-classifier.d.ts +21 -0
  45. package/dist/code-area-classifier.js +48 -0
  46. package/dist/config.d.ts +33 -1
  47. package/dist/config.js +78 -8
  48. package/dist/database/adapters/external.d.ts +24 -0
  49. package/dist/database/adapters/external.js +80 -0
  50. package/dist/database/adapters/neon.d.ts +41 -0
  51. package/dist/database/adapters/neon.js +98 -0
  52. package/dist/database/adapters/pg-snapshot-restore.d.ts +28 -0
  53. package/dist/database/adapters/pg-snapshot-restore.js +68 -0
  54. package/dist/database/adapters/sqlite-copy.d.ts +32 -0
  55. package/dist/database/adapters/sqlite-copy.js +145 -0
  56. package/dist/database/connection-injection.d.ts +35 -0
  57. package/dist/database/connection-injection.js +93 -0
  58. package/dist/database/index.d.ts +12 -0
  59. package/dist/database/index.js +23 -0
  60. package/dist/database/registry.d.ts +13 -0
  61. package/dist/database/registry.js +27 -0
  62. package/dist/database/topology.d.ts +52 -0
  63. package/dist/database/topology.js +44 -0
  64. package/dist/database/types.d.ts +89 -0
  65. package/dist/database/types.js +26 -0
  66. package/dist/design-authority.d.ts +53 -0
  67. package/dist/design-authority.js +84 -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 +188 -15
  92. package/dist/fix-ci.js +8 -1
  93. package/dist/fix-review.js +8 -1
  94. package/dist/harness/adapters/claude-code.d.ts +29 -0
  95. package/dist/harness/adapters/claude-code.js +191 -0
  96. package/dist/harness/adapters/codex.d.ts +25 -0
  97. package/dist/harness/adapters/codex.js +61 -0
  98. package/dist/harness/independence.d.ts +51 -0
  99. package/dist/harness/independence.js +67 -0
  100. package/dist/harness/index.d.ts +14 -0
  101. package/dist/harness/index.js +20 -0
  102. package/dist/harness/registry.d.ts +17 -0
  103. package/dist/harness/registry.js +31 -0
  104. package/dist/harness/types.d.ts +123 -0
  105. package/dist/harness/types.js +8 -0
  106. package/dist/harness/version-probe.d.ts +14 -0
  107. package/dist/harness/version-probe.js +123 -0
  108. package/dist/index.d.ts +30 -4
  109. package/dist/index.js +35 -2
  110. package/dist/models/classifier.d.ts +78 -0
  111. package/dist/models/classifier.js +277 -0
  112. package/dist/models/index.d.ts +3 -0
  113. package/dist/models/index.js +3 -0
  114. package/dist/models/registry.d.ts +97 -0
  115. package/dist/models/registry.js +173 -0
  116. package/dist/pillar-breakdown.d.ts +85 -0
  117. package/dist/pillar-breakdown.js +162 -0
  118. package/dist/priority.d.ts +5 -0
  119. package/dist/priority.js +18 -5
  120. package/dist/review-meta.d.ts +65 -0
  121. package/dist/review-meta.js +149 -0
  122. package/dist/review.d.ts +4 -0
  123. package/dist/review.js +12 -2
  124. package/dist/runners/claude-code-sdk.d.ts +31 -0
  125. package/dist/runners/claude-code-sdk.js +238 -0
  126. package/dist/runners/claude-code.d.ts +3 -0
  127. package/dist/runners/claude-code.js +30 -66
  128. package/dist/runners/codex.js +4 -1
  129. package/dist/runners/copilot.js +4 -1
  130. package/dist/runners/cursor.js +4 -1
  131. package/dist/runners/git-utils.d.ts +81 -0
  132. package/dist/runners/git-utils.js +201 -0
  133. package/dist/runners/index.d.ts +3 -0
  134. package/dist/runners/index.js +3 -0
  135. package/dist/runners/review-agent.d.ts +20 -0
  136. package/dist/runners/review-agent.js +171 -41
  137. package/dist/runners/runner-registry.js +10 -0
  138. package/dist/runners/sdk-review-runner.d.ts +65 -0
  139. package/dist/runners/sdk-review-runner.js +185 -0
  140. package/dist/runners/security-triage.d.ts +20 -4
  141. package/dist/runners/security-triage.js +39 -15
  142. package/dist/runners/types.d.ts +6 -0
  143. package/dist/runtime/attestations.d.ts +766 -0
  144. package/dist/runtime/attestations.js +1195 -0
  145. package/dist/runtime/git-env.d.ts +53 -0
  146. package/dist/runtime/git-env.js +60 -0
  147. package/dist/runtime/index.d.ts +7 -0
  148. package/dist/runtime/index.js +7 -0
  149. package/dist/runtime/parallelism-flag.d.ts +28 -0
  150. package/dist/runtime/parallelism-flag.js +39 -0
  151. package/dist/runtime/port-allocator.d.ts +32 -0
  152. package/dist/runtime/port-allocator.js +96 -0
  153. package/dist/runtime/worktree-pool.d.ts +86 -0
  154. package/dist/runtime/worktree-pool.js +204 -0
  155. package/dist/runtime/worktree.d.ts +25 -0
  156. package/dist/runtime/worktree.js +111 -0
  157. package/dist/sa-scoring/auto-calibrate.d.ts +69 -0
  158. package/dist/sa-scoring/auto-calibrate.js +107 -0
  159. package/dist/sa-scoring/c1-sa2-computable.d.ts +42 -0
  160. package/dist/sa-scoring/c1-sa2-computable.js +59 -0
  161. package/dist/sa-scoring/composite.d.ts +107 -0
  162. package/dist/sa-scoring/composite.js +139 -0
  163. package/dist/sa-scoring/depparse-client.d.ts +79 -0
  164. package/dist/sa-scoring/depparse-client.js +187 -0
  165. package/dist/sa-scoring/did-compiler.d.ts +122 -0
  166. package/dist/sa-scoring/did-compiler.js +286 -0
  167. package/dist/sa-scoring/drift-monitor.d.ts +84 -0
  168. package/dist/sa-scoring/drift-monitor.js +186 -0
  169. package/dist/sa-scoring/exemplar-bank.d.ts +78 -0
  170. package/dist/sa-scoring/exemplar-bank.js +154 -0
  171. package/dist/sa-scoring/feedback-store.d.ts +100 -0
  172. package/dist/sa-scoring/feedback-store.js +156 -0
  173. package/dist/sa-scoring/index.d.ts +71 -0
  174. package/dist/sa-scoring/index.js +158 -0
  175. package/dist/sa-scoring/layer1-deterministic.d.ts +115 -0
  176. package/dist/sa-scoring/layer1-deterministic.js +298 -0
  177. package/dist/sa-scoring/layer2-structural.d.ts +71 -0
  178. package/dist/sa-scoring/layer2-structural.js +151 -0
  179. package/dist/sa-scoring/layer3-llm.d.ts +86 -0
  180. package/dist/sa-scoring/layer3-llm.js +282 -0
  181. package/dist/sa-scoring/rescore-orchestrator.d.ts +52 -0
  182. package/dist/sa-scoring/rescore-orchestrator.js +47 -0
  183. package/dist/scheduling/burn-down.d.ts +27 -0
  184. package/dist/scheduling/burn-down.js +43 -0
  185. package/dist/scheduling/calibration.d.ts +42 -0
  186. package/dist/scheduling/calibration.js +150 -0
  187. package/dist/scheduling/index.d.ts +8 -0
  188. package/dist/scheduling/index.js +8 -0
  189. package/dist/scheduling/ledger.d.ts +59 -0
  190. package/dist/scheduling/ledger.js +216 -0
  191. package/dist/scheduling/off-peak.d.ts +27 -0
  192. package/dist/scheduling/off-peak.js +112 -0
  193. package/dist/scheduling/schedule-decision.d.ts +39 -0
  194. package/dist/scheduling/schedule-decision.js +89 -0
  195. package/dist/scheduling/tier-analysis.d.ts +47 -0
  196. package/dist/scheduling/tier-analysis.js +81 -0
  197. package/dist/scheduling/types.d.ts +140 -0
  198. package/dist/scheduling/types.js +11 -0
  199. package/dist/shared.d.ts +13 -0
  200. package/dist/shared.js +32 -0
  201. package/dist/state/schema.d.ts +5 -1
  202. package/dist/state/schema.js +223 -1
  203. package/dist/state/store.d.ts +55 -1
  204. package/dist/state/store.js +342 -7
  205. package/dist/state/types.d.ts +139 -0
  206. package/dist/types.d.ts +1 -1
  207. package/dist/validate-agent-output.js +4 -1
  208. package/dist/watch.js +6 -0
  209. package/package.json +3 -2
@@ -0,0 +1,79 @@
1
+ /**
2
+ * TypeScript client for the `sidecar-depparse` HTTP service.
3
+ *
4
+ * Used by Layer 1 deterministic SA scoring (RFC-0008 §B.4.2) to detect
5
+ * requirement constructions (`requires X`, `must have X`, `X required`,
6
+ * passive + negation-aware) in issue text.
7
+ *
8
+ * The client is injectable — production code wires in `HttpDepparseClient`
9
+ * pointing at the sidecar URL, tests inject a `FakeDepparseClient` to
10
+ * avoid the Python dependency.
11
+ *
12
+ * Retry semantics: one retry on 5xx responses (network or transient),
13
+ * typed failure on anything else. Fail-soft by default — caller can
14
+ * treat a `DepparseUnavailable` failure as "no matches" when the
15
+ * sidecar is optional.
16
+ */
17
+ export interface DepparseMatch {
18
+ pattern: string;
19
+ matchedText: string;
20
+ depPath: string[];
21
+ construction: string;
22
+ }
23
+ export interface DepparseMatchRequest {
24
+ text: string;
25
+ patterns: string[];
26
+ }
27
+ export interface DepparseMatchResponse {
28
+ matches: DepparseMatch[];
29
+ }
30
+ export interface DepparseHealth {
31
+ status: string;
32
+ model?: string;
33
+ modelLoaded: boolean;
34
+ }
35
+ export interface DepparseClient {
36
+ match(request: DepparseMatchRequest): Promise<DepparseMatchResponse>;
37
+ healthz(): Promise<DepparseHealth>;
38
+ }
39
+ export type DepparseErrorKind = 'network' | 'bad-request' | 'server-error' | 'model-unavailable' | 'timeout';
40
+ export declare class DepparseError extends Error {
41
+ readonly kind: DepparseErrorKind;
42
+ readonly status?: number;
43
+ constructor(kind: DepparseErrorKind, message: string, status?: number);
44
+ }
45
+ export interface HttpDepparseClientOptions {
46
+ baseUrl: string;
47
+ /** Per-request timeout in ms. Default 5 000. */
48
+ timeoutMs?: number;
49
+ /** Number of retries on 5xx / network errors. Default 1. */
50
+ retries?: number;
51
+ /** Injectable fetch — lets tests stub without overriding globalThis. */
52
+ fetchImpl?: typeof fetch;
53
+ }
54
+ export declare class HttpDepparseClient implements DepparseClient {
55
+ private readonly baseUrl;
56
+ private readonly timeoutMs;
57
+ private readonly retries;
58
+ private readonly fetchImpl;
59
+ constructor(opts: HttpDepparseClientOptions);
60
+ healthz(): Promise<DepparseHealth>;
61
+ match(request: DepparseMatchRequest): Promise<DepparseMatchResponse>;
62
+ private requestJson;
63
+ private doFetch;
64
+ }
65
+ /**
66
+ * In-memory test double that matches patterns by exact substring —
67
+ * sufficient for unit tests that don't care about dependency-parse
68
+ * semantics. Real scoring tests use recorded fixtures.
69
+ */
70
+ export declare class FakeDepparseClient implements DepparseClient {
71
+ private responses;
72
+ private healthResponse;
73
+ callLog: DepparseMatchRequest[];
74
+ setResponse(response: DepparseMatchResponse): void;
75
+ setHealth(health: DepparseHealth): void;
76
+ match(request: DepparseMatchRequest): Promise<DepparseMatchResponse>;
77
+ healthz(): Promise<DepparseHealth>;
78
+ }
79
+ //# sourceMappingURL=depparse-client.d.ts.map
@@ -0,0 +1,187 @@
1
+ /**
2
+ * TypeScript client for the `sidecar-depparse` HTTP service.
3
+ *
4
+ * Used by Layer 1 deterministic SA scoring (RFC-0008 §B.4.2) to detect
5
+ * requirement constructions (`requires X`, `must have X`, `X required`,
6
+ * passive + negation-aware) in issue text.
7
+ *
8
+ * The client is injectable — production code wires in `HttpDepparseClient`
9
+ * pointing at the sidecar URL, tests inject a `FakeDepparseClient` to
10
+ * avoid the Python dependency.
11
+ *
12
+ * Retry semantics: one retry on 5xx responses (network or transient),
13
+ * typed failure on anything else. Fail-soft by default — caller can
14
+ * treat a `DepparseUnavailable` failure as "no matches" when the
15
+ * sidecar is optional.
16
+ */
17
+ export class DepparseError extends Error {
18
+ kind;
19
+ status;
20
+ constructor(kind, message, status) {
21
+ super(message);
22
+ this.name = 'DepparseError';
23
+ this.kind = kind;
24
+ this.status = status;
25
+ }
26
+ }
27
+ export class HttpDepparseClient {
28
+ baseUrl;
29
+ timeoutMs;
30
+ retries;
31
+ fetchImpl;
32
+ constructor(opts) {
33
+ this.baseUrl = assertHttpUrl(opts.baseUrl).replace(/\/$/, '');
34
+ this.timeoutMs = opts.timeoutMs ?? 5_000;
35
+ this.retries = opts.retries ?? 1;
36
+ this.fetchImpl = opts.fetchImpl ?? fetch;
37
+ }
38
+ async healthz() {
39
+ const res = await this.requestJson('GET', '/healthz');
40
+ return {
41
+ status: res.status,
42
+ model: res.model ?? undefined,
43
+ modelLoaded: Boolean(res.model_loaded),
44
+ };
45
+ }
46
+ async match(request) {
47
+ const body = JSON.stringify(request);
48
+ const res = await this.requestJson('POST', '/v1/match', body);
49
+ return {
50
+ matches: res.matches.map((m) => ({
51
+ pattern: m.pattern,
52
+ matchedText: m.matched_text,
53
+ depPath: m.dep_path,
54
+ construction: m.construction,
55
+ })),
56
+ };
57
+ }
58
+ // ── Internals ──────────────────────────────────────────────────
59
+ async requestJson(method, path, body) {
60
+ const url = `${this.baseUrl}${path}`;
61
+ let lastError;
62
+ const attempts = this.retries + 1;
63
+ for (let attempt = 0; attempt < attempts; attempt++) {
64
+ try {
65
+ const res = await this.doFetch(url, method, body);
66
+ if (res.ok) {
67
+ return (await res.json());
68
+ }
69
+ if (res.status === 503) {
70
+ // Model not yet loaded — permanent for this request window.
71
+ const text = await safeText(res);
72
+ throw new DepparseError('model-unavailable', text || `503 at ${url}`, 503);
73
+ }
74
+ if (res.status >= 500) {
75
+ lastError = new DepparseError('server-error', `${res.status} at ${url}`, res.status);
76
+ continue; // retry
77
+ }
78
+ const text = await safeText(res);
79
+ throw new DepparseError('bad-request', text || `${res.status} at ${url}`, res.status);
80
+ }
81
+ catch (err) {
82
+ if (err instanceof DepparseError) {
83
+ if (err.kind === 'server-error' && attempt + 1 < attempts) {
84
+ lastError = err;
85
+ continue;
86
+ }
87
+ throw err;
88
+ }
89
+ // Network / abort / unknown — retry once
90
+ lastError = new DepparseError(errIsAbort(err) ? 'timeout' : 'network', err instanceof Error ? err.message : String(err));
91
+ if (attempt + 1 >= attempts)
92
+ throw lastError;
93
+ }
94
+ }
95
+ throw lastError ?? new DepparseError('network', `No response from ${url}`);
96
+ }
97
+ async doFetch(url, method, body) {
98
+ const controller = new AbortController();
99
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
100
+ try {
101
+ return await this.fetchImpl(url, {
102
+ method,
103
+ headers: body ? { 'content-type': 'application/json' } : undefined,
104
+ body,
105
+ signal: controller.signal,
106
+ });
107
+ }
108
+ finally {
109
+ clearTimeout(timer);
110
+ }
111
+ }
112
+ }
113
+ async function safeText(res) {
114
+ try {
115
+ return await res.text();
116
+ }
117
+ catch {
118
+ return '';
119
+ }
120
+ }
121
+ /**
122
+ * Refuses non-HTTP schemes. Env-var or CLI-supplied `baseUrl` with
123
+ * `file://`, `data:`, `javascript:`, or similar could trigger local
124
+ * file reads or arbitrary protocol handlers when passed to `fetch()`.
125
+ */
126
+ function assertHttpUrl(url) {
127
+ let parsed;
128
+ try {
129
+ parsed = new URL(url);
130
+ }
131
+ catch {
132
+ throw new DepparseError('bad-request', `depparse baseUrl is not a valid URL: ${url}`);
133
+ }
134
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
135
+ throw new DepparseError('bad-request', `depparse baseUrl must use http: or https: (got ${parsed.protocol})`);
136
+ }
137
+ return url;
138
+ }
139
+ function errIsAbort(err) {
140
+ return (typeof err === 'object' &&
141
+ err !== null &&
142
+ 'name' in err &&
143
+ err.name === 'AbortError');
144
+ }
145
+ // ── Fake client for tests ────────────────────────────────────────────
146
+ /**
147
+ * In-memory test double that matches patterns by exact substring —
148
+ * sufficient for unit tests that don't care about dependency-parse
149
+ * semantics. Real scoring tests use recorded fixtures.
150
+ */
151
+ export class FakeDepparseClient {
152
+ responses;
153
+ healthResponse = {
154
+ status: 'ok',
155
+ model: 'fake',
156
+ modelLoaded: true,
157
+ };
158
+ callLog = [];
159
+ setResponse(response) {
160
+ this.responses = response;
161
+ }
162
+ setHealth(health) {
163
+ this.healthResponse = health;
164
+ }
165
+ async match(request) {
166
+ this.callLog.push(request);
167
+ if (this.responses)
168
+ return this.responses;
169
+ // Fallback: return substring matches with no dep_path info.
170
+ const matches = [];
171
+ for (const pattern of request.patterns) {
172
+ if (request.text.toLowerCase().includes(pattern.toLowerCase())) {
173
+ matches.push({
174
+ pattern,
175
+ matchedText: pattern,
176
+ depPath: [],
177
+ construction: 'substring',
178
+ });
179
+ }
180
+ }
181
+ return { matches };
182
+ }
183
+ async healthz() {
184
+ return this.healthResponse;
185
+ }
186
+ }
187
+ //# sourceMappingURL=depparse-client.js.map
@@ -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