@ai-sdlc/orchestrator 0.10.0 → 0.14.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 (168) hide show
  1. package/dist/adapters.d.ts +42 -3
  2. package/dist/adapters.js +133 -3
  3. package/dist/admission-composite.d.ts +112 -1
  4. package/dist/admission-composite.js +85 -4
  5. package/dist/admission-enrichment.d.ts +12 -3
  6. package/dist/admission-enrichment.js +64 -13
  7. package/dist/admission-score.d.ts +30 -0
  8. package/dist/admission-score.js +4 -1
  9. package/dist/analysis/file-walker.js +5 -0
  10. package/dist/backlog-adapter.d.ts +45 -0
  11. package/dist/backlog-adapter.js +145 -1
  12. package/dist/calibration.d.ts +81 -0
  13. package/dist/calibration.js +76 -0
  14. package/dist/cli/commands/git-remote.js +1 -1
  15. package/dist/cli/commands/init-features.d.ts +301 -2
  16. package/dist/cli/commands/init-features.js +647 -7
  17. package/dist/cli/commands/init-templates.d.ts +198 -1
  18. package/dist/cli/commands/init-templates.js +943 -1
  19. package/dist/cli/commands/init.d.ts +45 -0
  20. package/dist/cli/commands/init.js +147 -5
  21. package/dist/cli/commands/run.js +9 -1
  22. package/dist/cli/index.d.ts +11 -0
  23. package/dist/cli/index.js +42 -8
  24. package/dist/compliance/composer.d.ts +79 -0
  25. package/dist/compliance/composer.js +258 -0
  26. package/dist/compliance/errors.d.ts +64 -0
  27. package/dist/compliance/errors.js +85 -0
  28. package/dist/compliance/loader.d.ts +52 -0
  29. package/dist/compliance/loader.js +124 -0
  30. package/dist/compliance/types.d.ts +184 -0
  31. package/dist/compliance/types.js +41 -0
  32. package/dist/compliance-clearance.d.ts +269 -0
  33. package/dist/compliance-clearance.js +269 -0
  34. package/dist/config.js +17 -0
  35. package/dist/cost-tracker.d.ts +22 -0
  36. package/dist/cost-tracker.js +41 -0
  37. package/dist/cycle-utils.js +14 -1
  38. package/dist/database/adapters/external.js +5 -1
  39. package/dist/embedding/adapters/openai-text-embedding-3-small.d.ts +71 -0
  40. package/dist/embedding/adapters/openai-text-embedding-3-small.js +190 -0
  41. package/dist/embedding/consumers/tessellation-drift.d.ts +74 -0
  42. package/dist/embedding/consumers/tessellation-drift.js +76 -0
  43. package/dist/embedding/cross-provider.d.ts +78 -0
  44. package/dist/embedding/cross-provider.js +75 -0
  45. package/dist/embedding/deprecation.d.ts +151 -0
  46. package/dist/embedding/deprecation.js +229 -0
  47. package/dist/embedding/errors.d.ts +90 -0
  48. package/dist/embedding/errors.js +150 -0
  49. package/dist/embedding/index.d.ts +29 -0
  50. package/dist/embedding/index.js +24 -0
  51. package/dist/embedding/pipeline-load.d.ts +146 -0
  52. package/dist/embedding/pipeline-load.js +178 -0
  53. package/dist/embedding/registry.d.ts +45 -0
  54. package/dist/embedding/registry.js +61 -0
  55. package/dist/embedding/stale-vector.d.ts +110 -0
  56. package/dist/embedding/stale-vector.js +92 -0
  57. package/dist/embedding/storage/index.d.ts +51 -0
  58. package/dist/embedding/storage/index.js +43 -0
  59. package/dist/embedding/storage/jsonl-backend.d.ts +150 -0
  60. package/dist/embedding/storage/jsonl-backend.js +332 -0
  61. package/dist/embedding/storage/types.d.ts +135 -0
  62. package/dist/embedding/storage/types.js +13 -0
  63. package/dist/embedding/types.d.ts +180 -0
  64. package/dist/embedding/types.js +10 -0
  65. package/dist/execute.d.ts +29 -2
  66. package/dist/execute.js +200 -38
  67. package/dist/fix-review.js +1 -1
  68. package/dist/index.d.ts +14 -2
  69. package/dist/index.js +19 -2
  70. package/dist/journey/inheritance-validator.d.ts +396 -0
  71. package/dist/journey/inheritance-validator.js +370 -0
  72. package/dist/journey/metric-snapshot.d.ts +403 -0
  73. package/dist/journey/metric-snapshot.js +370 -0
  74. package/dist/journey/state-id-drift-rule.d.ts +137 -0
  75. package/dist/journey/state-id-drift-rule.js +245 -0
  76. package/dist/journey-sa2-router.d.ts +395 -0
  77. package/dist/journey-sa2-router.js +308 -0
  78. package/dist/otel-exporter.js +0 -3
  79. package/dist/runners/claude-code.js +3 -3
  80. package/dist/runners/review-agent.js +6 -2
  81. package/dist/runners/runner-registry.d.ts +36 -0
  82. package/dist/runners/runner-registry.js +90 -0
  83. package/dist/runtime/attestations.d.ts +173 -13
  84. package/dist/runtime/attestations.js +265 -43
  85. package/dist/runtime/index.d.ts +1 -1
  86. package/dist/runtime/index.js +1 -1
  87. package/dist/sa-scoring/exemplar-bank.js +3 -1
  88. package/dist/sa-scoring/layer3-llm.js +6 -1
  89. package/dist/sa-scoring/revision-proposal-config.d.ts +178 -0
  90. package/dist/sa-scoring/revision-proposal-config.js +198 -0
  91. package/dist/sa-scoring/revision-proposal.d.ts +285 -0
  92. package/dist/sa-scoring/revision-proposal.js +417 -0
  93. package/dist/shared.d.ts +28 -0
  94. package/dist/shared.js +37 -0
  95. package/dist/signal-ingestion/adapters/community-thread.d.ts +43 -0
  96. package/dist/signal-ingestion/adapters/community-thread.js +55 -0
  97. package/dist/signal-ingestion/adapters/in-app-feedback.d.ts +67 -0
  98. package/dist/signal-ingestion/adapters/in-app-feedback.js +51 -0
  99. package/dist/signal-ingestion/adapters/manual.d.ts +78 -0
  100. package/dist/signal-ingestion/adapters/manual.js +112 -0
  101. package/dist/signal-ingestion/adapters/support-ticket.d.ts +47 -0
  102. package/dist/signal-ingestion/adapters/support-ticket.js +51 -0
  103. package/dist/signal-ingestion/classifier.d.ts +205 -0
  104. package/dist/signal-ingestion/classifier.js +494 -0
  105. package/dist/signal-ingestion/clustering-types.d.ts +36 -0
  106. package/dist/signal-ingestion/clustering-types.js +14 -0
  107. package/dist/signal-ingestion/clustering.d.ts +200 -0
  108. package/dist/signal-ingestion/clustering.js +413 -0
  109. package/dist/signal-ingestion/config.d.ts +351 -0
  110. package/dist/signal-ingestion/config.js +587 -0
  111. package/dist/signal-ingestion/d1.d.ts +252 -0
  112. package/dist/signal-ingestion/d1.js +235 -0
  113. package/dist/signal-ingestion/errors.d.ts +73 -0
  114. package/dist/signal-ingestion/errors.js +108 -0
  115. package/dist/signal-ingestion/governance-events.d.ts +181 -0
  116. package/dist/signal-ingestion/governance-events.js +189 -0
  117. package/dist/signal-ingestion/index.d.ts +35 -0
  118. package/dist/signal-ingestion/index.js +53 -0
  119. package/dist/signal-ingestion/manual-share-metric.d.ts +93 -0
  120. package/dist/signal-ingestion/manual-share-metric.js +106 -0
  121. package/dist/signal-ingestion/registry.d.ts +40 -0
  122. package/dist/signal-ingestion/registry.js +137 -0
  123. package/dist/signal-ingestion/residency.d.ts +227 -0
  124. package/dist/signal-ingestion/residency.js +238 -0
  125. package/dist/signal-ingestion/significance.d.ts +554 -0
  126. package/dist/signal-ingestion/significance.js +555 -0
  127. package/dist/signal-ingestion/types.d.ts +191 -0
  128. package/dist/signal-ingestion/types.js +8 -0
  129. package/dist/substrate/drift-composition.d.ts +270 -0
  130. package/dist/substrate/drift-composition.js +306 -0
  131. package/dist/substrate/drift-tui-surface.d.ts +61 -0
  132. package/dist/substrate/drift-tui-surface.js +102 -0
  133. package/dist/substrate/identity-class.d.ts +176 -0
  134. package/dist/substrate/identity-class.js +201 -0
  135. package/dist/tessellation/cross-soul-provenance-rule.d.ts +133 -0
  136. package/dist/tessellation/cross-soul-provenance-rule.js +171 -0
  137. package/dist/tessellation/inter-soul-embedding-distance-rule.d.ts +61 -0
  138. package/dist/tessellation/inter-soul-embedding-distance-rule.js +67 -0
  139. package/dist/tessellation/rule-registry.d.ts +269 -0
  140. package/dist/tessellation/rule-registry.js +92 -0
  141. package/dist/tessellation/soul-slug-ast-scan-rule.d.ts +90 -0
  142. package/dist/tessellation/soul-slug-ast-scan-rule.js +158 -0
  143. package/dist/tessellation-admission.d.ts +162 -0
  144. package/dist/tessellation-admission.js +146 -0
  145. package/dist/tessellation-drift.d.ts +246 -0
  146. package/dist/tessellation-drift.js +250 -0
  147. package/dist/validate-config.js +13 -0
  148. package/dist/validate-issue.js +2 -2
  149. package/dist/variant/cardinality-activation.d.ts +126 -0
  150. package/dist/variant/cardinality-activation.js +101 -0
  151. package/dist/variant/deprecation-lifecycle.d.ts +184 -0
  152. package/dist/variant/deprecation-lifecycle.js +208 -0
  153. package/dist/variant/drift-extension.d.ts +136 -0
  154. package/dist/variant/drift-extension.js +164 -0
  155. package/dist/variant/engineering-review.d.ts +185 -0
  156. package/dist/variant/engineering-review.js +142 -0
  157. package/dist/variant/index.d.ts +32 -0
  158. package/dist/variant/index.js +32 -0
  159. package/dist/variant/inheritance-validator.d.ts +165 -0
  160. package/dist/variant/inheritance-validator.js +139 -0
  161. package/dist/variant/internal-adopter/index.d.ts +11 -0
  162. package/dist/variant/internal-adopter/index.js +10 -0
  163. package/dist/variant/internal-adopter/products.d.ts +156 -0
  164. package/dist/variant/internal-adopter/products.js +366 -0
  165. package/dist/variant-admission.d.ts +316 -0
  166. package/dist/variant-admission.js +247 -0
  167. package/dist/webhook-manager.js +7 -1
  168. package/package.json +15 -12
