@ai-sdlc/orchestrator 0.9.0 → 0.13.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 (172) 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/adapters.d.ts +42 -3
  4. package/dist/adapters.js +133 -3
  5. package/dist/admission-composite.d.ts +134 -1
  6. package/dist/admission-composite.js +170 -6
  7. package/dist/admission-enrichment.d.ts +12 -3
  8. package/dist/admission-enrichment.js +71 -10
  9. package/dist/admission-hc.d.ts +20 -1
  10. package/dist/admission-hc.js +12 -1
  11. package/dist/admission-score.d.ts +44 -0
  12. package/dist/admission-score.js +11 -2
  13. package/dist/backlog-adapter.d.ts +45 -0
  14. package/dist/backlog-adapter.js +145 -1
  15. package/dist/calibration.d.ts +81 -0
  16. package/dist/calibration.js +76 -0
  17. package/dist/cli/commands/git-remote.d.ts +25 -0
  18. package/dist/cli/commands/git-remote.js +80 -1
  19. package/dist/cli/commands/init-features.d.ts +507 -0
  20. package/dist/cli/commands/init-features.js +1100 -0
  21. package/dist/cli/commands/init-templates.d.ts +301 -0
  22. package/dist/cli/commands/init-templates.js +1341 -0
  23. package/dist/cli/commands/init.d.ts +67 -0
  24. package/dist/cli/commands/init.js +301 -2
  25. package/dist/cli/commands/run.js +9 -1
  26. package/dist/cli/index.d.ts +11 -0
  27. package/dist/cli/index.js +42 -8
  28. package/dist/compliance/composer.d.ts +79 -0
  29. package/dist/compliance/composer.js +258 -0
  30. package/dist/compliance/errors.d.ts +64 -0
  31. package/dist/compliance/errors.js +85 -0
  32. package/dist/compliance/loader.d.ts +52 -0
  33. package/dist/compliance/loader.js +124 -0
  34. package/dist/compliance/types.d.ts +184 -0
  35. package/dist/compliance/types.js +41 -0
  36. package/dist/compliance-clearance.d.ts +269 -0
  37. package/dist/compliance-clearance.js +269 -0
  38. package/dist/config.js +17 -0
  39. package/dist/cost-tracker.d.ts +22 -0
  40. package/dist/cost-tracker.js +41 -0
  41. package/dist/database/adapters/external.js +5 -1
  42. package/dist/design-authority.d.ts +13 -0
  43. package/dist/design-authority.js +13 -0
  44. package/dist/embedding/adapters/openai-text-embedding-3-small.d.ts +71 -0
  45. package/dist/embedding/adapters/openai-text-embedding-3-small.js +190 -0
  46. package/dist/embedding/consumers/tessellation-drift.d.ts +74 -0
  47. package/dist/embedding/consumers/tessellation-drift.js +76 -0
  48. package/dist/embedding/cross-provider.d.ts +78 -0
  49. package/dist/embedding/cross-provider.js +75 -0
  50. package/dist/embedding/deprecation.d.ts +151 -0
  51. package/dist/embedding/deprecation.js +229 -0
  52. package/dist/embedding/errors.d.ts +90 -0
  53. package/dist/embedding/errors.js +150 -0
  54. package/dist/embedding/index.d.ts +29 -0
  55. package/dist/embedding/index.js +24 -0
  56. package/dist/embedding/pipeline-load.d.ts +146 -0
  57. package/dist/embedding/pipeline-load.js +178 -0
  58. package/dist/embedding/registry.d.ts +45 -0
  59. package/dist/embedding/registry.js +61 -0
  60. package/dist/embedding/stale-vector.d.ts +110 -0
  61. package/dist/embedding/stale-vector.js +92 -0
  62. package/dist/embedding/storage/index.d.ts +51 -0
  63. package/dist/embedding/storage/index.js +43 -0
  64. package/dist/embedding/storage/jsonl-backend.d.ts +150 -0
  65. package/dist/embedding/storage/jsonl-backend.js +332 -0
  66. package/dist/embedding/storage/types.d.ts +135 -0
  67. package/dist/embedding/storage/types.js +13 -0
  68. package/dist/embedding/types.d.ts +180 -0
  69. package/dist/embedding/types.js +10 -0
  70. package/dist/execute.d.ts +29 -2
  71. package/dist/execute.js +179 -35
  72. package/dist/index.d.ts +14 -2
  73. package/dist/index.js +19 -2
  74. package/dist/journey/inheritance-validator.d.ts +396 -0
  75. package/dist/journey/inheritance-validator.js +370 -0
  76. package/dist/journey/state-id-drift-rule.d.ts +137 -0
  77. package/dist/journey/state-id-drift-rule.js +245 -0
  78. package/dist/journey-sa2-router.d.ts +395 -0
  79. package/dist/journey-sa2-router.js +308 -0
  80. package/dist/models/classifier.d.ts +3 -1
  81. package/dist/models/classifier.js +61 -5
  82. package/dist/pillar-breakdown.d.ts +23 -1
  83. package/dist/pillar-breakdown.js +9 -0
  84. package/dist/runners/review-agent.js +6 -2
  85. package/dist/runners/runner-registry.d.ts +36 -0
  86. package/dist/runners/runner-registry.js +90 -0
  87. package/dist/runtime/attestations.d.ts +663 -11
  88. package/dist/runtime/attestations.js +971 -24
  89. package/dist/runtime/index.d.ts +1 -1
  90. package/dist/runtime/index.js +1 -1
  91. package/dist/runtime/parallelism-flag.d.ts +21 -3
  92. package/dist/runtime/parallelism-flag.js +27 -6
  93. package/dist/runtime/port-allocator.d.ts +11 -0
  94. package/dist/runtime/port-allocator.js +30 -0
  95. package/dist/sa-scoring/layer3-llm.js +6 -1
  96. package/dist/sa-scoring/revision-proposal-config.d.ts +178 -0
  97. package/dist/sa-scoring/revision-proposal-config.js +198 -0
  98. package/dist/sa-scoring/revision-proposal.d.ts +285 -0
  99. package/dist/sa-scoring/revision-proposal.js +417 -0
  100. package/dist/signal-ingestion/adapters/community-thread.d.ts +43 -0
  101. package/dist/signal-ingestion/adapters/community-thread.js +55 -0
  102. package/dist/signal-ingestion/adapters/in-app-feedback.d.ts +67 -0
  103. package/dist/signal-ingestion/adapters/in-app-feedback.js +51 -0
  104. package/dist/signal-ingestion/adapters/manual.d.ts +78 -0
  105. package/dist/signal-ingestion/adapters/manual.js +112 -0
  106. package/dist/signal-ingestion/adapters/support-ticket.d.ts +47 -0
  107. package/dist/signal-ingestion/adapters/support-ticket.js +51 -0
  108. package/dist/signal-ingestion/classifier.d.ts +205 -0
  109. package/dist/signal-ingestion/classifier.js +494 -0
  110. package/dist/signal-ingestion/clustering-types.d.ts +36 -0
  111. package/dist/signal-ingestion/clustering-types.js +14 -0
  112. package/dist/signal-ingestion/clustering.d.ts +200 -0
  113. package/dist/signal-ingestion/clustering.js +413 -0
  114. package/dist/signal-ingestion/config.d.ts +351 -0
  115. package/dist/signal-ingestion/config.js +587 -0
  116. package/dist/signal-ingestion/d1.d.ts +252 -0
  117. package/dist/signal-ingestion/d1.js +235 -0
  118. package/dist/signal-ingestion/errors.d.ts +73 -0
  119. package/dist/signal-ingestion/errors.js +108 -0
  120. package/dist/signal-ingestion/governance-events.d.ts +181 -0
  121. package/dist/signal-ingestion/governance-events.js +189 -0
  122. package/dist/signal-ingestion/index.d.ts +35 -0
  123. package/dist/signal-ingestion/index.js +53 -0
  124. package/dist/signal-ingestion/manual-share-metric.d.ts +93 -0
  125. package/dist/signal-ingestion/manual-share-metric.js +106 -0
  126. package/dist/signal-ingestion/registry.d.ts +40 -0
  127. package/dist/signal-ingestion/registry.js +137 -0
  128. package/dist/signal-ingestion/residency.d.ts +227 -0
  129. package/dist/signal-ingestion/residency.js +238 -0
  130. package/dist/signal-ingestion/significance.d.ts +554 -0
  131. package/dist/signal-ingestion/significance.js +555 -0
  132. package/dist/signal-ingestion/types.d.ts +191 -0
  133. package/dist/signal-ingestion/types.js +8 -0
  134. package/dist/substrate/drift-composition.d.ts +270 -0
  135. package/dist/substrate/drift-composition.js +306 -0
  136. package/dist/substrate/drift-tui-surface.d.ts +61 -0
  137. package/dist/substrate/drift-tui-surface.js +102 -0
  138. package/dist/substrate/identity-class.d.ts +176 -0
  139. package/dist/substrate/identity-class.js +201 -0
  140. package/dist/tessellation/cross-soul-provenance-rule.d.ts +133 -0
  141. package/dist/tessellation/cross-soul-provenance-rule.js +171 -0
  142. package/dist/tessellation/inter-soul-embedding-distance-rule.d.ts +61 -0
  143. package/dist/tessellation/inter-soul-embedding-distance-rule.js +67 -0
  144. package/dist/tessellation/rule-registry.d.ts +269 -0
  145. package/dist/tessellation/rule-registry.js +92 -0
  146. package/dist/tessellation/soul-slug-ast-scan-rule.d.ts +90 -0
  147. package/dist/tessellation/soul-slug-ast-scan-rule.js +158 -0
  148. package/dist/tessellation-admission.d.ts +162 -0
  149. package/dist/tessellation-admission.js +146 -0
  150. package/dist/tessellation-drift.d.ts +246 -0
  151. package/dist/tessellation-drift.js +250 -0
  152. package/dist/validate-config.js +13 -0
  153. package/dist/validate-issue.js +2 -2
  154. package/dist/variant/cardinality-activation.d.ts +126 -0
  155. package/dist/variant/cardinality-activation.js +101 -0
  156. package/dist/variant/deprecation-lifecycle.d.ts +184 -0
  157. package/dist/variant/deprecation-lifecycle.js +208 -0
  158. package/dist/variant/drift-extension.d.ts +136 -0
  159. package/dist/variant/drift-extension.js +164 -0
  160. package/dist/variant/engineering-review.d.ts +185 -0
  161. package/dist/variant/engineering-review.js +142 -0
  162. package/dist/variant/index.d.ts +32 -0
  163. package/dist/variant/index.js +32 -0
  164. package/dist/variant/inheritance-validator.d.ts +165 -0
  165. package/dist/variant/inheritance-validator.js +139 -0
  166. package/dist/variant/internal-adopter/index.d.ts +11 -0
  167. package/dist/variant/internal-adopter/index.js +10 -0
  168. package/dist/variant/internal-adopter/products.d.ts +156 -0
  169. package/dist/variant/internal-adopter/products.js +366 -0
  170. package/dist/variant-admission.d.ts +316 -0
  171. package/dist/variant-admission.js +247 -0
  172. package/package.json +11 -8