@@ -0,0 +1,190 @@
1
+ /**
2
+ * Default embedding adapter: openai-text-embedding-3-small per RFC-0019 §7.
3
+ *
4
+ * Model: text-embedding-3-small (OpenAI, snapshot 2024-01-25)
5
+ * Dimensions: 1536
6
+ * Max input: 8191 tokens
7
+ * Batch: up to 2048 inputs per call
8
+ * Billing: pay-per-token at ~$0.02 / 1M tokens
9
+ *
10
+ * Why text-embedding-3-small over -large:
11
+ * At $0.02/1M tokens, a 10K-token corpus re-embed costs ~$0.0002. The -large
12
+ * variant is 6.5x more expensive for marginal quality improvement on the
13
+ * short-text drift detection use case (RFC-0009 OQ-6). Adopters with
14
+ * quality-sensitive use cases MAY register the -large variant.
15
+ *
16
+ * Why snapshot 2024-01-25:
17
+ * Most recent stable snapshot as of RFC-0019 authoring. Pinning the snapshot
18
+ * date makes adapter upgrades a code change (visible in PR review) rather
19
+ * than a silent provider-side rollout. OpenAI has silently changed
20
+ * text-embedding-ada-002 behavior in the past — explicit pinning prevents that.
21
+ */
22
+ import { createHash } from 'node:crypto';
23
+ import { EmbeddingProviderError, EmbeddingDimensionMismatch } from '../errors.js';
24
+ /** Cost rate for text-embedding-3-small as of 2024-01-25. */
25
+ const COST_PER_TOKEN_USD = 0.02 / 1_000_000; // $0.02 per 1M tokens
26
+ /** Maximum inputs per batch call (OpenAI API limit). */
27
+ const MAX_BATCH_SIZE = 2048;
28
+ export class OpenAITextEmbedding3Small {
29
+ name = 'openai-text-embedding-3-small';
30
+ modelId = 'text-embedding-3-small';
31
+ modelVersion = '2024-01-25';
32
+ dimensions = 1536;
33
+ capabilities = {
34
+ dimensions: 1536,
35
+ maxInputTokens: 8191,
36
+ supportsBatching: true,
37
+ selfHosted: false,
38
+ billingModel: 'pay-per-token',
39
+ approxCostPer1MTokens: 0.02,
40
+ };
41
+ requires = {
42
+ envVar: 'OPENAI_API_KEY',
43
+ };
44
+ /** Optional cost-tracking callback. Set by the orchestrator after adapter instantiation. */
45
+ costCallback;
46
+ constructor(costCallback) {
47
+ this.costCallback = costCallback;
48
+ }
49
+ /**
50
+ * Wire a cost-tracking callback after construction.
51
+ * Called by the orchestrator when it has a CostTracker available.
52
+ */
53
+ setCostCallback(callback) {
54
+ this.costCallback = callback;
55
+ }
56
+ async isAvailable() {
57
+ if (!process.env.OPENAI_API_KEY) {
58
+ return {
59
+ available: false,
60
+ reason: 'env-var-missing',
61
+ detail: 'OPENAI_API_KEY not set; openai-text-embedding-3-small requires it.',
62
+ };
63
+ }
64
+ return { available: true };
65
+ }
66
+ async getAccountId() {
67
+ const key = process.env.OPENAI_API_KEY;
68
+ if (!key)
69
+ return null;
70
+ // One-way derivation: SHA-256 of '<adapter-name>:<api-key>'.
71
+ // MUST NOT leak the credential.
72
+ return createHash('sha256').update(`${this.name}:${key}`).digest('hex');
73
+ }
74
+ /**
75
+ * Embed a single text string.
76
+ *
77
+ * @param text - Source text. MUST be non-empty.
78
+ * @param consumerLabel - Cost attribution label (default: 'unspecified').
79
+ * Examples: 'rfc-0009-tessellation-drift', 'rfc-0008-ppa-similarity'.
80
+ */
81
+ async embed(text, consumerLabel = 'unspecified') {
82
+ if (!text || text.trim().length === 0) {
83
+ throw new EmbeddingProviderError(this.name, 'embed(): empty input rejected — pass non-empty text.');
84
+ }
85
+ const apiKey = process.env.OPENAI_API_KEY;
86
+ if (!apiKey) {
87
+ throw new EmbeddingProviderError(this.name, 'OPENAI_API_KEY is not set. Call isAvailable() before embed().');
88
+ }
89
+ const response = await fetch('https://api.openai.com/v1/embeddings', {
90
+ method: 'POST',
91
+ headers: {
92
+ Authorization: `Bearer ${apiKey}`,
93
+ 'Content-Type': 'application/json',
94
+ },
95
+ body: JSON.stringify({
96
+ model: this.modelId,
97
+ input: text,
98
+ encoding_format: 'float',
99
+ }),
100
+ });
101
+ if (!response.ok) {
102
+ const body = await response.text().catch(() => '(unreadable)');
103
+ throw new EmbeddingProviderError(this.name, `OpenAI /v1/embeddings returned HTTP ${response.status}: ${body}`);
104
+ }
105
+ const data = (await response.json());
106
+ const vector = data.data[0]?.embedding;
107
+ if (!vector) {
108
+ throw new EmbeddingProviderError(this.name, 'OpenAI /v1/embeddings response contained no embedding in data[0].');
109
+ }
110
+ if (vector.length !== this.dimensions) {
111
+ throw new EmbeddingDimensionMismatch(this.name, this.dimensions, vector.length);
112
+ }
113
+ await this._recordCost(data.usage.total_tokens, consumerLabel);
114
+ return vector;
115
+ }
116
+ /**
117
+ * Embed a batch of texts.
118
+ * OpenAI accepts up to 2048 inputs per call; this method chunks above that.
119
+ * Input order is preserved in the returned array.
120
+ *
121
+ * @param texts - Array of source texts. Each MUST be non-empty.
122
+ * @param consumerLabel - Cost attribution label (applies to all texts in batch).
123
+ */
124
+ async embedBatch(texts, consumerLabel = 'unspecified') {
125
+ if (texts.length === 0) {
126
+ return [];
127
+ }
128
+ for (const t of texts) {
129
+ if (!t || t.trim().length === 0) {
130
+ throw new EmbeddingProviderError(this.name, 'embedBatch(): empty string in input array rejected — all texts must be non-empty.');
131
+ }
132
+ }
133
+ const apiKey = process.env.OPENAI_API_KEY;
134
+ if (!apiKey) {
135
+ throw new EmbeddingProviderError(this.name, 'OPENAI_API_KEY is not set. Call isAvailable() before embedBatch().');
136
+ }
137
+ const results = [];
138
+ // Chunk into MAX_BATCH_SIZE slices to respect the OpenAI API limit.
139
+ for (let i = 0; i < texts.length; i += MAX_BATCH_SIZE) {
140
+ const chunk = texts.slice(i, i + MAX_BATCH_SIZE);
141
+ const response = await fetch('https://api.openai.com/v1/embeddings', {
142
+ method: 'POST',
143
+ headers: {
144
+ Authorization: `Bearer ${apiKey}`,
145
+ 'Content-Type': 'application/json',
146
+ },
147
+ body: JSON.stringify({
148
+ model: this.modelId,
149
+ input: chunk,
150
+ encoding_format: 'float',
151
+ }),
152
+ });
153
+ if (!response.ok) {
154
+ const body = await response.text().catch(() => '(unreadable)');
155
+ throw new EmbeddingProviderError(this.name, `OpenAI /v1/embeddings returned HTTP ${response.status} on batch chunk [${i}, ${i + chunk.length}): ${body}`);
156
+ }
157
+ const data = (await response.json());
158
+ // OpenAI returns data sorted by index — preserve input order.
159
+ const sorted = [...data.data].sort((a, b) => a.index - b.index);
160
+ for (const item of sorted) {
161
+ if (item.embedding.length !== this.dimensions) {
162
+ throw new EmbeddingDimensionMismatch(this.name, this.dimensions, item.embedding.length);
163
+ }
164
+ results.push(item.embedding);
165
+ }
166
+ await this._recordCost(data.usage.total_tokens, consumerLabel);
167
+ }
168
+ return results;
169
+ }
170
+ /**
171
+ * Record a cost event via the cost callback.
172
+ * No-op when no callback is wired (e.g., in unit tests without a CostTracker).
173
+ */
174
+ async _recordCost(tokens, consumerLabel) {
175
+ if (!this.costCallback)
176
+ return;
177
+ const accountId = await this.getAccountId();
178
+ const costRecord = {
179
+ provider: this.name,
180
+ modelVersion: this.modelVersion,
181
+ accountId,
182
+ consumerLabel,
183
+ tokens,
184
+ costUsd: tokens * COST_PER_TOKEN_USD,
185
+ billingModel: 'pay-per-token',
186
+ };
187
+ this.costCallback(costRecord);
188
+ }
189
+ }
190
+ //# sourceMappingURL=openai-text-embedding-3-small.js.map
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Spec-level wiring for the `Eτ_tessellation_drift` rule consumer per
3
+ * RFC-0009 Phase 4.2 / RFC-0019 Phase 4 (AISDLC-340).
4
+ *
5
+ * Runtime usage activates when RFC-0009 Phase 4.2 ships (AISDLC-317). The
6
+ * code here is the load-bearing API stub the runtime hooks call:
7
+ *
8
+ * - `TESSELLATION_DRIFT_CONSUMER_LABEL` is the canonical `consumerLabel`
9
+ * passed to `adapter.embed()` so cost-tracker records the
10
+ * drift-attribution dimension per OQ-6 re-walkthrough.
11
+ *
12
+ * - `TESSELLATION_DRIFT_STALE_VECTOR_POLICY` is the API-site override
13
+ * per OQ-2 re-walkthrough: drift reads historical trajectory across
14
+ * successive document revisions; lazy-re-embed is actively destructive
15
+ * of that signal (silently overwrites old vectors → loses time-series
16
+ * fidelity). The consumer pins `'fail-loud'` regardless of org default
17
+ * to preserve historical-trajectory fidelity.
18
+ *
19
+ * - `embedDriftSignal()` is the consumer-facing helper that wraps the
20
+ * adapter's embed() call with the consumer label baked in. RFC-0009's
21
+ * drift-computation pipeline calls THIS function, never adapter.embed()
22
+ * directly, so the consumerLabel is impossible to forget at the call site.
23
+ *
24
+ * Tests: `tessellation-drift.test.ts`
25
+ */
26
+ import type { EmbeddingAdapter } from '../types.js';
27
+ import type { EmbeddingStaleVectorPolicy } from '../storage/types.js';
28
+ /**
29
+ * Canonical consumer label for cost-tracker attribution. Per OQ-6
30
+ * re-walkthrough, callers pass this string so cost-tracker records the
31
+ * `(provider, modelVersion, accountId, consumerLabel='rfc-0009-tessellation-drift')`
32
+ * dimension and finance can answer "drift cost vs PPA cost" without
33
+ * re-instrumentation.
34
+ */
35
+ export declare const TESSELLATION_DRIFT_CONSUMER_LABEL = "rfc-0009-tessellation-drift";
36
+ /**
37
+ * API-site stale-vector policy override per OQ-2 re-walkthrough. The drift
38
+ * consumer pins `fail-loud` regardless of org default — silent re-embedding
39
+ * destroys the historical-trajectory signal the rule is measuring.
40
+ *
41
+ * Treat this constant as load-bearing: every drift-signal read site MUST
42
+ * read THIS value, not the org default, when deciding policy.
43
+ */
44
+ export declare const TESSELLATION_DRIFT_STALE_VECTOR_POLICY: EmbeddingStaleVectorPolicy;
45
+ /**
46
+ * Embed a single source text for the Eτ_tessellation_drift rule.
47
+ *
48
+ * RFC-0009's drift-computation pipeline (AISDLC-317, pending) calls THIS
49
+ * helper rather than `adapter.embed()` directly. The helper bakes in the
50
+ * consumer label so cost attribution is correct by construction.
51
+ *
52
+ * The adapter is passed in (not resolved here) because the orchestrator
53
+ * may have a fallback chain — the caller (pipeline runtime) decides which
54
+ * adapter to use; the consumer decides the LABEL and POLICY.
55
+ *
56
+ * @param adapter - Resolved embedding adapter (from `loadEmbeddingFromPipelineSpec`).
57
+ * @param text - Source text to embed (a tessellation shard, typically).
58
+ * @returns - Vector emitted by the adapter, length === adapter.dimensions.
59
+ */
60
+ export declare function embedDriftSignal(adapter: EmbeddingAdapter, text: string): Promise<number[]>;
61
+ /**
62
+ * Consumer descriptor exported for cross-cutting docs/registry purposes.
63
+ * Other consumers (PPA similarity, classifier embeddings, etc.) SHOULD
64
+ * export a similar descriptor so a future `cli-embedding-consumers list`
65
+ * can enumerate every consumer + its pinned policy.
66
+ */
67
+ export declare const TESSELLATION_DRIFT_CONSUMER: {
68
+ readonly label: "rfc-0009-tessellation-drift";
69
+ readonly staleVectorPolicy: "fail-loud";
70
+ readonly rationale: string;
71
+ readonly rfc: "RFC-0009";
72
+ readonly task: "AISDLC-340";
73
+ };
74
+ //# sourceMappingURL=tessellation-drift.d.ts.map
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Spec-level wiring for the `Eτ_tessellation_drift` rule consumer per
3
+ * RFC-0009 Phase 4.2 / RFC-0019 Phase 4 (AISDLC-340).
4
+ *
5
+ * Runtime usage activates when RFC-0009 Phase 4.2 ships (AISDLC-317). The
6
+ * code here is the load-bearing API stub the runtime hooks call:
7
+ *
8
+ * - `TESSELLATION_DRIFT_CONSUMER_LABEL` is the canonical `consumerLabel`
9
+ * passed to `adapter.embed()` so cost-tracker records the
10
+ * drift-attribution dimension per OQ-6 re-walkthrough.
11
+ *
12
+ * - `TESSELLATION_DRIFT_STALE_VECTOR_POLICY` is the API-site override
13
+ * per OQ-2 re-walkthrough: drift reads historical trajectory across
14
+ * successive document revisions; lazy-re-embed is actively destructive
15
+ * of that signal (silently overwrites old vectors → loses time-series
16
+ * fidelity). The consumer pins `'fail-loud'` regardless of org default
17
+ * to preserve historical-trajectory fidelity.
18
+ *
19
+ * - `embedDriftSignal()` is the consumer-facing helper that wraps the
20
+ * adapter's embed() call with the consumer label baked in. RFC-0009's
21
+ * drift-computation pipeline calls THIS function, never adapter.embed()
22
+ * directly, so the consumerLabel is impossible to forget at the call site.
23
+ *
24
+ * Tests: `tessellation-drift.test.ts`
25
+ */
26
+ /**
27
+ * Canonical consumer label for cost-tracker attribution. Per OQ-6
28
+ * re-walkthrough, callers pass this string so cost-tracker records the
29
+ * `(provider, modelVersion, accountId, consumerLabel='rfc-0009-tessellation-drift')`
30
+ * dimension and finance can answer "drift cost vs PPA cost" without
31
+ * re-instrumentation.
32
+ */
33
+ export const TESSELLATION_DRIFT_CONSUMER_LABEL = 'rfc-0009-tessellation-drift';
34
+ /**
35
+ * API-site stale-vector policy override per OQ-2 re-walkthrough. The drift
36
+ * consumer pins `fail-loud` regardless of org default — silent re-embedding
37
+ * destroys the historical-trajectory signal the rule is measuring.
38
+ *
39
+ * Treat this constant as load-bearing: every drift-signal read site MUST
40
+ * read THIS value, not the org default, when deciding policy.
41
+ */
42
+ export const TESSELLATION_DRIFT_STALE_VECTOR_POLICY = 'fail-loud';
43
+ /**
44
+ * Embed a single source text for the Eτ_tessellation_drift rule.
45
+ *
46
+ * RFC-0009's drift-computation pipeline (AISDLC-317, pending) calls THIS
47
+ * helper rather than `adapter.embed()` directly. The helper bakes in the
48
+ * consumer label so cost attribution is correct by construction.
49
+ *
50
+ * The adapter is passed in (not resolved here) because the orchestrator
51
+ * may have a fallback chain — the caller (pipeline runtime) decides which
52
+ * adapter to use; the consumer decides the LABEL and POLICY.
53
+ *
54
+ * @param adapter - Resolved embedding adapter (from `loadEmbeddingFromPipelineSpec`).
55
+ * @param text - Source text to embed (a tessellation shard, typically).
56
+ * @returns - Vector emitted by the adapter, length === adapter.dimensions.
57
+ */
58
+ export async function embedDriftSignal(adapter, text) {
59
+ return adapter.embed(text, TESSELLATION_DRIFT_CONSUMER_LABEL);
60
+ }
61
+ /**
62
+ * Consumer descriptor exported for cross-cutting docs/registry purposes.
63
+ * Other consumers (PPA similarity, classifier embeddings, etc.) SHOULD
64
+ * export a similar descriptor so a future `cli-embedding-consumers list`
65
+ * can enumerate every consumer + its pinned policy.
66
+ */
67
+ export const TESSELLATION_DRIFT_CONSUMER = {
68
+ label: TESSELLATION_DRIFT_CONSUMER_LABEL,
69
+ staleVectorPolicy: TESSELLATION_DRIFT_STALE_VECTOR_POLICY,
70
+ rationale: 'Eτ drift reads historical trajectory across successive document revisions; ' +
71
+ 'lazy-re-embed silently overwrites old vectors and destroys the time-series signal. ' +
72
+ 'Pin fail-loud at the API site (RFC-0019 OQ-2 re-walkthrough).',
73
+ rfc: 'RFC-0009',
74
+ task: 'AISDLC-340',
75
+ };
76
+ //# sourceMappingURL=tessellation-drift.js.map
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Cross-provider compatibility per RFC-0019 §9.3 + OQ-3 re-walkthrough split.
3
+ *
4
+ * The OQ-3 re-walkthrough resolution surfaces a critical distinction that
5
+ * v0.2 missed: cross-PROVIDER comparisons (openai vs cohere) are NEVER valid
6
+ * because the math is genuinely undefined — vectors in different embedding
7
+ * spaces have no metrically-valid distance. cross-VERSION-within-provider
8
+ * comparisons (3-small@2024-01-25 vs 3-small@2025-01-25) DELEGATE to the
9
+ * stale-vector policy because closely-correlated embedding spaces support
10
+ * lazy re-embed.
11
+ *
12
+ * Policy mapping:
13
+ *
14
+ * stored.provider != current.provider → ALWAYS REFUSE + emit
15
+ * `Decision: cross-provider-comparison-attempted`
16
+ * → auto-action: emit cli-embedding-bump
17
+ * migration task
18
+ *
19
+ * stored.provider == current.provider
20
+ * AND
21
+ * stored.modelVersion != current.modelVersion → DELEGATE to staleVectorPolicy
22
+ *
23
+ * stored matches current entirely → COMPATIBLE (no policy)
24
+ *
25
+ * This resolves v0.2's logical conflict where both cases were lumped under
26
+ * "strict no-op" contradicting OQ-2's lazy-re-embed default.
27
+ *
28
+ * @module embedding/cross-provider
29
+ */
30
+ import { EmbeddingError } from './errors.js';
31
+ /**
32
+ * The three possible outcomes of a provider-compatibility check.
33
+ *
34
+ * - `compatible` — same provider AND same model version. Caller
35
+ * may compare vectors directly.
36
+ * - `cross-version` — same provider, different model version.
37
+ * Caller MUST consult the stale-vector policy.
38
+ * - `cross-provider` — different provider. Caller MUST refuse the
39
+ * comparison and emit a catalog Decision +
40
+ * migration task.
41
+ */
42
+ export type ProviderCompatibility = 'compatible' | 'cross-version' | 'cross-provider';
43
+ /**
44
+ * Compare a stored entry's provenance against the currently configured adapter.
45
+ * Pure function — no side effects, no event emission. Callers wrap the result
46
+ * with their own catalog logging.
47
+ */
48
+ export declare function checkProviderCompatibility(storedProvider: string, storedModelVersion: string, currentProvider: string, currentModelVersion: string): ProviderCompatibility;
49
+ /**
50
+ * Thrown when a cross-PROVIDER comparison is attempted (e.g., openai vs cohere).
51
+ * Always fatal — the math is undefined and there is no auto-migration path.
52
+ * Operators must run cli-embedding-bump to re-embed the entire corpus on the
53
+ * new provider.
54
+ */
55
+ export declare class CrossProviderComparisonError extends EmbeddingError {
56
+ readonly storedProvider: string;
57
+ readonly currentProvider: string;
58
+ readonly textHash?: string | undefined;
59
+ constructor(storedProvider: string, currentProvider: string, textHash?: string | undefined);
60
+ }
61
+ /**
62
+ * Build the catalog Decision payload for a cross-provider attempt per
63
+ * RFC-0019 §9.3 OQ-3 re-walkthrough. The caller forwards this to
64
+ * `appendDecisionEvent()` so a `Decision: cross-provider-comparison-attempted`
65
+ * lands in the catalog and the operator gets a migration task.
66
+ */
67
+ export interface CrossProviderDecisionPayload {
68
+ /** Stable summary used as the catalog Decision summary. */
69
+ summary: string;
70
+ /** Suggested migration command. */
71
+ migrationCommand: string;
72
+ /** Severity — always high for cross-provider. */
73
+ severity: 'high';
74
+ /** Auto-action — always emit a cli-embedding-bump migration task. */
75
+ autoAction: 'emit-migration-task';
76
+ }
77
+ export declare function buildCrossProviderDecisionPayload(storedProvider: string, currentProvider: string): CrossProviderDecisionPayload;
78
+ //# sourceMappingURL=cross-provider.d.ts.map
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Cross-provider compatibility per RFC-0019 §9.3 + OQ-3 re-walkthrough split.
3
+ *
4
+ * The OQ-3 re-walkthrough resolution surfaces a critical distinction that
5
+ * v0.2 missed: cross-PROVIDER comparisons (openai vs cohere) are NEVER valid
6
+ * because the math is genuinely undefined — vectors in different embedding
7
+ * spaces have no metrically-valid distance. cross-VERSION-within-provider
8
+ * comparisons (3-small@2024-01-25 vs 3-small@2025-01-25) DELEGATE to the
9
+ * stale-vector policy because closely-correlated embedding spaces support
10
+ * lazy re-embed.
11
+ *
12
+ * Policy mapping:
13
+ *
14
+ * stored.provider != current.provider → ALWAYS REFUSE + emit
15
+ * `Decision: cross-provider-comparison-attempted`
16
+ * → auto-action: emit cli-embedding-bump
17
+ * migration task
18
+ *
19
+ * stored.provider == current.provider
20
+ * AND
21
+ * stored.modelVersion != current.modelVersion → DELEGATE to staleVectorPolicy
22
+ *
23
+ * stored matches current entirely → COMPATIBLE (no policy)
24
+ *
25
+ * This resolves v0.2's logical conflict where both cases were lumped under
26
+ * "strict no-op" contradicting OQ-2's lazy-re-embed default.
27
+ *
28
+ * @module embedding/cross-provider
29
+ */
30
+ import { EmbeddingError } from './errors.js';
31
+ /**
32
+ * Compare a stored entry's provenance against the currently configured adapter.
33
+ * Pure function — no side effects, no event emission. Callers wrap the result
34
+ * with their own catalog logging.
35
+ */
36
+ export function checkProviderCompatibility(storedProvider, storedModelVersion, currentProvider, currentModelVersion) {
37
+ if (storedProvider !== currentProvider) {
38
+ return 'cross-provider';
39
+ }
40
+ if (storedModelVersion !== currentModelVersion) {
41
+ return 'cross-version';
42
+ }
43
+ return 'compatible';
44
+ }
45
+ /**
46
+ * Thrown when a cross-PROVIDER comparison is attempted (e.g., openai vs cohere).
47
+ * Always fatal — the math is undefined and there is no auto-migration path.
48
+ * Operators must run cli-embedding-bump to re-embed the entire corpus on the
49
+ * new provider.
50
+ */
51
+ export class CrossProviderComparisonError extends EmbeddingError {
52
+ storedProvider;
53
+ currentProvider;
54
+ textHash;
55
+ constructor(storedProvider, currentProvider, textHash) {
56
+ super(`Cross-provider comparison refused: stored vector is from '${storedProvider}', ` +
57
+ `current adapter is '${currentProvider}'. Embedding spaces are not metrically ` +
58
+ `comparable across providers. Migrate via: cli-embedding-bump --to ${currentProvider}` +
59
+ (textHash ? ` (offending textHash: ${textHash})` : ''));
60
+ this.storedProvider = storedProvider;
61
+ this.currentProvider = currentProvider;
62
+ this.textHash = textHash;
63
+ this.name = 'CrossProviderComparisonError';
64
+ }
65
+ }
66
+ export function buildCrossProviderDecisionPayload(storedProvider, currentProvider) {
67
+ return {
68
+ summary: `Cross-provider comparison attempted: '${storedProvider}' vs '${currentProvider}'. ` +
69
+ `Refused (math undefined). Run cli-embedding-bump to migrate.`,
70
+ migrationCommand: `cli-embedding-bump --to ${currentProvider}`,
71
+ severity: 'high',
72
+ autoAction: 'emit-migration-task',
73
+ };
74
+ }
75
+ //# sourceMappingURL=cross-provider.js.map
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Deprecation lifecycle per RFC-0019 §9.1 + OQ-4 re-walkthrough.
3
+ *
4
+ * Three-layer grace-period precedence (highest → lowest):
5
+ * 1. per-org `gracePeriodDays` from `.ai-sdlc/embedding-config.yaml`
6
+ * 2. adapter-declared `defaultGracePeriodDays` (capability matrix)
7
+ * 3. framework default — 90 days
8
+ *
9
+ * Catalog dedup via per-Decision-key counter prevents Decision flood under
10
+ * orchestrator-driven loads: emit `Decision: embedding-provider-deprecated`
11
+ * at MILESTONES (89/60/30/7/1 days before deprecatedAt), NOT per-load. The
12
+ * dedup key is `embedding-provider-deprecated:<adapter-name>:<deprecatedAt>`.
13
+ *
14
+ * Lifecycle phases:
15
+ * - Pre-warning : today < (deprecatedAt - gracePeriod) → silent
16
+ * - Warning : today ∈ [deprecatedAt - gracePeriod, deprecatedAt) → milestone events
17
+ * - Deprecated : today ∈ [deprecatedAt, removedAt) → continued warnings; HIGH in strict mode
18
+ * - Removed : today ≥ removedAt → emit `Decision: embedding-provider-removed`
19
+ * + auto-action: emit cli-embedding-bump migration task.
20
+ * Pipeline NEVER halts — downstream consumers degrade.
21
+ *
22
+ * @module embedding/deprecation
23
+ */
24
+ /**
25
+ * Framework default grace-period length per OQ-4 re-walkthrough.
26
+ * Conservative within OpenAI's typical 12-15 month deprecation window.
27
+ */
28
+ export declare const FRAMEWORK_DEFAULT_GRACE_PERIOD_DAYS = 90;
29
+ /**
30
+ * Days-before-deprecatedAt at which a catalog Decision is emitted.
31
+ * Dedup counter ensures one event per milestone per (adapter, deprecatedAt) tuple.
32
+ *
33
+ * Sorted DESCENDING — when computing "next-due milestone" we walk from
34
+ * largest to smallest and pick the first one we have crossed.
35
+ */
36
+ export declare const DEPRECATION_MILESTONE_DAYS: ReadonlyArray<number>;
37
+ /**
38
+ * Inputs to the deprecation lifecycle evaluator.
39
+ * `today` defaults to the current date; callers pass an explicit Date in tests.
40
+ */
41
+ export interface DeprecationLifecycleInput {
42
+ /** Canonical adapter name (e.g., 'openai-text-embedding-ada-002'). */
43
+ adapterName: string;
44
+ /** ISO date when the deprecation warning period starts (adapter.deprecatedAt). */
45
+ deprecatedAt?: string;
46
+ /** ISO date when the adapter is removed (adapter.removedAt). */
47
+ removedAt?: string;
48
+ /** Canonical replacement alias for migration messaging. */
49
+ replacementAlias?: string;
50
+ /** Adapter-declared default grace period override. */
51
+ adapterDefaultGracePeriodDays?: number;
52
+ /** Per-org override from embedding-config.yaml. */
53
+ orgGracePeriodDays?: number;
54
+ /** Whether the operator runs in strict mode (escalate severity at deprecatedAt). */
55
+ strictModeAtDeprecatedAt?: boolean;
56
+ /** Override "today" for deterministic tests. */
57
+ today?: Date;
58
+ }
59
+ /**
60
+ * Lifecycle phase the adapter is currently in.
61
+ */
62
+ export type DeprecationPhase = 'pre-warning' | 'warning' | 'deprecated' | 'removed' | 'inactive';
63
+ /**
64
+ * One catalog Decision event the caller should append.
65
+ */
66
+ export interface DeprecationDecisionEvent {
67
+ /** RFC-0035 catalog Decision type. */
68
+ decisionType: 'embedding-provider-deprecated' | 'embedding-provider-removed';
69
+ /** Dedup key — caller MUST refuse duplicate emissions for the same key. */
70
+ dedupKey: string;
71
+ /** Catalog severity. */
72
+ severity: 'info' | 'high';
73
+ /** Human-readable summary used as the Decision summary. */
74
+ summary: string;
75
+ /** Milestone reached, in days-before-deprecatedAt. Null for `removed` events. */
76
+ milestoneDaysBefore: number | null;
77
+ /**
78
+ * Auto-action the orchestrator MUST perform when this event lands.
79
+ * `null` for milestone events that don't auto-trigger anything.
80
+ */
81
+ autoAction: 'emit-migration-task' | null;
82
+ }
83
+ /**
84
+ * Result of evaluating the deprecation lifecycle for one adapter at one point
85
+ * in time.
86
+ */
87
+ export interface DeprecationLifecycleResult {
88
+ /** Current phase. */
89
+ phase: DeprecationPhase;
90
+ /** Effective grace period used (after three-layer precedence). */
91
+ effectiveGracePeriodDays: number;
92
+ /** Days from today to deprecatedAt (negative if past). Null when undeclared. */
93
+ daysToDeprecatedAt: number | null;
94
+ /** Days from today to removedAt (negative if past). Null when undeclared. */
95
+ daysToRemovedAt: number | null;
96
+ /**
97
+ * Decision events the caller should emit. May be empty (phase == pre-warning
98
+ * OR no milestone was crossed in the current load).
99
+ *
100
+ * The caller is responsible for catalog dedup — DO NOT emit when the dedup
101
+ * key has already been seen. See {@link DeprecationDecisionEvent.dedupKey}.
102
+ */
103
+ decisionEvents: DeprecationDecisionEvent[];
104
+ }
105
+ /**
106
+ * Resolve the effective grace period using the three-layer precedence chain
107
+ * per OQ-4 re-walkthrough.
108
+ */
109
+ export declare function resolveGracePeriodDays(orgOverride: number | undefined, adapterDefault: number | undefined): number;
110
+ /**
111
+ * Find the most-recently-crossed milestone at the current moment — i.e., the
112
+ * SMALLEST milestone the caller is at-or-under. Returns `null` when no
113
+ * milestone is due (today is BEFORE the largest milestone OR adapter has no
114
+ * deprecatedAt).
115
+ *
116
+ * "Crossed" = `daysToDeprecatedAt <= milestone`. We want the smallest such
117
+ * milestone so each emission represents a NEW threshold crossed:
118
+ *
119
+ * 90 days out → no milestone due
120
+ * 89 days out → milestone 89 (largest threshold first crossed)
121
+ * 75 days out → milestone 89 (still inside 89 window, 60 not yet crossed)
122
+ * 60 days out → milestone 60 (newly crossed)
123
+ * 50 days out → milestone 60 (still inside 60, 30 not yet crossed)
124
+ * 30 days out → milestone 30
125
+ * ...
126
+ *
127
+ * The dedup counter then collapses repeated emissions at the same milestone
128
+ * to exactly one event per (adapter, deprecatedAt, milestone) tuple.
129
+ *
130
+ * @param daysToDeprecatedAt - Result from `daysBetween(today, deprecatedAt)`.
131
+ * Positive when deprecatedAt is in the future, negative when past.
132
+ */
133
+ export declare function nextDueMilestone(daysToDeprecatedAt: number): number | null;
134
+ /**
135
+ * Build the catalog dedup key for a deprecation event at a given milestone.
136
+ * Same key across pipeline loads → caller refuses to emit a second time.
137
+ */
138
+ export declare function buildDedupKey(decisionType: DeprecationDecisionEvent['decisionType'], adapterName: string, deprecatedAt: string | undefined, milestoneDaysBefore: number | null): string;
139
+ /**
140
+ * Evaluate the deprecation lifecycle for one adapter.
141
+ *
142
+ * Returns the phase, resolved grace period, and any catalog Decision events
143
+ * the caller should emit. Pipeline-load NEVER halts on the result; downstream
144
+ * consumers degrade gracefully per the RFC-0035 G0 non-blocking contract.
145
+ *
146
+ * Dedup contract: the returned `decisionEvents` always carry the dedup key.
147
+ * The caller MUST consult its dedup store before appending — re-emitting the
148
+ * same `(adapter, deprecatedAt, milestone)` triple is a bug.
149
+ */
150
+ export declare function evaluateDeprecationLifecycle(input: DeprecationLifecycleInput): DeprecationLifecycleResult;
151
+ //# sourceMappingURL=deprecation.d.ts.map