@@ -0,0 +1,250 @@
1
+ /**
2
+ * RFC-0009 Phase 4.2 — Eτ_tessellation_drift detector (orchestrator-side).
3
+ *
4
+ * Detects design-coherence drift across tessellated souls per RFC-0009 §7.2
5
+ * (OQ-6 resolution: detection is orchestrator-side, not adapter-side).
6
+ *
7
+ * Three detection rules are specified in §7.2:
8
+ *
9
+ * Rule #1 — AST scan for soul-name string literals in shared substrate
10
+ * SHIPS HERE. Static scan over the substrate file set looking for
11
+ * soul-slug string literals and `if (soul === '<slug>')` patterns
12
+ * in modules that are NOT scoped to a single soul.
13
+ *
14
+ * Rule #2 — Embedding distance between Soul DIDs over time
15
+ * EXPLICITLY DEFERRED to RFC-0019 (Embedding Provider Adapter).
16
+ * Not implemented here; would land in a separate detector once
17
+ * `embedDocument(text)` is callable from the orchestrator.
18
+ *
19
+ * Rule #3 — Cross-soul provenance audits
20
+ * SHIPS HERE. Walks the supplied provenance records (RFC-0009 §8.3
21
+ * ProvenanceRecord with `targetedSouls`, `substrateScoped`,
22
+ * `tessellatedSoulRef`, all landed by AISDLC-315) and flags work
23
+ * whose `targetedSouls` set crosses tessellation boundaries
24
+ * without an amendment record; flags substrate work whose
25
+ * downstream provenance shows soul-distinct outcomes diverge.
26
+ *
27
+ * Both shipped rules are gated on adopter opt-in via `enabled` (default
28
+ * `false`) per RFC-0009 §10 Phase 4 promotion convention ("All sub-dimension
29
+ * activations are gated on adopter opt-in initially").
30
+ *
31
+ * Detected drift events emit to `events.jsonl` (the RFC-0015 substrate) via
32
+ * the supplied `emit` callback. Callers wire this to
33
+ * `orchestrator/src/artifacts.appendEvent(artifactsDir, event)`.
34
+ *
35
+ * @see spec/rfcs/RFC-0009-tessellated-design-intent-documents.md §7.2 + §10
36
+ */
37
+ export const DEFAULT_DIVERGENCE_THRESHOLD = 0.3;
38
+ // ── Internal helpers ───────────────────────────────────────────────────
39
+ /**
40
+ * Validate a soul slug. Mirrors `TessellationSoul.soulId` JSON-schema
41
+ * constraint: lowercase alphanumeric + dashes, 1-64 chars.
42
+ */
43
+ function isValidSlug(slug) {
44
+ return /^[a-z0-9-]+$/.test(slug) && slug.length >= 1 && slug.length <= 64;
45
+ }
46
+ /**
47
+ * Escape a string for safe use inside a RegExp character class / pattern.
48
+ * The soul slug grammar already excludes regex metacharacters, but this
49
+ * stays defensive in case future schema changes admit more chars.
50
+ */
51
+ function escapeRegex(s) {
52
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
53
+ }
54
+ /**
55
+ * Scan one substrate file for soul-leakage patterns.
56
+ *
57
+ * Detection strategy is deliberately textual rather than full-parser-AST.
58
+ * RFC-0009 §7.2 calls this "AST scan" but a regex pass over source files
59
+ * is the framework primitive that ships today and matches the rule's
60
+ * goal — surface soul-slug leakage in substrate code for operator review.
61
+ * False positives (e.g., the slug appearing inside a code comment) are
62
+ * accepted as part of the surface; operators triage and silence per-file
63
+ * via a tessellation amendment if needed.
64
+ *
65
+ * Two patterns are scanned, both line-by-line so we report 1-based line
66
+ * numbers in findings:
67
+ *
68
+ * 1. `'<slug>'` or `"<slug>"` — bare string-literal match.
69
+ * 2. `if (soul === '<slug>')` / `=== "<slug>"` — soul-discriminating branch.
70
+ * Soul identifier is matched permissively (`soul`, `soulId`, `soul_id`).
71
+ */
72
+ function scanSubstrateFile(file, soulSlugs) {
73
+ const findings = [];
74
+ if (file.contents.length === 0)
75
+ return findings;
76
+ // Pre-compile patterns per soul slug so we walk the file once per slug.
77
+ // Files are typically small (<5KB per substrate file in practice) so
78
+ // the O(N×S) walk is fine without further optimization.
79
+ const lines = file.contents.split('\n');
80
+ for (const slug of soulSlugs) {
81
+ if (!isValidSlug(slug))
82
+ continue;
83
+ const esc = escapeRegex(slug);
84
+ // Pattern 1: bare string literal (single or double quote, exact match).
85
+ const literalRe = new RegExp(`(['"])${esc}\\1`);
86
+ // Pattern 2: soul-discriminating conditional. Permissive on the soul
87
+ // identifier name so it catches `soul`, `soulId`, `soul_id`, etc.
88
+ const condRe = new RegExp(`(?:soul[A-Za-z_]*)\\s*===\\s*(['"])${esc}\\1`);
89
+ for (let i = 0; i < lines.length; i++) {
90
+ const line = lines[i];
91
+ const condMatch = line.match(condRe);
92
+ if (condMatch) {
93
+ findings.push({
94
+ filePath: file.path,
95
+ soulSlug: slug,
96
+ line: i + 1,
97
+ pattern: 'soul-conditional',
98
+ excerpt: line.trim().slice(0, 200),
99
+ });
100
+ // Don't double-report the same line as a bare literal.
101
+ continue;
102
+ }
103
+ if (literalRe.test(line)) {
104
+ findings.push({
105
+ filePath: file.path,
106
+ soulSlug: slug,
107
+ line: i + 1,
108
+ pattern: 'string-literal',
109
+ excerpt: line.trim().slice(0, 200),
110
+ });
111
+ }
112
+ }
113
+ }
114
+ return findings;
115
+ }
116
+ /**
117
+ * Derive a short stable work-item reference from a provenance record's
118
+ * `promptHash` (16 hex chars is plenty for human-grep utility while still
119
+ * being unique enough across the audit window). Falls back to the
120
+ * timestamp when promptHash is empty.
121
+ */
122
+ function deriveWorkItemRef(record) {
123
+ if (record.promptHash && record.promptHash.length > 0) {
124
+ return record.promptHash.slice(0, 16);
125
+ }
126
+ return record.timestamp;
127
+ }
128
+ /** Compute the max-min spread of an outcome map. Returns 0 for empty / 1-soul maps. */
129
+ function outcomeSpread(outcomeBySoul) {
130
+ const values = Object.values(outcomeBySoul).filter((v) => typeof v === 'number');
131
+ if (values.length < 2)
132
+ return 0;
133
+ return Math.max(...values) - Math.min(...values);
134
+ }
135
+ // ── Public API ─────────────────────────────────────────────────────────
136
+ /**
137
+ * Run Eτ_tessellation_drift detection against the supplied inputs.
138
+ *
139
+ * The detector is **read-only** — it does not mutate the tessellation,
140
+ * the provenance records, or the substrate files. It computes events and
141
+ * forwards each via the optional `emit` callback (callers wire this to
142
+ * `appendEvent(artifactsDir, ev)`).
143
+ *
144
+ * When `config.enabled !== true` the detector short-circuits immediately
145
+ * and returns `{ events: [], optedOut: true }`. This honors the RFC-0009
146
+ * §10 Phase 4 "adopter opt-in initially" convention; the detector exists
147
+ * in the surface but produces zero side effects until explicitly enabled.
148
+ *
149
+ * @param input Detection inputs (tessellation + optional substrate + provenance).
150
+ * @param config Detector configuration (master `enabled` flag + per-rule toggles).
151
+ * @param emit Optional event sink; called once per detected event. Errors
152
+ * from `emit` are propagated to the caller (intentional — the
153
+ * detector is sync-pure with respect to its inputs; the caller
154
+ * decides how to handle event-stream failures).
155
+ */
156
+ export async function detectTessellationDrift(input, config = {}, emit) {
157
+ // ── Master opt-out short-circuit ────────────────────────────────
158
+ if (config.enabled !== true) {
159
+ return { events: [], optedOut: true };
160
+ }
161
+ const events = [];
162
+ const now = new Date().toISOString();
163
+ const soulSlugs = input.tessellation.souls.map((s) => s.soulId).filter(isValidSlug);
164
+ const soulSlugSet = new Set(soulSlugs);
165
+ const rules = config.rules ?? {};
166
+ const runAstScan = rules.astScan !== false;
167
+ const runCrossSoulProvenance = rules.crossSoulProvenance !== false;
168
+ const divergenceThreshold = config.divergenceThreshold ?? DEFAULT_DIVERGENCE_THRESHOLD;
169
+ // ── Rule #1: AST scan for soul-name string literals in substrate ─
170
+ if (runAstScan && input.substrateFiles && input.substrateFiles.length > 0) {
171
+ const allFindings = [];
172
+ for (const file of input.substrateFiles) {
173
+ const findings = scanSubstrateFile(file, soulSlugs);
174
+ if (findings.length > 0)
175
+ allFindings.push(...findings);
176
+ }
177
+ if (allFindings.length > 0) {
178
+ const involved = new Set(allFindings.map((f) => f.soulSlug));
179
+ const ev = {
180
+ type: 'TessellationDriftDetected',
181
+ rule: 'ast-scan',
182
+ timestamp: now,
183
+ tessellatedDid: input.tessellatedDid,
184
+ involvedSouls: [...involved].sort(),
185
+ severity: 'warning',
186
+ message: `AST scan: ${allFindings.length} soul-name leakage hit(s) across ${involved.size} soul(s) in shared substrate`,
187
+ details: { rule: 'ast-scan', findings: allFindings },
188
+ };
189
+ events.push(ev);
190
+ }
191
+ }
192
+ // ── Rule #3: Cross-soul provenance audits ────────────────────────
193
+ if (runCrossSoulProvenance && input.provenance && input.provenance.length > 0) {
194
+ const findings = [];
195
+ for (const entry of input.provenance) {
196
+ const { record, amendmentRecorded, outcomeBySoul } = entry;
197
+ // Filter targetedSouls down to souls actually present in the
198
+ // tessellation — out-of-tessellation slugs are stale and not
199
+ // actionable as drift.
200
+ const valid = (record.targetedSouls ?? []).filter((s) => soulSlugSet.has(s));
201
+ // Detection A: provenance crosses tessellation boundary without amendment.
202
+ if (valid.length >= 2 && amendmentRecorded !== true) {
203
+ findings.push({
204
+ kind: 'cross-boundary-no-amendment',
205
+ workItemRef: deriveWorkItemRef(record),
206
+ crossedSouls: [...valid].sort(),
207
+ note: `work item targeted ${valid.length} souls without a recorded cross-tessellation amendment`,
208
+ });
209
+ }
210
+ // Detection B: substrate-scoped provenance with divergent
211
+ // soul-distinct outcomes.
212
+ if (record.substrateScoped === true &&
213
+ outcomeBySoul &&
214
+ Object.keys(outcomeBySoul).length >= 2) {
215
+ const spread = outcomeSpread(outcomeBySoul);
216
+ if (spread >= divergenceThreshold) {
217
+ findings.push({
218
+ kind: 'substrate-divergent-outcomes',
219
+ workItemRef: deriveWorkItemRef(record),
220
+ crossedSouls: Object.keys(outcomeBySoul).sort(),
221
+ outcomeBySoul: { ...outcomeBySoul },
222
+ note: `substrate provenance shows soul-distinct outcome spread ${spread.toFixed(3)} ≥ threshold ${divergenceThreshold}`,
223
+ });
224
+ }
225
+ }
226
+ }
227
+ if (findings.length > 0) {
228
+ const involved = new Set(findings.flatMap((f) => f.crossedSouls));
229
+ const ev = {
230
+ type: 'TessellationDriftDetected',
231
+ rule: 'cross-soul-provenance',
232
+ timestamp: now,
233
+ tessellatedDid: input.tessellatedDid,
234
+ involvedSouls: [...involved].sort(),
235
+ severity: 'warning',
236
+ message: `Cross-soul provenance: ${findings.length} finding(s) across ${involved.size} soul(s)`,
237
+ details: { rule: 'cross-soul-provenance', findings },
238
+ };
239
+ events.push(ev);
240
+ }
241
+ }
242
+ // ── Emit ────────────────────────────────────────────────────────
243
+ if (emit) {
244
+ for (const ev of events) {
245
+ await emit(ev);
246
+ }
247
+ }
248
+ return { events, optedOut: false };
249
+ }
250
+ //# sourceMappingURL=tessellation-drift.js.map
@@ -41,10 +41,23 @@ export function validateConfigFiles(configDir, fileFilter) {
41
41
  try {
42
42
  const raw = readFileSync(resolve(dir, file), 'utf-8');
43
43
  const doc = parseYaml(raw);
44
+ // Skip non-resource YAML files (placeholder/commented-out YAMLs, review-exemplars, etc.)
45
+ // AI-SDLC resources always have apiVersion + kind fields.
46
+ // Mirrors the identical guard in config.ts:116 — keep both in sync.
47
+ if (!doc || typeof doc !== 'object' || !('apiVersion' in doc) || !('kind' in doc)) {
48
+ continue;
49
+ }
44
50
  const result = validateResource(doc);
45
51
  const kind = typeof doc === 'object' && doc !== null && 'kind' in doc
46
52
  ? doc.kind
47
53
  : null;
54
+ if (result.skipped) {
55
+ // Loader-private or adopter-extension kind — not in the AI-SDLC
56
+ // schema registry. Skip silently so adopter pipelines that use
57
+ // kinds like `MaintainersList` or `SoulTrackMap` don't get
58
+ // false-positive warnings. See docs/operations/schema-extensions.md.
59
+ continue;
60
+ }
48
61
  if (result.valid) {
49
62
  results.push({ file, kind, valid: true, errors: [] });
50
63
  }
@@ -16,7 +16,7 @@ function buildEvaluationContext(issue) {
16
16
  ? 1
17
17
  : 0
18
18
  : 0;
19
- const complexityMatch = issue.description?.match(/### Complexity\s*\n+\s*(\d+)/i);
19
+ const complexityMatch = issue.description?.match(/### Complexity[ \t]*\n+[ \t]*(\d+)/i);
20
20
  const complexity = complexityMatch ? Number(complexityMatch[1]) : 0;
21
21
  return {
22
22
  authorType: 'ai-agent',
@@ -96,7 +96,7 @@ export async function validateIssueWithExtensions(issue, qualityGate, options) {
96
96
  export function parseComplexity(issueBody) {
97
97
  if (!issueBody)
98
98
  return 0;
99
- const match = issueBody.match(/### Complexity\s*\n+\s*(\d+)/i);
99
+ const match = issueBody.match(/### Complexity[ \t]*\n+[ \t]*(\d+)/i);
100
100
  return match ? Number(match[1]) : 0;
101
101
  }
102
102
  //# sourceMappingURL=validate-issue.js.map
@@ -0,0 +1,126 @@
1
+ /**
2
+ * RFC-0017 Phase 5 — OQ-8 cardinality activation Decision wiring (AISDLC-438).
3
+ *
4
+ * Per OQ-8 resolution (2026-05-18):
5
+ *
6
+ * "Future Decision in catalog; auto-promote on ≥2 adopter requests. Author's
7
+ * threshold expressed through the Decision Catalog substrate. Each adopter
8
+ * request → `Decision: variant-cardinality-activation-request` → Stage A
9
+ * counter; at threshold, Decision auto-promotes to operator batch review with
10
+ * 'file follow-on RFC' recommendation."
11
+ *
12
+ * This module implements:
13
+ *
14
+ * 1. **Stage A counter** — tracks distinct adopter cardinality activation
15
+ * requests. Each request emits `variant-cardinality-activation-request` to
16
+ * the Decision Catalog substrate. Requests from the same adopter are
17
+ * deduplicated (one signal per distinct adopter, per OQ-8 intent).
18
+ *
19
+ * 2. **Auto-promote at threshold** — when `distinctAdopterCount >= threshold`
20
+ * (default 2), the result is marked `promotedToOperatorReview: true` with
21
+ * a recommendation to file a follow-on RFC for cardinality activation.
22
+ * This mirrors the RFC-0036 OQ-6 first-party-adapter graduation pattern.
23
+ *
24
+ * 3. **Operator runbook pointer** — the recommendation string references the
25
+ * canonical runbook path for the "file follow-on RFC" path.
26
+ *
27
+ * All events route through RFC-0035 G0 (non-blocking pipeline contract).
28
+ * The cardinality `field` itself (`primary | secondary | experimental`) is
29
+ * RESERVED in the schema and ignored at runtime in v1.
30
+ *
31
+ * @see spec/rfcs/RFC-0017-in-soul-variant-pattern.md OQ-8 + §5.2
32
+ * @see spec/rfcs/RFC-0035-decision-catalog-operator-routing.md G0 §6.2
33
+ * @see docs/operations/variant-pattern-promotion.md — cardinality activation section
34
+ */
35
+ /**
36
+ * Minimum number of distinct adopter requests before the cardinality
37
+ * activation Decision auto-promotes to operator batch review (OQ-8 threshold).
38
+ *
39
+ * Mirrors RFC-0036 OQ-6 first-party-adapter graduation threshold pattern.
40
+ * Two distinct adopters requesting the same capability is sufficient signal
41
+ * to bring the topic to operator attention for follow-on RFC consideration.
42
+ */
43
+ export declare const DEFAULT_CARDINALITY_ACTIVATION_THRESHOLD = 2;
44
+ /**
45
+ * A single adopter's request to activate the `cardinality` field
46
+ * (`primary | secondary | experimental`) on Soul Variants.
47
+ *
48
+ * Each request is a signal from an InternalAdopter or external operator that
49
+ * their use case requires lifecycle distinctions beyond the flat v1 schema.
50
+ */
51
+ export interface CardinalityActivationRequest {
52
+ /** Adopter identifier (e.g. org slug, adopter name). Used for deduplication. */
53
+ requestedBy: string;
54
+ /** Soul the request pertains to (for context; not used in deduplication). */
55
+ soulId: string;
56
+ /** Variant the request pertains to (for context). */
57
+ variantId: string;
58
+ /**
59
+ * Free-text rationale for why cardinality activation would help this adopter.
60
+ * This text surfaces in the operator batch review when promoted.
61
+ */
62
+ rationale: string;
63
+ /** ISO 8601 timestamp of when the request was submitted. */
64
+ requestedAt: string;
65
+ }
66
+ /**
67
+ * Result of processing a batch of cardinality activation requests.
68
+ * Returned by `trackCardinalityActivationRequest()`.
69
+ */
70
+ export interface CardinalityActivationResult {
71
+ /** Decision kind emitted to the catalog per OQ-8 resolution. */
72
+ decisionKind: 'variant-cardinality-activation-request';
73
+ /** Total number of raw requests processed (may include duplicates). */
74
+ totalRequests: number;
75
+ /** Number of distinct adopters (deduplicated by `requestedBy`). */
76
+ distinctAdopterCount: number;
77
+ /** The distinct adopter IDs contributing to this batch. */
78
+ distinctAdopters: string[];
79
+ /**
80
+ * Whether the threshold has been crossed and the Decision has been
81
+ * auto-promoted to operator batch review.
82
+ */
83
+ promotedToOperatorReview: boolean;
84
+ /**
85
+ * Human-readable recommendation surfaced in the operator batch review
86
+ * when `promotedToOperatorReview === true`. Undefined when below threshold.
87
+ */
88
+ recommendation?: string;
89
+ /**
90
+ * The activation threshold that was applied (default 2, per-org configurable).
91
+ */
92
+ threshold: number;
93
+ /**
94
+ * RFC-0035 G0: routing is non-blocking — the pipeline continues regardless
95
+ * of how many requests have been received. The operator batch review is
96
+ * informational, not a gate.
97
+ */
98
+ routing: {
99
+ blocking: false;
100
+ routingKind: 'decision-catalog-auto-promote';
101
+ };
102
+ }
103
+ /**
104
+ * Track a batch of cardinality activation requests and emit Stage A counter
105
+ * state per OQ-8 resolution.
106
+ *
107
+ * Deduplicates by `requestedBy` — multiple requests from the same adopter
108
+ * count as one distinct signal (preventing a single eager adopter from
109
+ * triggering the promotion threshold alone).
110
+ *
111
+ * When `distinctAdopterCount >= threshold`, marks `promotedToOperatorReview: true`
112
+ * and populates `recommendation` with the "file follow-on RFC" runbook pointer.
113
+ *
114
+ * @param requests - One or more adopter cardinality activation requests.
115
+ * @param threshold - Override for the activation threshold (default: 2).
116
+ */
117
+ export declare function trackCardinalityActivationRequest(requests: CardinalityActivationRequest[], threshold?: number): CardinalityActivationResult;
118
+ /**
119
+ * Convenience predicate: returns true when the cardinality activation result
120
+ * should be surfaced to the operator in the next batch review cycle.
121
+ *
122
+ * This is a thin wrapper over `result.promotedToOperatorReview` for
123
+ * callers that need a typed boolean predicate.
124
+ */
125
+ export declare function shouldPromoteToOperatorReview(result: CardinalityActivationResult): boolean;
126
+ //# sourceMappingURL=cardinality-activation.d.ts.map
@@ -0,0 +1,101 @@
1
+ /**
2
+ * RFC-0017 Phase 5 — OQ-8 cardinality activation Decision wiring (AISDLC-438).
3
+ *
4
+ * Per OQ-8 resolution (2026-05-18):
5
+ *
6
+ * "Future Decision in catalog; auto-promote on ≥2 adopter requests. Author's
7
+ * threshold expressed through the Decision Catalog substrate. Each adopter
8
+ * request → `Decision: variant-cardinality-activation-request` → Stage A
9
+ * counter; at threshold, Decision auto-promotes to operator batch review with
10
+ * 'file follow-on RFC' recommendation."
11
+ *
12
+ * This module implements:
13
+ *
14
+ * 1. **Stage A counter** — tracks distinct adopter cardinality activation
15
+ * requests. Each request emits `variant-cardinality-activation-request` to
16
+ * the Decision Catalog substrate. Requests from the same adopter are
17
+ * deduplicated (one signal per distinct adopter, per OQ-8 intent).
18
+ *
19
+ * 2. **Auto-promote at threshold** — when `distinctAdopterCount >= threshold`
20
+ * (default 2), the result is marked `promotedToOperatorReview: true` with
21
+ * a recommendation to file a follow-on RFC for cardinality activation.
22
+ * This mirrors the RFC-0036 OQ-6 first-party-adapter graduation pattern.
23
+ *
24
+ * 3. **Operator runbook pointer** — the recommendation string references the
25
+ * canonical runbook path for the "file follow-on RFC" path.
26
+ *
27
+ * All events route through RFC-0035 G0 (non-blocking pipeline contract).
28
+ * The cardinality `field` itself (`primary | secondary | experimental`) is
29
+ * RESERVED in the schema and ignored at runtime in v1.
30
+ *
31
+ * @see spec/rfcs/RFC-0017-in-soul-variant-pattern.md OQ-8 + §5.2
32
+ * @see spec/rfcs/RFC-0035-decision-catalog-operator-routing.md G0 §6.2
33
+ * @see docs/operations/variant-pattern-promotion.md — cardinality activation section
34
+ */
35
+ // ── Constants ─────────────────────────────────────────────────────────────────
36
+ /**
37
+ * Minimum number of distinct adopter requests before the cardinality
38
+ * activation Decision auto-promotes to operator batch review (OQ-8 threshold).
39
+ *
40
+ * Mirrors RFC-0036 OQ-6 first-party-adapter graduation threshold pattern.
41
+ * Two distinct adopters requesting the same capability is sufficient signal
42
+ * to bring the topic to operator attention for follow-on RFC consideration.
43
+ */
44
+ export const DEFAULT_CARDINALITY_ACTIVATION_THRESHOLD = 2;
45
+ // ── Main function ─────────────────────────────────────────────────────────────
46
+ /**
47
+ * Track a batch of cardinality activation requests and emit Stage A counter
48
+ * state per OQ-8 resolution.
49
+ *
50
+ * Deduplicates by `requestedBy` — multiple requests from the same adopter
51
+ * count as one distinct signal (preventing a single eager adopter from
52
+ * triggering the promotion threshold alone).
53
+ *
54
+ * When `distinctAdopterCount >= threshold`, marks `promotedToOperatorReview: true`
55
+ * and populates `recommendation` with the "file follow-on RFC" runbook pointer.
56
+ *
57
+ * @param requests - One or more adopter cardinality activation requests.
58
+ * @param threshold - Override for the activation threshold (default: 2).
59
+ */
60
+ export function trackCardinalityActivationRequest(requests, threshold = DEFAULT_CARDINALITY_ACTIVATION_THRESHOLD) {
61
+ const seenAdopters = new Set();
62
+ for (const req of requests) {
63
+ seenAdopters.add(req.requestedBy);
64
+ }
65
+ const distinctAdopterCount = seenAdopters.size;
66
+ const distinctAdopters = [...seenAdopters].sort();
67
+ const promotedToOperatorReview = distinctAdopterCount >= threshold;
68
+ const recommendation = promotedToOperatorReview
69
+ ? [
70
+ `Cardinality activation threshold reached (${distinctAdopterCount}/${threshold} distinct adopters).`,
71
+ `Operator action: file a follow-on RFC to activate 'cardinality: primary | secondary | experimental'`,
72
+ `on Soul Variants. See docs/operations/variant-pattern-promotion.md §Cardinality Activation`,
73
+ `for the "file follow-on RFC" runbook path.`,
74
+ `Requesting adopters: ${distinctAdopters.join(', ')}.`,
75
+ ].join(' ')
76
+ : undefined;
77
+ return {
78
+ decisionKind: 'variant-cardinality-activation-request',
79
+ totalRequests: requests.length,
80
+ distinctAdopterCount,
81
+ distinctAdopters,
82
+ promotedToOperatorReview,
83
+ recommendation,
84
+ threshold,
85
+ routing: {
86
+ blocking: false,
87
+ routingKind: 'decision-catalog-auto-promote',
88
+ },
89
+ };
90
+ }
91
+ /**
92
+ * Convenience predicate: returns true when the cardinality activation result
93
+ * should be surfaced to the operator in the next batch review cycle.
94
+ *
95
+ * This is a thin wrapper over `result.promotedToOperatorReview` for
96
+ * callers that need a typed boolean predicate.
97
+ */
98
+ export function shouldPromoteToOperatorReview(result) {
99
+ return result.promotedToOperatorReview;
100
+ }
101
+ //# sourceMappingURL=cardinality-activation.js.map